Refactoring - Several refactoring

This commit is contained in:
Noah Boos 2025-07-08 22:57:51 +02:00
parent e694935aa2
commit e852f991bb
6 changed files with 363 additions and 761 deletions

View file

@ -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<void> {
/**
* @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<Object> {
/**
* @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<string> = []
/**
* @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<string>} codeBlockFields
* @description All the fields that has to be added as direct child fields of codeBlockParameters.
*/
const codeBlockChildFields: Array<string> = ["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<string> = ["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;

View file

@ -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();
}
}

View file

@ -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<Object> = [];
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<string> = 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<string> = 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();
}
}

View file

@ -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<string> = BuildTagsArray();
await ProcessAddNote(deckSelector, modelSelector, inputContainer, tags, modal);
});
parent.addEventListener("keydown", async (event) => {
if (event.shiftKey && event.key === "Enter") {
const tags: Array<string> = 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<Object> = [];
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<Object>, keys: Array<string>, 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<string>} tags
*/
export function BuildTagsArray(): Array<string> {
const tagInputs = document.querySelectorAll('#tagInput');
let tags: Array<string> = [];
tagInputs.forEach((tagInput) => {
// @ts-ignore
tags.push(tagInput.value);
})
return tags;
}

View file

@ -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<string> = 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<string> = 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<Object> = [];
/**
* @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);
}
}
}

View file

@ -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<string, string|number>} 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<Object>, keys: Array<string>, 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<string> {
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<string>} tags
*/
export function BuildTagsArray(): Array<string> {
const tagInputs = document.querySelectorAll('#tagInput');
let tags: Array<string> = [];
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;
}