import {App, ButtonComponent, Modal, Setting} from 'obsidian'; import {SelectModalElement} from './SelectModalElement'; import {mod} from '../utils/Utils'; export abstract class SelectModal extends Modal { allowMultiSelect: boolean; title: string; description: string; addSkipButton: boolean; cancelButton?: ButtonComponent; skipButton?: ButtonComponent; submitButton?: ButtonComponent; elementWrapper?: HTMLDivElement; elements: T[]; selectModalElements: SelectModalElement[]; protected constructor(app: App, elements: T[], allowMultiSelect: boolean = true) { super(app); this.allowMultiSelect = allowMultiSelect; this.title = ''; this.description = ''; this.addSkipButton = false; this.cancelButton = undefined; this.skipButton = undefined; this.submitButton = undefined; this.elementWrapper = undefined; this.elements = elements; this.selectModalElements = []; this.scope.register([], 'ArrowUp', (evt) => { this.highlightUp(); evt.preventDefault(); }); this.scope.register([], 'ArrowDown', (evt) => { this.highlightDown(); evt.preventDefault(); }); this.scope.register([], 'ArrowRight', () => { this.activateHighlighted(); }); this.scope.register([], ' ', (evt) => { if (this.elementWrapper && this.elementWrapper === document.activeElement) { this.activateHighlighted(); evt.preventDefault(); } }); this.scope.register([], 'Enter', () => this.submit()); } abstract renderElement(value: T, el: HTMLElement): any; abstract submit(): void; abstract skip(): void; disableAllOtherElements(elementId: number) { for (const selectModalElement of this.selectModalElements) { if (selectModalElement.id !== elementId) { selectModalElement.setActive(false); } } } deHighlightAllOtherElements(elementId: number) { for (const selectModalElement of this.selectModalElements) { if (selectModalElement.id !== elementId) { selectModalElement.setHighlighted(false); } } } async onOpen() { const {contentEl, titleEl} = this; titleEl.createEl('h2', {text: this.title}); contentEl.addClass('media-db-plugin-select-modal'); contentEl.createEl('p', {text: this.description}); this.elementWrapper = contentEl.createDiv({cls: 'media-db-plugin-select-wrapper'}); this.elementWrapper.tabIndex = 0; let i = 0; for (const element of this.elements) { const selectModalElement = new SelectModalElement(element, this.elementWrapper, i, this, false); this.selectModalElements.push(selectModalElement); this.renderElement(element, selectModalElement.element); i += 1; } this.selectModalElements.first()?.element.scrollIntoView(); const bottomSettingRow = new Setting(contentEl); bottomSettingRow.addButton(btn => { btn.setButtonText('Cancel'); btn.onClick(() => this.close()); btn.buttonEl.addClass('media-db-plugin-button'); this.cancelButton = btn; }); if (this.addSkipButton) { bottomSettingRow.addButton(btn => { btn.setButtonText('Skip'); btn.onClick(() => this.skip()); btn.buttonEl.addClass('media-db-plugin-button'); this.skipButton = btn; }); } bottomSettingRow.addButton(btn => { btn.setButtonText('Ok'); btn.setCta(); btn.onClick(() => this.submit()); btn.buttonEl.addClass('media-db-plugin-button'); this.submitButton = btn; }); } activateHighlighted() { for (const selectModalElement of this.selectModalElements) { if (selectModalElement.isHighlighted()) { selectModalElement.setActive(!selectModalElement.isActive()); if (!this.allowMultiSelect) { this.disableAllOtherElements(selectModalElement.id); } } } } highlightUp() { for (const selectModalElement of this.selectModalElements) { if (selectModalElement.isHighlighted()) { this.getPreviousSelectModalElement(selectModalElement).setHighlighted(true); return; } } // nothing is highlighted this.selectModalElements.last().setHighlighted(true); } highlightDown() { for (const selectModalElement of this.selectModalElements) { if (selectModalElement.isHighlighted()) { this.getNextSelectModalElement(selectModalElement).setHighlighted(true); return; } } // nothing is highlighted this.selectModalElements.first().setHighlighted(true); } private getNextSelectModalElement(selectModalElement: SelectModalElement): SelectModalElement { let nextId = selectModalElement.id + 1; nextId = mod(nextId, this.selectModalElements.length); return this.selectModalElements.filter(x => x.id === nextId).first(); } private getPreviousSelectModalElement(selectModalElement: SelectModalElement): SelectModalElement { let nextId = selectModalElement.id - 1; nextId = mod(nextId, this.selectModalElements.length); return this.selectModalElements.filter(x => x.id === nextId).first(); } }