diff --git a/src/API.ts b/src/API.ts index 406d19e2..95bceb7d 100644 --- a/src/API.ts +++ b/src/API.ts @@ -8,6 +8,7 @@ import ModalFormPlugin from "./main"; import { ModalFormError } from "./utils/ModalFormError"; import { FormModal } from "./FormModal"; import { log_error, log_notice } from "./utils/Log"; +import * as std from "@std"; type pickOption = { pick: string[] }; type omitOption = { omit: string[] }; @@ -21,6 +22,7 @@ function isOmitOption(opts: limitOptions): opts is omitOption { } export class API { + std = std; /** * Constructor for the API class * @param {App} app - The application instance @@ -29,17 +31,14 @@ export class API { constructor( private app: App, private plugin: ModalFormPlugin, - ) { } + ) {} /** * Opens a modal form with the provided form definition * @param {FormDefinition} formDefinition - The form definition to use * @returns {Promise} - A promise that resolves with the form result */ - openModalForm( - formDefinition: FormDefinition, - options?: FormOptions, - ): Promise { + openModalForm(formDefinition: FormDefinition, options?: FormOptions): Promise { return new Promise((resolve) => { new FormModal(this.app, formDefinition, resolve, options).open(); }); @@ -49,14 +48,12 @@ export class API { } private getFormByName(name: string): FormDefinition | undefined { - const form = this.plugin.settings?.formDefinitions.find( - (form) => form.name === name, - ); + const form = this.plugin.settings?.formDefinitions.find((form) => form.name === name); if (form instanceof MigrationError) { log_notice( "🚫 The form you tried to load has an invalid format", `The form "${name}" has an invalid format.` + - `We tried to automatically convert it but it failed, please fix it manually in the forms manager. + `We tried to automatically convert it but it failed, please fix it manually in the forms manager. `, ); return undefined; @@ -76,9 +73,7 @@ export class API { if (formDefinition) { return this.openModalForm(formDefinition, options); } else { - const error = new ModalFormError( - `Form definition ${name} not found`, - ); + const error = new ModalFormError(`Form definition ${name} not found`); log_error(error); return Promise.reject(error); } @@ -92,16 +87,12 @@ export class API { const omit = opts.omit; newFormDefinition = { ...formDefinition, - fields: formDefinition.fields.filter( - (field) => !omit.includes(field.name), - ), + fields: formDefinition.fields.filter((field) => !omit.includes(field.name)), }; } else if (isPickOption(opts)) { newFormDefinition = { ...formDefinition, - fields: formDefinition.fields.filter((field) => - opts.pick.includes(field.name), - ), + fields: formDefinition.fields.filter((field) => opts.pick.includes(field.name)), }; } else { throw new ModalFormError( @@ -111,9 +102,7 @@ export class API { } return this.openModalForm(newFormDefinition); } else { - const error = new ModalFormError( - `Form definition ${name} not found`, - ); + const error = new ModalFormError(`Form definition ${name} not found`); log_error(error); return Promise.reject(error); } diff --git a/src/FormModal.ts b/src/FormModal.ts index 09952c54..d19bb193 100644 --- a/src/FormModal.ts +++ b/src/FormModal.ts @@ -60,7 +60,7 @@ export class FormModal extends Modal { const name = definition.label || definition.name; const required = definition.isRequired ?? false; const fieldBase = new Setting(contentEl) - .setName(`${name} ${required ? "*" : ""}`) + .setName(`${name} ${required ? "*" : ""}`.trim()) .setDesc(definition.description); // This intermediary constants are necessary so typescript can narrow down the proper types. // without them, you will have to use the whole access path (definition.input.folder), diff --git a/src/main.ts b/src/main.ts index 22a4375e..e9f38a4c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,14 +20,12 @@ import { getDefaultSettings, } from "src/core/settings"; import { log_error, log_notice } from "./utils/Log"; -import * as E from "fp-ts/Either"; -import { pipe } from "fp-ts/function"; -import * as A from "fp-ts/Array"; import { settingsStore } from "./store/store"; -import { O } from "@std"; +import { O, pipe, E, A } from "@std"; import { executeTemplate } from "./core/template/templateParser"; import { NewNoteModal } from "./suggesters/NewNoteModal"; import { file_exists } from "./utils/files"; +import { FormPickerModal } from "./suggesters/FormPickerModal"; type ViewType = typeof EDIT_FORM_VIEW | typeof MANAGE_FORMS_VIEW; @@ -61,7 +59,7 @@ function notifyMigrationErrors(errors: MigrationError[]) { // This is the plugin entrypoint export default class ModalFormPlugin extends Plugin { public settings: ModalFormSettings | undefined; - private unsubscribeSettingsStore: () => void = () => { }; + private unsubscribeSettingsStore: () => void = () => {}; // This things will be setup in the onload function rather than constructor public api!: PublicAPI; @@ -84,9 +82,7 @@ export default class ModalFormPlugin extends Plugin { // then if you save another form you will unexpectedly save the mutated form too. // Maybe we could instead do a deep copy instead, but until this proven to be a bottleneck I will leave it like this. const savedSettings = await this.getSettings(); - const formDefinition = savedSettings.formDefinitions.find( - (form) => form.name === formName, - ); + const formDefinition = savedSettings.formDefinitions.find((form) => form.name === formName); if (!formDefinition) { throw new ModalFormError(`Form ${formName} not found`); } @@ -107,14 +103,10 @@ export default class ModalFormPlugin extends Plugin { async activateView(viewType: ViewType, state?: FormDefinition) { const { workspace } = this.app; - let leaf: WorkspaceLeaf | undefined = - workspace.getLeavesOfType(viewType)[0]; + let leaf: WorkspaceLeaf | undefined = workspace.getLeavesOfType(viewType)[0]; if (leaf) { console.info("found leaf, no reason to create a new one"); - } else if ( - Platform.isMobile || - this.settings?.editorPosition === "mainView" - ) { + } else if (Platform.isMobile || this.settings?.editorPosition === "mainView") { leaf = this.app.workspace.getLeaf("tab"); } else if (this.settings?.editorPosition === "right") { leaf = this.app.workspace.getRightLeaf(false); @@ -142,11 +134,10 @@ export default class ModalFormPlugin extends Plugin { const [migrationIsNeeded, settings] = pipe( parseSettings(data), E.map((settings): [boolean, ModalFormSettings] => { - const migrationIsNeeded = - settings.formDefinitions.some(formNeedsMigration); - const { right: formDefinitions, left: errors } = A.partitionMap( - migrateToLatest, - )(settings.formDefinitions); + const migrationIsNeeded = settings.formDefinitions.some(formNeedsMigration); + const { right: formDefinitions, left: errors } = A.partitionMap(migrateToLatest)( + settings.formDefinitions, + ); notifyParsingErrors(errors); const validSettings: ModalFormSettings = { ...settings, @@ -175,8 +166,8 @@ export default class ModalFormPlugin extends Plugin { attachShortcutToGlobalWindow() { if (!this.settings) { - log_error(new ModalFormError("Settings not loaded yet")) - return + log_error(new ModalFormError("Settings not loaded yet")); + return; } const globalNamespace = this.settings.globalNamespace; if (this.settings?.attachShortcutToGlobalWindow) { @@ -190,6 +181,13 @@ export default class ModalFormPlugin extends Plugin { await this.saveSettings(); } + get validFormDefinitions(): FormDefinition[] { + return pipe( + this.settings!.formDefinitions, + A.filterMap((form) => (form instanceof MigrationError ? O.none : O.some(form))), + ); + } + async onload() { const settings = await this.getSettings(); if (settings.formDefinitions.length === 0) { @@ -203,14 +201,8 @@ export default class ModalFormPlugin extends Plugin { }); this.api = new API(this.app, this); this.attachShortcutToGlobalWindow(); - this.registerView( - EDIT_FORM_VIEW, - (leaf) => new EditFormView(leaf, this), - ); - this.registerView( - MANAGE_FORMS_VIEW, - (leaf) => new ManageFormsView(leaf, this), - ); + this.registerView(EDIT_FORM_VIEW, (leaf) => new EditFormView(leaf, this)); + this.registerView(MANAGE_FORMS_VIEW, (leaf) => new ManageFormsView(leaf, this)); // This creates an icon in the left ribbon. this.addRibbonIcon("documents", "Edit forms", (evt: MouseEvent) => { @@ -239,6 +231,16 @@ export default class ModalFormPlugin extends Plugin { }, }); + this.addCommand({ + id: "edit-form", + name: "Edit form", + callback: async () => { + new FormPickerModal(this.app, this.validFormDefinitions, (formToEdit) => { + this.activateView(EDIT_FORM_VIEW, formToEdit); + }).open(); + }, + }); + // This adds a settings tab so the user can configure various aspects of the plugin this.addSettingTab(new ModalFormSettingTab(this.app, this)); } @@ -250,13 +252,9 @@ export default class ModalFormPlugin extends Plugin { * @returns a unique name for the note, full path including the extension */ getUniqueNoteName(name: string, destinationFolder?: string): string { - const defaultNotesFolder = this.app.fileManager.getNewFileParent( - "", - "note.md", - ); + const defaultNotesFolder = this.app.fileManager.getNewFileParent("", "note.md"); function makePath(name: string, folder?: string, suffix?: number) { - return `${folder || defaultNotesFolder.path}/${name}${suffix ? "-" + suffix : "" - }.md`; + return `${folder || defaultNotesFolder.path}/${name}${suffix ? "-" + suffix : ""}.md`; } let destinationPath = makePath(name, destinationFolder); let i = 1; @@ -291,14 +289,8 @@ export default class ModalFormPlugin extends Plugin { destinationFolder: string, ) => { const formData = await this.api.openForm(form); - const newNoteFullPath = this.getUniqueNoteName( - noteName, - destinationFolder, - ); - const noteContent = executeTemplate( - form.template.parsedTemplate, - formData.getData(), - ); + const newNoteFullPath = this.getUniqueNoteName(noteName, destinationFolder); + const noteContent = executeTemplate(form.template.parsedTemplate, formData.getData()); console.log("new note content", noteContent); this.app.vault.create(newNoteFullPath, noteContent); };