diff --git a/src/core/FormResult.ts b/src/core/FormResult.ts index 2c22b9ce..b34d7e25 100644 --- a/src/core/FormResult.ts +++ b/src/core/FormResult.ts @@ -87,7 +87,7 @@ export default class FormResult { return value; } getValue(key: string): FormValue { - return FormValue.from(this.data[key]); + return FormValue.from(this.data[key], key); } // alias getV = this.getValue; diff --git a/src/core/FormValue.ts b/src/core/FormValue.ts index 077a9f87..07d183a2 100644 --- a/src/core/FormValue.ts +++ b/src/core/FormValue.ts @@ -1,3 +1,6 @@ +import { E, O, ensureError, pipe } from "@std"; +import { notifyError } from "src/utils/Log"; + function _toBulletList(value: Record | unknown[]) { if (Array.isArray(value)) { return value.map((v) => `- ${v}`).join("\n"); @@ -15,10 +18,13 @@ function isRecord(value: unknown): value is Record { * It has some convenience methods to render the value in a way * that works well with templates. */ -export class FormValue { - constructor(protected value: unknown) {} - static from(value: unknown) { - return new FormValue(value); +export class FormValue { + constructor( + protected value: T, + protected name: string, + ) {} + static from(value: U, name: string) { + return new FormValue(value, name); } /** * Returns the value as a string. @@ -34,7 +40,6 @@ export class FormValue { case "string": return this.value; case "number": - return this.value.toString(); case "boolean": return this.value.toString(); case "object": @@ -76,7 +81,60 @@ export class FormValue { } } /** - * Alias for `toBulletList` + * Converts the value to a dataview property using the field name as the key. + * If the value is empty or undefined, it will return an empty string and not render anything. */ + toDataview() { + const value = this.value; + if (value === undefined) return ""; + if (Array.isArray(value)) { + return `[${this.name}:: ${JSON.stringify(value).slice(1, -1)}]`; + } + return `[${this.name}:: ${this.toString()}]`; + } + /** + * Transforms the containerd value using the provided function. + * If the value is undefined or null the function will not be called + * and the result will be the same as the original. + * This is useful if you want to apply somme modifications to the value + * before rendering it, for example if none of the existing format methods suit your needs. + * @param {function} fn the function to transform the values + * @returns a new FormValue with the transformed value + **/ + map(fn: (value: unknown) => U): FormValue { + const safeFn = E.tryCatchK(fn, ensureError); + const unchanged = () => this as FormValue; + return pipe( + this.value, + O.fromNullable, + O.map(safeFn), + O.fold(unchanged, (v) => + pipe( + v, + E.fold( + (e) => { + notifyError("Error in map of " + this.name)(e.message); + return unchanged(); + }, + + (v: U) => FormValue.from(v, this.name), + ), + ), + ), + ); + } + /** Alias for `toDataview` */ + toDv = this.toDataview; + /** Alias for `toBulletList` */ toBullets = this.toBulletList; + /** + * Convenient getter to get the value as bullets, so you don't need to call `toBulletList` manually. + * example: + * ```ts + * result.getValue("myField").bullets; + * ``` + */ + get bullets() { + return this.toBulletList(); + } } diff --git a/src/std/index.ts b/src/std/index.ts index 62f4508c..fed8f231 100644 --- a/src/std/index.ts +++ b/src/std/index.ts @@ -16,6 +16,7 @@ import { none, fromNullable as fromNullableOpt, fold as ofold, + chain as ochain, } from "fp-ts/Option"; import { isLeft, @@ -87,6 +88,7 @@ export const O = { none, fold: ofold, fromNullable: fromNullableOpt, + chain: ochain, }; export const parse = tryCatchK(parseV, (e: unknown) => e as ValiError); @@ -157,7 +159,7 @@ export function tap(msg: string) { }; } -function ensureError(e: unknown): Error { +export function ensureError(e: unknown): Error { return e instanceof Error ? e : new Error(String(e)); } diff --git a/src/utils/Log.ts b/src/utils/Log.ts index 180e20f2..ca738828 100644 --- a/src/utils/Log.ts +++ b/src/utils/Log.ts @@ -1,26 +1,37 @@ import { Notice } from "obsidian"; import { ModalFormError } from "./ModalFormError"; - -export function log_notice(title: string, msg: string | DocumentFragment, titleClass?: string, bodyClass?: string): void { +export function log_notice( + title: string, + msg: string | DocumentFragment, + titleClass?: string, + bodyClass?: string, +): void { const notice = new Notice("", 15000); const el = notice.noticeEl; el.empty(); - const head = el.createEl('h6', { text: title, cls: titleClass }) - head.setCssStyles({ marginTop: '0px' }) - const body = el.createEl('div', { text: msg, cls: bodyClass }) + const head = el.createEl("h6", { text: title, cls: titleClass }); + head.setCssStyles({ marginTop: "0px" }); + const body = el.createEl("div", { text: msg, cls: bodyClass }); el.append(head, body); } export function log_update(msg: string): void { - log_notice('Modal form update', msg) + log_notice("Modal form update", msg); } export function log_error(e: Error | ModalFormError): void { if (e instanceof ModalFormError && e.console_msg) { - log_notice('Modal from error: ', e.message + "\n" + e.console_msg, 'var(--text-error)') + log_notice("Modal from error: ", e.message + "\n" + e.console_msg, "var(--text-error)"); console.error(`Modal form Error:`, e.message, "\n", e.console_msg); } else { - log_notice('Modal from error', e.message) + log_notice("Modal from error", e.message); } } + +/** + * Convenience function to create a function that logs a notice with a title. + * Use it to notify the user about important errors + * @param title + */ +export const notifyError = (title: string) => (msg: string) => log_notice(`🚨 ${title} 🚨`, msg, "notice-error");