From e852f991bb9860f42468b0669b33c2fe77f0efb2 Mon Sep 17 00:00:00 2001 From: Noah Boos Date: Tue, 8 Jul 2025 22:57:51 +0200 Subject: [PATCH] Refactoring - Several refactoring --- src/modals/AddNoteFromCodeBlockModal.ts | 172 ++++----------- src/modals/AddNoteFromMetadataModal.ts | 90 ++------ src/modals/AddNoteModal.ts | 191 ++-------------- src/modals/addNoteModalsUtils.ts | 279 ++++++++++++++++++++++++ src/modals/modalsUtils.ts | 171 --------------- src/utils.ts | 221 +------------------ 6 files changed, 363 insertions(+), 761 deletions(-) create mode 100644 src/modals/addNoteModalsUtils.ts delete mode 100644 src/modals/modalsUtils.ts diff --git a/src/modals/AddNoteFromCodeBlockModal.ts b/src/modals/AddNoteFromCodeBlockModal.ts index 3dff7a9..4904ae7 100644 --- a/src/modals/AddNoteFromCodeBlockModal.ts +++ b/src/modals/AddNoteFromCodeBlockModal.ts @@ -1,32 +1,26 @@ -import {App, ButtonComponent, DropdownComponent, Modal, TFile} from "obsidian"; +import { + App, + DropdownComponent, + Modal, + TFile +} from "obsidian"; import AnkiIntegration from "../main"; import { - AddButton, AddContainer, - AddDropdown, - AddFieldGroups, - AddInput, - AddOptionsToDropdownFromDataset, - AddParagraph, - AddSubtitle, AddTagInputGroup, - AddTitle, + AddSubtitle, + AddTitle +} from "../utils"; +import { + GenerateFieldGroups, + AddDeckSelector, + AddModelSelector, + AddSubmitButton, + AddTagsSection, AutoAssignDeck, AutoAssignModel, AutoGenerateFields, - BuildTagsArray, - CreateFieldsGroupData, - FetchModelByName, - ReadFileContent -} from "../utils"; -import {ProcessAddNote} from "../AnkiConnect"; -import { - GenerateFieldGroups, - GenerateDeckSelector, - GenerateModelSelector, - GenerateSubmitButton, - GenerateTagsSection -} from "./modalsUtils"; -import {Drop} from "esbuild"; + AddTagInputGroup +} from "./addNoteModalsUtils"; /** * A modal dialog for creating a new Anki note by using a code block content as pre-filled values. @@ -46,13 +40,6 @@ export class AddNoteFromCodeBlockModal extends Modal { */ plugin: AnkiIntegration; - /** - * Creates a new AddNoteFromCodeBlockModal instance. - * Initializes the modal with provided app and plugin. - * @param {App} app - The Obsidian app instance. - * @param {AnkiIntegration} plugin - The AnkiIntegration plugin instance. - * @constructor - */ constructor(app: App, plugin: AnkiIntegration) { super(app); this.plugin = plugin; @@ -65,55 +52,30 @@ export class AddNoteFromCodeBlockModal extends Modal { */ const ankiData: Object = this.plugin.settings.ankiData; - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; this.contentEl.focus(); AddTitle(contentEl, "Add a new note using code block"); AddSubtitle(contentEl, "Deck & Model"); - /** - * @type {HTMLDivElement} dropdownContainer - * @description Container for the deck and model dropdown selectors. - */ - const dropdownContainer: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__dropdownContainer--flex" - ]) - - const deckSelector: DropdownComponent = GenerateDeckSelector(dropdownContainer, ankiData); - - const modelSelector: DropdownComponent = GenerateModelSelector(dropdownContainer, ankiData); - - const tagsContainer: HTMLDivElement = GenerateTagsSection(contentEl); + const dropdownContainer: HTMLDivElement = AddContainer(contentEl, ["ankiIntegrationModal__dropdownContainer--flex"]) + const deckSelector: DropdownComponent = AddDeckSelector(dropdownContainer, ankiData); + const modelSelector: DropdownComponent = AddModelSelector(dropdownContainer, ankiData); + const tagsContainer: HTMLDivElement = AddTagsSection(contentEl); const tagsBody: HTMLDivElement = tagsContainer.querySelector('#tagsBody'); const tagsBodyParagraph: HTMLElement = tagsContainer.querySelector('#tagsBodyTip'); AddSubtitle(contentEl, "Fields"); + const inputFieldsContainer: HTMLDivElement = AddContainer(contentEl, ["ankiIntegrationModal__inputContainer--flex"]); - /** - * @type {HTMLDivElement} inputContainer - * @description Container for dynamically generated input fields based on the selected model. - */ - const inputContainer: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__inputContainer--flex" - ]); - - /** - * Event listener triggered when the model selector value changes. - * Updates the inputContainer with the fields corresponding to the selected model. - * @param {string} value - The selected model name. - */ modelSelector.onChange(async (value) => { const codeBlockParameters = await this.GetCodeBlockParameters(); - GenerateFieldGroups(this.plugin, inputContainer, value, codeBlockParameters); + GenerateFieldGroups(this.plugin, inputFieldsContainer, value, codeBlockParameters); }); - this.onOpenAsync(deckSelector, modelSelector, tagsBody, tagsBodyParagraph, inputContainer); + this.onOpenAsync(deckSelector, modelSelector, tagsBody, tagsBodyParagraph, inputFieldsContainer); - GenerateSubmitButton(contentEl, deckSelector, modelSelector, inputContainer, this); + AddSubmitButton(contentEl, deckSelector, modelSelector, inputFieldsContainer, this); } /** @@ -121,13 +83,7 @@ export class AddNoteFromCodeBlockModal extends Modal { * Removes all elements within the modal's content area. */ onClose() { - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; - - // Clear the content of the modal. contentEl.empty(); } @@ -139,18 +95,10 @@ export class AddNoteFromCodeBlockModal extends Modal { * @param {HTMLDivElement} inputContainer - Speaking for itself. */ async onOpenAsync(deckSelector: DropdownComponent, modelSelector: DropdownComponent, tagsBody: HTMLDivElement, tagsBodyParagraph: HTMLElement, inputContainer: HTMLDivElement): Promise { - /** - * @type {Object} codeBlockParameters - * @description Stores the values parsed by GetCodeBlockParameters(). - */ const codeBlockParameters: Object = await this.GetCodeBlockParameters(); - // console.log(codeBlockParameters); if (!codeBlockParameters) { GenerateFieldGroups(this.plugin, inputContainer, modelSelector.getValue(), null); } else { - /** - * @description Functions called to pre-select and pre-fill both dropdowns and input fields. - */ AutoAssignDeck(deckSelector, codeBlockParameters); AutoAssignModel(modelSelector, codeBlockParameters); if (codeBlockParameters["tags"] != null) { @@ -173,35 +121,12 @@ export class AddNoteFromCodeBlockModal extends Modal { * @return {Object} codeBlockParameters */ async GetCodeBlockParameters(): Promise { - /** - * @type {TFile} activeFileData - * @description The open and active file in the current instance of Obsidian. - * @remarks Since we can't retrieve a code block if no file is opened, if activeFileData is null, we shut down the function. - */ const activeFileData: TFile = this.app.workspace.getActiveFile(); - if (!activeFileData) { - return; - } - /** - * @type {string} activeFileContent - * @description The whole content of the note. - */ - const activeFileContent: string = await ReadFileContent(this, activeFileData); - /** - * @type {string} codeBlock - * @description The first code block using "AnkiIntegration" as its language in activeFileContent. - */ + if (!activeFileData) return; + const activeFileContent: string = await this.app.vault.read(activeFileData); const codeBlock: string = activeFileContent.match(/(```AnkiIntegration[\s\S]*?```)/)[1]; - /** - * @type {RegExp} regex - * @description A regular expression that is used to retrieve each line of the code block using a "Key: Value;" or "Key: "Value";" format. - */ - const regex: RegExp = /^\s*(\w+):\s*(?:"([^"]+)"|([^;]+));/gm; - /** - * @type {RegExp} tagsRegex - * @description A regular expression used to retrieve tags from the extracted corresponding line. - */ - const tagsRegex: RegExp = /"([^"]+)"/g; + const retrieveLinesRegex: RegExp = /^\s*(\w+):\s*(?:"([^"]+)"|([^;]+));/gm; + const retrieveTagsRegex: RegExp = /"([^"]+)"/g; /** * @type {Object} codeBlockParameters * @description The object that stores all the fields of the note that has to be created, along with their values. @@ -211,43 +136,20 @@ export class AddNoteFromCodeBlockModal extends Modal { "fields": {}, "tags": {} }; - /** - * @type {Array} match - * @description Stores all the result of regex.exec(codeBlock). - */ let match: Array = [] - /** - * @description As long as there are string that match the regex, - * we add them as field of codeBlockParameters or as field of codeBlockParameters["fields"]. - */ - while ((match = regex.exec(codeBlock)) !== null) { - /** - * @type {Array} codeBlockFields - * @description All the fields that has to be added as direct child fields of codeBlockParameters. - */ - const codeBlockChildFields: Array = ["deck", "model", "tags"]; - /** - * @type {string} key - * @description The key of the item that will be added to codeBlockParameters. - */ + while ((match = retrieveLinesRegex.exec(codeBlock)) !== null) { + const noteChildAttributes: Array = ["deck", "model", "tags"]; const key: string = match[1]; - /** - * @type {string} value - * @description The value of the item that will be added to codeBlockParameters. - */ const value: string = match[2] || match[3]; - /** - * @description If/else statements allowing to add a value as a direct child of codeBlockParameters or as a direct child of codeBlockParameters["fields"]. - */ - if (codeBlockChildFields.includes(key)) { - if (key == "tags") { - codeBlockParameters[key] = []; - while ((match = tagsRegex.exec(value)) !== null) { + const isKeyATag: boolean = key === "tags"; + const isKeyANoteChildAttribute: boolean = noteChildAttributes.includes(key); + if (isKeyANoteChildAttribute) { + codeBlockParameters[key] = isKeyATag ? [] : value; + if (isKeyATag) { + while ((match = retrieveTagsRegex.exec(value)) !== null) { const tag = match[1]; codeBlockParameters[key].push(tag); } - } else { - codeBlockParameters[key] = value; } } else { codeBlockParameters["fields"][key.toLowerCase()] = value; diff --git a/src/modals/AddNoteFromMetadataModal.ts b/src/modals/AddNoteFromMetadataModal.ts index 5606d10..d90ec27 100644 --- a/src/modals/AddNoteFromMetadataModal.ts +++ b/src/modals/AddNoteFromMetadataModal.ts @@ -1,29 +1,28 @@ import { - App, ButtonComponent, DropdownComponent, FrontMatterCache, + App, + DropdownComponent, + FrontMatterCache, Modal, TFile } from "obsidian"; import AnkiIntegration from "../main"; import { - ProcessAddNote -} from "../AnkiConnect"; -import { - AddButton, AddContainer, - AddDropdown, AddFieldGroups, AddInput, - AddOptionsToDropdownFromDataset, AddParagraph, - AddSubtitle, AddTagInputGroup, - AddTitle, AutoAssignDeck, AutoAssignModel, AutoGenerateFields, BuildTagsArray, CreateFieldsGroupData, - FetchModelByName + AddSubtitle, + AddTitle } from "../utils"; import { GenerateFieldGroups, - GenerateDeckSelector, - GenerateModelSelector, - GenerateSubmitButton, - GenerateTagsSection -} from "./modalsUtils"; + AddDeckSelector, + AddModelSelector, + AddSubmitButton, + AddTagsSection, + AutoAssignDeck, + AutoAssignModel, + AutoGenerateFields, + AddTagInputGroup +} from "./addNoteModalsUtils"; /** * A modal dialog for creating a new Anki note by using metadata as pre-filled values. @@ -43,13 +42,6 @@ export class AddNoteFromMetadataModal extends Modal { */ plugin: AnkiIntegration; - /** - * Creates a new AddNoteFromMetadataModal instance. - * Initializes the modal with the provided app and plugin. - * @param {App} app - The Obsidian app instance. - * @param {AnkiIntegration} plugin - The AnkiIntegration plugin instance. - * @constructor - */ constructor(app: App, plugin: AnkiIntegration) { super(app); this.plugin = plugin; @@ -62,10 +54,6 @@ export class AddNoteFromMetadataModal extends Modal { */ const ankiData: Object = this.plugin.settings.ankiData; - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; this.contentEl.focus(); @@ -81,23 +69,13 @@ export class AddNoteFromMetadataModal extends Modal { yaml = this.app.metadataCache.getFileCache(activeFileData).frontmatter; } - // Add the title and subtitle to the modal. AddTitle(contentEl, "Add a new note using metadata"); AddSubtitle(contentEl, "Deck & Model"); - /** - * @type {HTMLDivElement} dropdownContainer - * @description Container for the deck and model dropdown selectors. - */ - const dropdownContainer: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__dropdownContainer--flex" - ]); - - const deckSelector = GenerateDeckSelector(dropdownContainer, ankiData); - - const modelSelector: DropdownComponent = GenerateModelSelector(dropdownContainer, ankiData); - - const tagsContainer: HTMLDivElement = GenerateTagsSection(contentEl); + const dropdownContainer: HTMLDivElement = AddContainer(contentEl, ["ankiIntegrationModal__dropdownContainer--flex"]); + const deckSelector: DropdownComponent = AddDeckSelector(dropdownContainer, ankiData); + const modelSelector: DropdownComponent = AddModelSelector(dropdownContainer, ankiData); + const tagsContainer: HTMLDivElement = AddTagsSection(contentEl); const tagsBody: HTMLDivElement = tagsContainer.querySelector('#tagsBody'); const tagsBodyParagraph: HTMLElement = tagsContainer.querySelector('#tagsBodyTip'); @@ -108,38 +86,22 @@ export class AddNoteFromMetadataModal extends Modal { } } - // Add the "Fields" section subtitle. AddSubtitle(contentEl, "Fields"); + const inputFieldsContainer: HTMLDivElement = AddContainer(contentEl, ["ankiIntegrationModal__inputContainer--flex"]); - /** - * @type {HTMLDivElement} inputContainer - * @description Container for dynamically generated input fields based on the selected model. - */ - const inputContainer: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__inputContainer--flex" - ]); - - /** - * Event listener triggered when the model selector value changes. - * Updates the inputContainer with the fields corresponding to the selected model. - * @param {string} value - The selected model name. - */ modelSelector.onChange(async (value) => { - GenerateFieldGroups(this.plugin, inputContainer, value, yaml); + GenerateFieldGroups(this.plugin, inputFieldsContainer, value, yaml); }); - /** - * @description If yaml isn't null, trigger the autofill functions. Else, display the "Select a model [...]" message. - */ if (yaml != null) { AutoAssignDeck(deckSelector, yaml); AutoAssignModel(modelSelector, yaml); - AutoGenerateFields(this, modelSelector, inputContainer, yaml); + AutoGenerateFields(this, modelSelector, inputFieldsContainer, yaml); } else { - AddParagraph(inputContainer, "Select a model to see its fields."); + AddParagraph(inputFieldsContainer, "Select a model to see its fields."); } - GenerateSubmitButton(contentEl, deckSelector, modelSelector, inputContainer, this); + AddSubmitButton(contentEl, deckSelector, modelSelector, inputFieldsContainer, this); } /** @@ -147,13 +109,7 @@ export class AddNoteFromMetadataModal extends Modal { * Removes all elements within the modal's content area. */ onClose() { - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; - - // Clear the content of the modal. contentEl.empty(); } } \ No newline at end of file diff --git a/src/modals/AddNoteModal.ts b/src/modals/AddNoteModal.ts index 8fcddec..d25e1ee 100644 --- a/src/modals/AddNoteModal.ts +++ b/src/modals/AddNoteModal.ts @@ -1,21 +1,22 @@ import { - App, ButtonComponent, - Modal, + App, + DropdownComponent, + Modal } from "obsidian"; import AnkiIntegration from "../main"; import { - ProcessAddNote -} from "../AnkiConnect"; -import { - AddButton, AddContainer, - AddDropdown, AddFieldGroups, AddInput, - AddOptionsToDropdownFromDataset, AddParagraph, - AddSubtitle, AddTagInputGroup, - AddTitle, BuildTagsArray, CreateFieldsGroupData, - FetchModelByName + AddSubtitle, + AddTitle } from "../utils"; +import { + AddDeckSelector, + AddModelSelector, + AddSubmitButton, + AddTagsSection, + GenerateFieldGroups +} from "./addNoteModalsUtils"; /** * A modal dialog for creating a new Anki note. @@ -33,12 +34,6 @@ export class AddNoteModal extends Modal { */ plugin: AnkiIntegration; - /** - * Creates a new AddNoteModal instance. - * Initializes the modal with the provided app and plugin. - * @param {App} app - The Obsidian app instance. - * @param {AnkiIntegration} plugin - The AnkiIntegration plugin instance. - */ constructor(app: App, plugin: AnkiIntegration) { super(app); this.plugin = plugin; @@ -55,170 +50,30 @@ export class AddNoteModal extends Modal { */ const ankiData: Object = this.plugin.settings.ankiData; - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; this.contentEl.focus(); - // Add the title and subtitle to the modal. AddTitle(contentEl, "Add a new note"); AddSubtitle(contentEl, "Deck & Model"); - /** - * @type {HTMLDivElement} dropdownContainer - * @description Container for the deck and model dropdown selectors. - */ - const dropdownContainer: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__dropdownContainer--flex" - ]); + const dropdownContainer: HTMLDivElement = AddContainer(contentEl, ["ankiIntegrationModal__dropdownContainer--flex"]); + const deckSelector: DropdownComponent = AddDeckSelector(dropdownContainer, ankiData); + const modelSelector: DropdownComponent = AddModelSelector(dropdownContainer, ankiData); + AddTagsSection(contentEl); - // Create and configure the deck selector dropdown. - const deckSelector = AddDropdown(dropdownContainer, "Choose a deck"); - - /** - * @type {string[]} deckKeys - * @description An array containing the keys of all available decks. - */ - const deckKeys: string[] = Object.keys(ankiData["decksData"]); - AddOptionsToDropdownFromDataset(deckSelector, deckKeys, "name", "name", ankiData["decksData"]); - - // Create and configure the model selector dropdown. - const modelSelector = AddDropdown(dropdownContainer, "Choose a model"); - - /** - * @type {string[]} modelKeys - * @description An array containing the keys of all available models. - */ - const modelKeys: string[] = Object.keys(ankiData["modelsData"]); - AddOptionsToDropdownFromDataset(modelSelector, modelKeys, "name", "name", ankiData["modelsData"]); - - /** - * @type {HTMLDivElement} tagsHeader - * @description A container serving as the head part of the tags section. - */ - const tagsHeader: HTMLDivElement = AddContainer(contentEl, [ - "ankiIntegrationModal__container--flex-row", - "ankiIntegrationModal__container--flex-align-center", - "ankiIntegrationModal__container--flex-justify-space-between", - ]); - AddSubtitle(tagsHeader, "Tags"); - /** - * @type {ButtonComponent} addTagFieldButton - * @description Button used by the user to add a tag field in the pop-up. - */ - let addTagFieldButton: ButtonComponent = AddButton(tagsHeader, "", "circle-plus"); - addTagFieldButton.buttonEl.removeClasses([ - "ankiIntegrationModal__button--default-width", - "ankiIntegrationModal__button--default-margin", - "ankiIntegrationModal__button--default-padding" - ]); - /** - * @type {HTMLDivElement} tagsBody - * @description A container serving as the body of the tags section. - */ - const tagsBody: HTMLDivElement = AddContainer(contentEl); - tagsBody.addClasses([ - "ankiIntegrationModal__container--flex-row", - "ankiIntegrationModal__container--flex-wrap", - "ankiIntegrationModal__container--gap-16px" - ]) - - const tagsBodyParagraph: HTMLElement = AddParagraph(tagsBody, "No tags will be added to this note, click the \"+\" button to add a new one."); - - /** - * @description addTagFieldButton's onClick() event listener used to add a tag input group in tagsBody. - */ - addTagFieldButton.onClick(async () => { - if (tagsBody.firstChild == tagsBodyParagraph) { - tagsBody.removeChild(tagsBodyParagraph); - } - AddTagInputGroup(tagsBody, tagsBodyParagraph); - }); - - // Add the "Fields" section subtitle. AddSubtitle(contentEl, "Fields"); - /** - * @type {HTMLDivElement} inputContainer - * @description Container for dynamically generated input fields based on the selected model. - */ - const inputContainer: HTMLDivElement = AddContainer(contentEl, [ + const inputFieldsContainer: HTMLDivElement = AddContainer(contentEl, [ "ankiIntegrationModal__inputContainer--flex" ]); - // Display default message before a model is selected. - AddParagraph(inputContainer, "Select a model to see its fields."); + AddParagraph(inputFieldsContainer, "Select a model to see its fields."); - /** - * Event listener triggered when the model selector value changes. - * Updates the inputContainer with the fields corresponding to the selected model. - * @param {string} value - The selected model name. - */ modelSelector.onChange(async (value) => { - /** - * @type {Object} selectedModel - * @description The model object corresponding to the selected model name. - */ - const selectedModel: Object = FetchModelByName(this.plugin, value); - - /** - * @type {Array} fieldsGroupData - * An array of input data storing as separate object (1 objet = 1 input) the keys used to create each label-input pair and the values of each input. - */ - const fieldsGroupData: Array = []; - - inputContainer.empty(); - - /** - * @description - * Checks the currently selected option of the dropdown. - * If its value is default, it displays a message requesting the user to select a model. - * Else, it means that a model is selected, therefore, it creates the fields groups data and displays them. - */ - if (value === "default") { - AddParagraph(inputContainer, "Select a model to see its fields."); - return; - } else { - /** - * @description - * Create the different fields group objects and push them into fieldsGroupData. - */ - CreateFieldsGroupData(fieldsGroupData, selectedModel["fields"], {}); - AddFieldGroups(inputContainer, fieldsGroupData); - } + GenerateFieldGroups(this.plugin, inputFieldsContainer, value, null); }); - /** - * @type {ButtonComponent} submitButtonEl - * @description Submit button for the user to add the note. - */ - const submitButtonEl: ButtonComponent = AddButton(contentEl, "Create note"); - - /** - * @description - * "Click" event handler to send the form and trigger ProcessAddNote(). - * @async - * @param {MouseEvent} event - The click event triggered by the submit button. - */ - submitButtonEl.onClick(async () => { - const tags: Array = BuildTagsArray(); - await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, this); - }) - - /** - * @description - * "SHIFT + ENTER" event shortcut handler to send the form and trigger ProcessAddNote(). - * @async - * @param {KeyboardEvent} event - The registered keys that are pressed when contentEl is open. - */ - this.contentEl.addEventListener("keydown", async (event) => { - if (event.shiftKey && event.key === "Enter") { - const tags: Array = BuildTagsArray(); - await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, this); - } - }) + AddSubmitButton(contentEl, deckSelector, modelSelector, inputFieldsContainer, this) } /** @@ -226,13 +81,7 @@ export class AddNoteModal extends Modal { * Removes all elements within the modal's content area. */ onClose() { - /** - * @type {HTMLElement} contentEl - * @description The main content container of the modal. - */ const { contentEl } = this; - - // Clear the content of the modal. contentEl.empty(); } } \ No newline at end of file diff --git a/src/modals/addNoteModalsUtils.ts b/src/modals/addNoteModalsUtils.ts new file mode 100644 index 0000000..4b9312e --- /dev/null +++ b/src/modals/addNoteModalsUtils.ts @@ -0,0 +1,279 @@ +import { + ButtonComponent, + DropdownComponent, + FrontMatterCache, + Modal +} from "obsidian"; +import { + AddButton, + AddContainer, + AddDropdown, + AddFieldGroups, AddInput, + AddOptionsToDropdownFromDataset, + AddParagraph, + AddSubtitle, + FetchModelByName +} from "../utils"; +import {ProcessAddNote} from "../AnkiConnect"; +import AnkiIntegration from "../main"; +import {AddNoteFromMetadataModal} from "./AddNoteFromMetadataModal"; +import {AddNoteFromCodeBlockModal} from "./AddNoteFromCodeBlockModal"; + +export function AddDeckSelector(parent: HTMLDivElement, ankiData: Object) { + const deckSelector: DropdownComponent = AddDropdown(parent, "Choose a deck"); + const deckKeys: string[] = Object.keys(ankiData["decksData"]); + AddOptionsToDropdownFromDataset(deckSelector, deckKeys, "name", "name", ankiData["decksData"]); + + return deckSelector; +} + +export function AddModelSelector(parent: HTMLDivElement, ankiData: Object) { + const modelSelector: DropdownComponent = AddDropdown(parent, "Choose a model"); + const modelKeys: string[] = Object.keys(ankiData["modelsData"]); + AddOptionsToDropdownFromDataset(modelSelector, modelKeys, "name", "name", ankiData["modelsData"]); + + return modelSelector; +} + +export function AddTagsSection(parent: HTMLElement) { + const tagsContainer: HTMLDivElement = AddContainer(parent, [ + "ankiIntegrationModal__container--flex-column" + ]); + + const tagsHeader: HTMLDivElement = AddContainer(tagsContainer, [ + "ankiIntegrationModal__container--flex-row", + "ankiIntegrationModal__container--flex-align-center", + "ankiIntegrationModal__container--flex-justify-space-between", + ]); + + AddSubtitle(tagsHeader, "Tags"); + + let addTagFieldButton: ButtonComponent = AddButton(tagsHeader, "", "circle-plus"); + addTagFieldButton.buttonEl.removeClasses([ + "ankiIntegrationModal__button--default-width", + "ankiIntegrationModal__button--default-margin", + "ankiIntegrationModal__button--default-padding" + ]); + + const tagsBody: HTMLDivElement = AddContainer( + tagsContainer, + [ + "ankiIntegrationModal__container--flex-row", + "ankiIntegrationModal__container--flex-wrap", + "ankiIntegrationModal__container--gap-16px" + ], + "tagsBody") + ; + + const tagsBodyParagraph: HTMLElement = AddParagraph(tagsBody, "No tags will be added to this note, click the \"+\" button to add a new one.", [], "tagsBodyTip"); + + addTagFieldButton.onClick(async () => { + if (tagsBody.firstChild == tagsBodyParagraph) { + tagsBody.removeChild(tagsBodyParagraph); + } + AddTagInputGroup(tagsBody, tagsBodyParagraph); + }); + + return tagsContainer; +} + +export function AddSubmitButton(parent: HTMLElement, deckSelector: DropdownComponent, modelSelector: DropdownComponent, inputContainer: HTMLDivElement, modal: Modal) { + const submitButtonEl: ButtonComponent = AddButton(parent, "Create Note"); + + submitButtonEl.onClick(async () => { + const tags: Array = BuildTagsArray(); + await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, modal); + }); + + parent.addEventListener("keydown", async (event) => { + if (event.shiftKey && event.key === "Enter") { + const tags: Array = BuildTagsArray(); + await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, modal); + } + }) +} + +export function GenerateFieldGroups(plugin: AnkiIntegration, inputContainer: HTMLDivElement, selectedValue: any, inputValues: FrontMatterCache | Object | null) { + inputContainer.empty(); + + const selectedModel: Object = FetchModelByName(plugin, selectedValue); + const inputFieldGroupData: Array = []; + + if (selectedValue === "default") { + AddParagraph(inputContainer, "Select a model to see its fields."); + return; + } else { + if (inputValues) { + CreateFieldsGroupData(inputFieldGroupData, selectedModel["fields"], inputValues); + AddFieldGroups(inputContainer, inputFieldGroupData); + } else { + CreateFieldsGroupData(inputFieldGroupData, selectedModel["fields"]); + AddFieldGroups(inputContainer, inputFieldGroupData); + } + } +} + +/** + * Adds a div as a child of a given HTMLElement. The div contains an input and a button. + * @param {HTMLElement} parent - The parent container to which the button will be added. + * @param tagsBodyParagraph + * @param {string} tagValue - Speaking for itself. + * @return {HTMLDivElement} tagInputGroup + */ +export function AddTagInputGroup(parent: HTMLElement, tagsBodyParagraph: HTMLElement, tagValue: string = null): HTMLDivElement { + /** + * @type {HTMLDivElement} inputGroup + * @description A container storing the input field and the delete input field button. + */ + const tagInputGroup: HTMLDivElement = AddContainer(parent); + tagInputGroup.addClasses([ + "ankiIntegrationModal__container--width-fit-content", + "ankiIntegrationModal__container--flex-row" + ]); + /** + * @type {HTMLInputElement} tagInput + * @description A tag input field. + * @remarks Default width class has to be removed and replaced by a field-sizing width for the great-parent's wrap to work. + */ + const tagInput: HTMLInputElement = AddInput(tagInputGroup, "text", "My tag::Super", tagValue, [ + "ankiIntegrationModal__input--field-sizing-content", + "ankiIntegrationModal__tagInput--border", + "ankiIntegrationModal__tagInput--focus" + ]); + tagInput.removeClasses([ + "ankiIntegrationModal__input--default-width" + ]); + tagInput.id = 'tagInput'; + /** + * @type {ButtonComponent} deleteTagInputButton + * @description A button allowing the user to delete the field group the button belongs to. + * @remarks For the button to look great along with the input, the CTA is disabled, a class is added and all the default classes are removed. + */ + const deleteTagInputButton: ButtonComponent = AddButton(tagInputGroup, "", "x", [ + "ankiIntegrationModal__deleteInputButton--border", + "ankiIntegrationModal__icon--color-red" + ]); + deleteTagInputButton.removeCta(); + deleteTagInputButton.buttonEl.removeClasses([ + "ankiIntegrationModal__button--default-width", + "ankiIntegrationModal__button--default-margin", + "ankiIntegrationModal__button--default-padding" + ]); + /** + * @description deleteTagInputButton's onClick() event listener used to delete an input group in tagsBody. + */ + deleteTagInputButton.onClick(async () => { + tagInputGroup.remove(); + if (parent.children.length == 0) { + parent.appendChild(tagsBodyParagraph); + } + }) + + tagInput.focus(); + + return tagInputGroup; +} + +/** + * Check if the value of noteParameters["deck"] exists as an option of deckSelector and pre-select it if it exists. + */ +export function AutoAssignDeck(deckSelector: DropdownComponent, noteParameters: Object) { + let deckSelectorHasNoteParametersDeck: boolean = Array.from(deckSelector.selectEl.options).some(option => option.value === noteParameters["deck"]); + if (deckSelectorHasNoteParametersDeck) { + deckSelector.setValue(noteParameters["deck"]); + } +} + +/** + * Check if the value of noteParameters["model"] exists as an option of modelSelector and pre-select it if it exists. + */ +export function AutoAssignModel(modelSelector: DropdownComponent, noteParameters: Object) { + let modelSelectorHasNoteParametersModel: boolean = Array.from(modelSelector.selectEl.options).some(option => option.value === noteParameters["model"]); + if (modelSelectorHasNoteParametersModel) { + modelSelector.setValue(noteParameters["model"]); + } +} + +/** + * If there is no model metadata existing as model option, it displays the "Select a model..." message, + * else, since it means that a model has been preselected, it generates the fields groups and pre-fill them. + */ +export function AutoGenerateFields(modal: AddNoteFromMetadataModal | AddNoteFromCodeBlockModal, modelSelector: DropdownComponent, inputContainer: HTMLDivElement, noteParameters: Object) { + let modelSelectorHasNoteParametersModel: boolean = Array.from(modelSelector.selectEl.options).some(option => option.value === noteParameters["model"]); + if (!modelSelectorHasNoteParametersModel) { + AddParagraph(inputContainer, "Select a model to see its fields."); + } else { + GenerateFieldGroups(modal.plugin, inputContainer, modelSelector.getValue(), noteParameters); + } +} + +/** + * Function that create and push fields group data in a given array. + * + * @description + * The following for statement inject in fieldsGroupData a new object corresponding to an input and its label that has to be generated. + * Each object has the following property : + * - fieldName, a string used as the label text and the placeholder text of the input. + * - fieldValue, a string used as the value text of the input. + * The fieldName is mandatorily filled, while the fieldValue is optional by being set to null in the first place and being overwritten if the set of value contains a key that is the same as the currently used key. + * @param fieldsGroupData - The array in which fieldsGroupData Object will be push. + * @param {Array} keys - The set of keys. + * @param {Array} values - The set of values. + */ +export function CreateFieldsGroupData(fieldsGroupData: Array, keys: Array, values: Object = {}) { + for (let i = 0 ; i < keys.length; i++) { + const fieldName = keys[i]; + let fieldValue = null; + if (values["fields"]) { + fieldValue = ExtractValueFromCodeBlock(fieldValue, values, fieldName); + } else { + fieldValue = ExtractValueFromMetadata(fieldValue, values, fieldName); + } + fieldsGroupData[i] = { + fieldName: fieldName, + fieldValue: fieldValue + } + } +} + +/** + * Extract value from metadata. + * @param {string} fieldValue - The value to change. + * @param {Object} values - An object storing the gathered values to draw in. + * @param {string} fieldName - The name of the field the value has to be taken for. + * @return {string} fieldValue + */ +function ExtractValueFromMetadata(fieldValue: string, values: Object, fieldName: string): string { + if (values.hasOwnProperty(fieldName.toLowerCase())) { + fieldValue = values[fieldName.toLowerCase()]; + return fieldValue; + } +} + +/** + * Extract value from code block. + * @param {string} fieldValue - The value to change. + * @param {Object} values - An object storing the gathered values to draw in. + * @param {string} fieldName - The name of the field the value has to be taken for. + * @return {string} fieldValue + */ +function ExtractValueFromCodeBlock(fieldValue: string, values: Object, fieldName: string): string { + if (values["fields"].hasOwnProperty(fieldName.toLowerCase())) { + fieldValue = values["fields"][fieldName.toLowerCase()]; + return fieldValue; + } +} + +/** + * Build an array that stores all tags entered by the user in the form used to add a note. + * @return {Array} tags + */ +export function BuildTagsArray(): Array { + const tagInputs = document.querySelectorAll('#tagInput'); + let tags: Array = []; + tagInputs.forEach((tagInput) => { + // @ts-ignore + tags.push(tagInput.value); + }) + return tags; +} \ No newline at end of file diff --git a/src/modals/modalsUtils.ts b/src/modals/modalsUtils.ts deleted file mode 100644 index e77cb59..0000000 --- a/src/modals/modalsUtils.ts +++ /dev/null @@ -1,171 +0,0 @@ -import {ButtonComponent, DropdownComponent, FrontMatterCache, Modal} from "obsidian"; -import { - AddButton, - AddContainer, - AddDropdown, AddFieldGroups, - AddOptionsToDropdownFromDataset, - AddParagraph, - AddSubtitle, - AddTagInputGroup, BuildTagsArray, CreateFieldsGroupData, FetchModelByName -} from "../utils"; -import {ProcessAddNote} from "../AnkiConnect"; -import AnkiIntegration from "../main"; - -export function GenerateDeckSelector(parent: HTMLDivElement, ankiData: Object) { - /** - * @type {DropdownComponent} deckSelector - * @description Dropdown allowing the user to select a deck among those that are synchronized. - */ - const deckSelector: DropdownComponent = AddDropdown(parent, "Choose a deck"); - - /** - * @type {string[]} deckKeys - * @description An array containing the keys of all available decks. - */ - const deckKeys: string[] = Object.keys(ankiData["decksData"]); - AddOptionsToDropdownFromDataset(deckSelector, deckKeys, "name", "name", ankiData["decksData"]); - - return deckSelector; -} - -export function GenerateModelSelector(parent: HTMLDivElement, ankiData: Object) { - /** - * @type {DropdownComponent} modelSelector - * @description Dropdown allowing the user to select a model among those that are synchronized. - */ - const modelSelector: DropdownComponent = AddDropdown(parent, "Choose a model"); - - /** - * @type {string[]} modelKeys - * @description An array containing the keys of all available models. - */ - const modelKeys: string[] = Object.keys(ankiData["modelsData"]); - AddOptionsToDropdownFromDataset(modelSelector, modelKeys, "name", "name", ankiData["modelsData"]); - - return modelSelector; -} - -export function GenerateTagsSection(parent: HTMLElement) { - /** - * - */ - const tagsContainer: HTMLDivElement = AddContainer(parent, [ - "ankiIntegrationModal__container--flex-column" - ]) - /** - * @type {HTMLDivElement} tagsHeader - * @description A container serving as the head part of the tags section. - */ - const tagsHeader: HTMLDivElement = AddContainer(tagsContainer, [ - "ankiIntegrationModal__container--flex-row", - "ankiIntegrationModal__container--flex-align-center", - "ankiIntegrationModal__container--flex-justify-space-between", - ]); - AddSubtitle(tagsHeader, "Tags"); - /** - * @type {ButtonComponent} addTagFieldButton - * @description Button used by the user to add a tag field in the pop-up. - */ - let addTagFieldButton: ButtonComponent = AddButton(tagsHeader, "", "circle-plus"); - addTagFieldButton.buttonEl.removeClasses([ - "ankiIntegrationModal__button--default-width", - "ankiIntegrationModal__button--default-margin", - "ankiIntegrationModal__button--default-padding" - ]); - /** - * @type {HTMLDivElement} tagsBody - * @description A container serving as the body of the tags section. - */ - const tagsBody: HTMLDivElement = AddContainer( - tagsContainer, - [ - "ankiIntegrationModal__container--flex-row", - "ankiIntegrationModal__container--flex-wrap", - "ankiIntegrationModal__container--gap-16px" - ], - "tagsBody") - ; - - const tagsBodyParagraph: HTMLElement = AddParagraph(tagsBody, "No tags will be added to this note, click the \"+\" button to add a new one.", [], "tagsBodyTip"); - - /** - * @description addTagFieldButton's onClick() event listener used to add a tag input group in tagsBody. - */ - addTagFieldButton.onClick(async () => { - if (tagsBody.firstChild == tagsBodyParagraph) { - tagsBody.removeChild(tagsBodyParagraph); - } - /** - * @type {HTMLDivElement} inputGroup - * @description A container storing the input field and the delete input field button. - */ - const tagInputGroup: HTMLDivElement = AddTagInputGroup(tagsBody, tagsBodyParagraph); - }); - - return tagsContainer; -} - -export function GenerateSubmitButton(parent: HTMLElement, deckSelector: DropdownComponent, modelSelector: DropdownComponent, inputContainer: HTMLDivElement, modal: Modal) { - /** - * @type {ButtonComponent} submitButtonEl - * @description Submit button for the user to add the note. - */ - const submitButtonEl: ButtonComponent = AddButton(parent, "Create Note"); - - /** - * @description - * "Click" event handler to send the form and trigger ProcessAddNote(). - * @async - * @param {MouseEvent} event - The click event triggered by the submit button. - */ - submitButtonEl.onClick(async () => { - const tags: Array = BuildTagsArray(); - await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, modal); - }); - /** - * @description - * "SHIFT + ENTER" event shortcut handler to send the form and trigger ProcessAddNote(). - * @async - * @param {KeyboardEvent} event - The registered keys that are pressed when contentEl is open. - */ - parent.addEventListener("keydown", async (event) => { - if (event.shiftKey && event.key === "Enter") { - const tags: Array = BuildTagsArray(); - await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, modal); - } - }) -} - -export function GenerateFieldGroups(plugin: AnkiIntegration, inputContainer: HTMLDivElement, selectedValue: any, inputValues:FrontMatterCache | Object | null) { - inputContainer.empty(); - - /** - * @type {Object} selectedModel - * @description The model object corresponding to the selected model name. - */ - const selectedModel: Object = FetchModelByName(plugin, selectedValue); - /** - * @type {Array} fieldsGroupData - * @description An array of input data storing as separate object (1 object = 1 input) the keys used to create each label-input pair and the values of each input. - */ - const fieldsGroupData: Array = []; - - /** - * @description - * Checks the currently selected option of the dropdown. - * If its value is default, it displays a message requesting the user to select a model. - * Else, it means that a model is selected, therefore, it creates the fields groups data and displays them. - */ - if (selectedValue === "default") { - AddParagraph(inputContainer, "Select a model to see its fields."); - return; - } else { - if (inputValues) { - CreateFieldsGroupData(fieldsGroupData, selectedModel["fields"], inputValues); - AddFieldGroups(inputContainer, fieldsGroupData); - } else { - CreateFieldsGroupData(fieldsGroupData, selectedModel["fields"]); - AddFieldGroups(inputContainer, fieldsGroupData); - } - } -} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 0a8a98f..7e54446 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,8 @@ import AnkiIntegration from "./main"; -import {ButtonComponent, DropdownComponent, Modal, TFile} from "obsidian"; -import {AddNoteFromMetadataModal} from "./modals/AddNoteFromMetadataModal"; -import {AddNoteFromCodeBlockModal} from "./modals/AddNoteFromCodeBlockModal"; -import {GenerateFieldGroups} from "./modals/modalsUtils"; +import { + ButtonComponent, + DropdownComponent, +} from "obsidian"; /** * Fetches a model that has been saved in data.json by SynchronizeData(). @@ -11,10 +11,6 @@ import {GenerateFieldGroups} from "./modals/modalsUtils"; * @return {Object} model - The model data, if found. */ export function FetchModelByName(plugin: AnkiIntegration, name: string): Object { - /** - * @type {Object} modelsData - * A reference to the JSON containing every model-related data. - */ const modelsData: Object = plugin.settings.ankiData["modelsData"]; for (const [key, model] of Object.entries(modelsData)) { if (model.name === name) { @@ -35,10 +31,6 @@ export function AddContainer(parent: HTMLElement, classes: string[] = [], id: st * Pushes into classes all CSS classes that are mandatory for a container. */ classes.push(); - /** - * @type {HTMLDivElement} createdEl - * The created container. - */ let createdEl: HTMLDivElement; createdEl = parent.createEl("div", { cls: [ @@ -67,10 +59,6 @@ export function AddTitle(parent: HTMLElement, title: string, classes: string[] = "ankiIntegrationModal__h1--margin", "ankiIntegrationModal__h1--text-align" ); - /** - * @type {HTMLElement} createdEl - * The created title. - */ let createdEl: HTMLElement; createdEl = parent.createEl("h1", { text: title, @@ -96,10 +84,6 @@ export function AddSubtitle(parent: HTMLElement, subtitle: string, classes: stri classes.push( "ankiIntegrationModal__h2--fit-content" ); - /** - * @type {HTMLElement} createdEl - * The created subtitle. - */ let createdEl: HTMLElement; createdEl = parent.createEl("h2", { text: subtitle, @@ -127,10 +111,6 @@ export function AddParagraph(parent: HTMLElement, text: string, classes: string[ "ankiIntegrationModal__paragraph--text-align", "ankiIntegrationModal__paragraph--default-width" ); - /** - * @type {HTMLElement} createdEl - * The created paragraph. - */ let createdEl: HTMLElement; createdEl = parent.createEl("p", { text: text, @@ -159,10 +139,6 @@ export function AddDropdown(parent: HTMLElement, defaultString: string, classes: classes.push( "ankiIntegrationModal__dropdown--default-width" ) - /** - * @type {DropdownComponent} createdEl - * The created dropdown component. - */ let createdEl: DropdownComponent; createdEl = new DropdownComponent(parent); /** @@ -203,12 +179,6 @@ export function AddLabel(parent: HTMLElement, text: string, classes: string[] = * Pushes into classes all CSS classes that are mandatory for a container. */ classes.push(); - /** - * @type {HTMLLabelElement} createdEl - * The created label. - * @remarks - * Defining the created label as an `HTMLLabelElement` instead of a generic `HTMLElement` ensures access to label-specific properties. - */ let createdEl: HTMLLabelElement; createdEl = parent.createEl("label", { text: text, @@ -236,12 +206,6 @@ export function AddInput(parent: HTMLElement, type: string, placeholder: string classes.push( "ankiIntegrationModal__input--default-width" ); - /** - * @type {HTMLInputElement} createdEl - * The created input. - * @remarks - * Defining the created input as an `HTMLInputElement` instead of a generic `HTMLElement` ensures access to input-specific properties such as `value` or `checked`. - */ let createdEl: HTMLInputElement; createdEl = parent.createEl("input", { type: type, @@ -284,10 +248,6 @@ export function AddButton(parent: HTMLElement, text: string = null, icon: string "ankiIntegrationModal__button--default-margin", "ankiIntegrationModal__button--default-padding" ); - /** - * @type {ButtonComponent} createdEl - * The created button. - */ let createdEl: ButtonComponent = new ButtonComponent(parent); createdEl.setCta(); if (text) { @@ -300,177 +260,4 @@ export function AddButton(parent: HTMLElement, text: string = null, icon: string createdEl.setClass(cssClass); }) return createdEl; -} - -/** - * Function that create and push fields group data in a given array. - * - * @description - * The following for statement inject in fieldsGroupData a new object corresponding to an input and its label that has to be generated. - * Each object has the following property : - * - fieldName, a string used as the label text and the placeholder text of the input. - * - fieldValue, a string used as the value text of the input. - * The fieldName is mandatorily filled, while the fieldValue is optional by being set to null in the first place and being overwritten if the set of value contains a key that is the same as the currently used key. - * @param fieldsGroupData - The array in which fieldsGroupData Object will be push. - * @param {Array} keys - The set of keys. - * @param {Array} values - The set of values. - */ -export function CreateFieldsGroupData(fieldsGroupData: Array, keys: Array, values: Object = {}) { - for (let i = 0 ; i < keys.length; i++) { - const fieldName = keys[i]; - let fieldValue = null; - if (values["fields"]) { - fieldValue = ExtractValueFromCodeBlock(fieldValue, values, fieldName); - } else { - fieldValue = ExtractValueFromMetadata(fieldValue, values, fieldName); - } - fieldsGroupData[i] = { - fieldName: fieldName, - fieldValue: fieldValue - } - } -} - -/** - * Extract value from metadata. - * @param {string} fieldValue - The value to change. - * @param {Object} values - An object storing the gathered values to draw in. - * @param {string} fieldName - The name of the field the value has to be taken for. - * @return {string} fieldValue - */ -function ExtractValueFromMetadata(fieldValue: string, values: Object, fieldName: string): string { - if (values.hasOwnProperty(fieldName.toLowerCase())) { - fieldValue = values[fieldName.toLowerCase()]; - return fieldValue; - } -} - -/** - * Extract value from code block. - * @param {string} fieldValue - The value to change. - * @param {Object} values - An object storing the gathered values to draw in. - * @param {string} fieldName - The name of the field the value has to be taken for. - * @return {string} fieldValue - */ -function ExtractValueFromCodeBlock(fieldValue: string, values: Object, fieldName: string): string { - if (values["fields"].hasOwnProperty(fieldName.toLowerCase())) { - fieldValue = values["fields"][fieldName.toLowerCase()]; - return fieldValue; - } -} - -/** - * Function that returns the content of a given TFile. - * @param {Modal} modal - The instance of the modal that needs to read a file content. - * @param {TFile} fileData - The file that has to be read. - */ -export async function ReadFileContent(modal: Modal, fileData: TFile): Promise { - return await modal.app.vault.read(fileData); -} - -/** - * Check if the value of noteParameters["deck"] exists as an option of deckSelector and pre-select it if it exists. - */ -export function AutoAssignDeck(deckSelector: DropdownComponent, noteParameters: Object) { - let deckSelectorHasNoteParametersDeck: boolean = Array.from(deckSelector.selectEl.options).some(option => option.value === noteParameters["deck"]); - if (deckSelectorHasNoteParametersDeck) { - deckSelector.setValue(noteParameters["deck"]); - } -} - -/** - * Check if the value of noteParameters["model"] exists as an option of modelSelector and pre-select it if it exists. - */ -export function AutoAssignModel(modelSelector: DropdownComponent, noteParameters: Object) { - let modelSelectorHasNoteParametersModel: boolean = Array.from(modelSelector.selectEl.options).some(option => option.value === noteParameters["model"]); - if (modelSelectorHasNoteParametersModel) { - modelSelector.setValue(noteParameters["model"]); - } -} - -/** - * If there is no model metadata existing as model option, it displays the "Select a model..." message, - * else, since it means that a model has been preselected, it generates the fields groups and pre-fill them. - */ -export function AutoGenerateFields(modal: AddNoteFromMetadataModal | AddNoteFromCodeBlockModal, modelSelector: DropdownComponent, inputContainer: HTMLDivElement, noteParameters: Object) { - let modelSelectorHasNoteParametersModel: boolean = Array.from(modelSelector.selectEl.options).some(option => option.value === noteParameters["model"]); - if (!modelSelectorHasNoteParametersModel) { - AddParagraph(inputContainer, "Select a model to see its fields."); - } else { - GenerateFieldGroups(modal.plugin, inputContainer, modelSelector.getValue(), noteParameters); - } -} - -/** - * Build an array that stores all tags entered by the user in the form used to add a note. - * @return {Array} tags - */ -export function BuildTagsArray(): Array { - const tagInputs = document.querySelectorAll('#tagInput'); - let tags: Array = []; - tagInputs.forEach((tagInput) => { - // @ts-ignore - tags.push(tagInput.value); - }) - return tags; -} - -/** - * Adds a div as a child of a given HTMLElement. The div contains an input and a button. - * @param {HTMLElement} parent - The parent container to which the button will be added. - * @param {string} tagValue - Speaking for itself. - * @return {HTMLDivElement} tagInputGroup - */ -export function AddTagInputGroup(parent: HTMLElement, tagsBodyParagraph: HTMLElement, tagValue: string = null): HTMLDivElement { - /** - * @type {HTMLDivElement} inputGroup - * @description A container storing the input field and the delete input field button. - */ - const tagInputGroup: HTMLDivElement = AddContainer(parent); - tagInputGroup.addClasses([ - "ankiIntegrationModal__container--width-fit-content", - "ankiIntegrationModal__container--flex-row" - ]); - /** - * @type {HTMLInputElement} tagInput - * @description A tag input field. - * @remarks Default width class has to be removed and replaced by a field-sizing width for the great-parent's wrap to work. - */ - const tagInput: HTMLInputElement = AddInput(tagInputGroup, "text", "My tag::Super", tagValue, [ - "ankiIntegrationModal__input--field-sizing-content", - "ankiIntegrationModal__tagInput--border", - "ankiIntegrationModal__tagInput--focus" - ]); - tagInput.removeClasses([ - "ankiIntegrationModal__input--default-width" - ]); - tagInput.id = 'tagInput'; - /** - * @type {ButtonComponent} deleteTagInputButton - * @description A button allowing the user to delete the field group the button belongs to. - * @remarks For the button to look great along with the input, the CTA is disabled, a class is added and all the default classes are removed. - */ - const deleteTagInputButton: ButtonComponent = AddButton(tagInputGroup, "", "x", [ - "ankiIntegrationModal__deleteInputButton--border", - "ankiIntegrationModal__icon--color-red" - ]); - deleteTagInputButton.removeCta(); - deleteTagInputButton.buttonEl.removeClasses([ - "ankiIntegrationModal__button--default-width", - "ankiIntegrationModal__button--default-margin", - "ankiIntegrationModal__button--default-padding" - ]); - /** - * @description deleteTagInputButton's onClick() event listener used to delete an input group in tagsBody. - */ - deleteTagInputButton.onClick(async () => { - tagInputGroup.remove(); - if (parent.children.length == 0) { - parent.appendChild(tagsBodyParagraph); - } - }) - - tagInput.focus(); - - return tagInputGroup; } \ No newline at end of file