diff --git a/src/ui/components/suggesters/BookSuggest.svelte b/src/ui/components/suggesters/BookSuggest.svelte new file mode 100644 index 0000000..7b4f6ea --- /dev/null +++ b/src/ui/components/suggesters/BookSuggest.svelte @@ -0,0 +1,43 @@ + + + diff --git a/src/ui/modals/ReadingLogEntryEditModalView.svelte b/src/ui/modals/ReadingLogEntryEditModalView.svelte index a6887a9..bfc5e83 100644 --- a/src/ui/modals/ReadingLogEntryEditModalView.svelte +++ b/src/ui/modals/ReadingLogEntryEditModalView.svelte @@ -1,7 +1,7 @@
@@ -65,14 +61,7 @@
- + { - getSuggestions(inputStr: string): TFile[] { - const abstractFiles = this.app.vault.getAllLoadedFiles(); - const files: TFile[] = []; - const lowerCaseInputStr = inputStr.toLowerCase(); - - abstractFiles.forEach((file: TAbstractFile) => { - if ( - file instanceof TFile && - file.extension === "md" && - file.basename.toLowerCase().contains(lowerCaseInputStr) - ) { - files.push(file); - } - }); - - return files; - } - - renderSuggestion(file: TFile, el: HTMLElement): void { - el.setText(file.basename); - } - - selectSuggestion(file: TFile): void { - this.inputEl.value = file.basename; - this.inputEl.trigger("input"); - this.close(); - } -} diff --git a/src/ui/suggesters/FieldSuggest.ts b/src/ui/suggesters/FieldSuggest.ts deleted file mode 100644 index 47ee1cc..0000000 --- a/src/ui/suggesters/FieldSuggest.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { App } from "obsidian"; -import { TextInputSuggest } from "./core"; - -export class FieldSuggest extends TextInputSuggest { - constructor( - app: App, - inputEl: HTMLInputElement, - private readonly accepts?: string[] - ) { - super(app, inputEl); - } - - async getSuggestions(inputStr: string): Promise { - const typesContent = await this.app.vault.adapter.read( - this.app.vault.configDir + "/types.json" - ); - const types = JSON.parse(typesContent).types; - - return Object.entries(types) - .filter(([field, type]) => { - if (this.accepts && !this.accepts.includes(type as string)) { - return false; - } - - return field.toLowerCase().includes(inputStr.toLowerCase()); - }) - .map(([field, _]) => field); - } - - renderSuggestion(field: string, el: HTMLElement): void { - el.setText(field); - } - - selectSuggestion(field: string): void { - this.inputEl.value = field; - this.inputEl.trigger("input"); - this.close(); - } -} diff --git a/src/ui/suggesters/FileSuggest.ts b/src/ui/suggesters/FileSuggest.ts deleted file mode 100644 index edc053b..0000000 --- a/src/ui/suggesters/FileSuggest.ts +++ /dev/null @@ -1,34 +0,0 @@ -// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes - -import { TAbstractFile, TFile } from "obsidian"; -import { TextInputSuggest } from "./core"; - -export class FileSuggest extends TextInputSuggest { - getSuggestions(inputStr: string): TFile[] { - const abstractFiles = this.app.vault.getAllLoadedFiles(); - const files: TFile[] = []; - const lowerCaseInputStr = inputStr.toLowerCase(); - - abstractFiles.forEach((file: TAbstractFile) => { - if ( - file instanceof TFile && - file.extension === "md" && - file.path.toLowerCase().contains(lowerCaseInputStr) - ) { - files.push(file); - } - }); - - return files; - } - - renderSuggestion(file: TFile, el: HTMLElement): void { - el.setText(file.path); - } - - selectSuggestion(file: TFile): void { - this.inputEl.value = file.path; - this.inputEl.trigger("input"); - this.close(); - } -} diff --git a/src/ui/suggesters/FolderSuggest.ts b/src/ui/suggesters/FolderSuggest.ts deleted file mode 100644 index 881879f..0000000 --- a/src/ui/suggesters/FolderSuggest.ts +++ /dev/null @@ -1,33 +0,0 @@ -// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes - -import { TAbstractFile, TFolder } from "obsidian"; -import { TextInputSuggest } from "./core"; - -export class FolderSuggest extends TextInputSuggest { - getSuggestions(inputStr: string): TFolder[] { - const abstractFiles = this.app.vault.getAllLoadedFiles(); - const folders: TFolder[] = []; - const lowerCaseInputStr = inputStr.toLowerCase(); - - abstractFiles.forEach((folder: TAbstractFile) => { - if ( - folder instanceof TFolder && - folder.path.toLowerCase().contains(lowerCaseInputStr) - ) { - folders.push(folder); - } - }); - - return folders; - } - - renderSuggestion(file: TFolder, el: HTMLElement): void { - el.setText(file.path); - } - - selectSuggestion(file: TFolder): void { - this.inputEl.value = file.path; - this.inputEl.trigger("input"); - this.close(); - } -} diff --git a/src/ui/suggesters/core.ts b/src/ui/suggesters/core.ts deleted file mode 100644 index 1320cc6..0000000 --- a/src/ui/suggesters/core.ts +++ /dev/null @@ -1,205 +0,0 @@ -// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes - -import { App, type ISuggestOwner, Scope } from "obsidian"; -import { createPopper, type Instance as PopperInstance } from "@popperjs/core"; - -const wrapAround = (value: number, size: number): number => { - return ((value % size) + size) % size; -}; - -class Suggest { - private owner: ISuggestOwner; - private values: T[]; - private suggestions: HTMLDivElement[]; - private selectedItem: number; - private containerEl: HTMLElement; - - constructor( - owner: ISuggestOwner, - containerEl: HTMLElement, - scope: Scope - ) { - this.owner = owner; - this.containerEl = containerEl; - - containerEl.on( - "click", - ".suggestion-item", - this.onSuggestionClick.bind(this) - ); - containerEl.on( - "mousemove", - ".suggestion-item", - this.onSuggestionMouseover.bind(this) - ); - - scope.register([], "ArrowUp", (event) => { - if (!event.isComposing) { - this.setSelectedItem(this.selectedItem - 1, true); - return false; - } - }); - - scope.register([], "ArrowDown", (event) => { - if (!event.isComposing) { - this.setSelectedItem(this.selectedItem + 1, true); - return false; - } - }); - - scope.register([], "Enter", (event) => { - if (!event.isComposing) { - this.useSelectedItem(event); - return false; - } - }); - } - - onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void { - event.preventDefault(); - - const item = this.suggestions.indexOf(el); - this.setSelectedItem(item, false); - this.useSelectedItem(event); - } - - onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void { - const item = this.suggestions.indexOf(el); - this.setSelectedItem(item, false); - } - - setSuggestions(values: T[]) { - this.containerEl.empty(); - const suggestionEls: HTMLDivElement[] = []; - - values.forEach((value) => { - const suggestionEl = this.containerEl.createDiv("suggestion-item"); - this.owner.renderSuggestion(value, suggestionEl); - suggestionEls.push(suggestionEl); - }); - - this.values = values; - this.suggestions = suggestionEls; - this.setSelectedItem(0, false); - } - - useSelectedItem(event: MouseEvent | KeyboardEvent) { - const currentValue = this.values[this.selectedItem]; - if (currentValue) { - this.owner.selectSuggestion(currentValue, event); - } - } - - setSelectedItem(selectedIndex: number, scrollIntoView: boolean) { - const normalizedIndex = wrapAround( - selectedIndex, - this.suggestions.length - ); - const prevSelectedSuggestion = this.suggestions[this.selectedItem]; - const selectedSuggestion = this.suggestions[normalizedIndex]; - - prevSelectedSuggestion?.removeClass("is-selected"); - selectedSuggestion?.addClass("is-selected"); - - this.selectedItem = normalizedIndex; - - if (scrollIntoView) { - selectedSuggestion.scrollIntoView(false); - } - } -} - -export abstract class TextInputSuggest implements ISuggestOwner { - private popper: PopperInstance; - private scope: Scope; - private suggestEl: HTMLElement; - private suggest: Suggest; - - constructor( - protected app: App, - protected inputEl: HTMLInputElement | HTMLTextAreaElement - ) { - this.scope = new Scope(); - - this.suggestEl = createDiv("suggestion-container"); - const suggestion = this.suggestEl.createDiv("suggestion"); - this.suggest = new Suggest(this, suggestion, this.scope); - - this.scope.register([], "Escape", this.close.bind(this)); - - this.inputEl.addEventListener("input", this.onInputChanged.bind(this)); - this.inputEl.addEventListener("focus", this.onInputChanged.bind(this)); - this.inputEl.addEventListener("blur", this.close.bind(this)); - this.suggestEl.on( - "mousedown", - ".suggestion-container", - (event: MouseEvent) => { - event.preventDefault(); - } - ); - } - - async onInputChanged(): Promise { - const inputStr = this.inputEl.value; - let suggestions = this.getSuggestions(inputStr); - if (suggestions instanceof Promise) { - suggestions = await suggestions; - } - - if (!suggestions) { - this.close(); - return; - } - - if (suggestions.length > 0) { - this.suggest.setSuggestions(suggestions); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - this.open((this.app).dom.appContainerEl, this.inputEl); - } else { - this.close(); - } - } - - open(container: HTMLElement, inputEl: HTMLElement): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this.app).keymap.pushScope(this.scope); - - container.appendChild(this.suggestEl); - this.popper = createPopper(inputEl, this.suggestEl, { - placement: "bottom-start", - modifiers: [ - { - name: "sameWidth", - enabled: true, - fn: ({ state, instance }) => { - // Note: positioning needs to be calculated twice - - // first pass - positioning it according to the width of the popper - // second pass - position it with the width bound to the reference element - // we need to early exit to avoid an infinite loop - const targetWidth = `${state.rects.reference.width}px`; - if (state.styles.popper.width === targetWidth) { - return; - } - state.styles.popper.width = targetWidth; - instance.update(); - }, - phase: "beforeWrite", - requires: ["computeStyles"], - }, - ], - }); - } - - close(): void { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (this.app).keymap.popScope(this.scope); - - this.suggest.setSuggestions([]); - if (this.popper) this.popper.destroy(); - this.suggestEl.detach(); - } - - abstract getSuggestions(inputStr: string): T[] | Promise; - abstract renderSuggestion(item: T, el: HTMLElement): void; - abstract selectSuggestion(item: T): void; -} diff --git a/src/ui/suggesters/index.ts b/src/ui/suggesters/index.ts deleted file mode 100644 index 886f363..0000000 --- a/src/ui/suggesters/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { BookSuggest } from "./BookSuggest"; -export { FieldSuggest } from "./FieldSuggest"; -export { FileSuggest } from "./FileSuggest"; -export { FolderSuggest } from "./FolderSuggest";