From 076e2012259cb7c446ee6cca8e574861f3f163db Mon Sep 17 00:00:00 2001 From: toasted-nutbread Date: Fri, 30 Apr 2021 18:01:16 -0400 Subject: Improve dictionary options (#1640) * Update dictionary options layout * Update extras * Improve display when no dictionaries are installed * Implement "All" toggle * Move modal * Update welcome page * Remove resizer button * Fix ordering * Fix extra closing tag --- ext/js/pages/settings/dictionary-controller.js | 293 +++++++++++++++---------- 1 file changed, 182 insertions(+), 111 deletions(-) (limited to 'ext/js/pages') diff --git a/ext/js/pages/settings/dictionary-controller.js b/ext/js/pages/settings/dictionary-controller.js index f000aa62..6d358f25 100644 --- a/ext/js/pages/settings/dictionary-controller.js +++ b/ext/js/pages/settings/dictionary-controller.js @@ -20,19 +20,21 @@ */ class DictionaryEntry { - constructor(dictionaryController, node, index, dictionaryInfo) { + constructor(dictionaryController, fragment, index, dictionaryInfo) { this._dictionaryController = dictionaryController; - this._node = node; this._index = index; this._dictionaryInfo = dictionaryInfo; this._eventListeners = new EventListenerCollection(); - this._detailsContainer = null; - this._hasDetails = false; - this._hasCounts = false; - } - - get node() { - return this._node; + this._counts = null; + this._nodes = [...fragment.childNodes]; + this._enabledCheckbox = fragment.querySelector('.dictionary-enabled'); + this._priorityInput = fragment.querySelector('.dictionary-priority'); + this._menuButton = fragment.querySelector('.dictionary-menu-button'); + this._outdatedButton = fragment.querySelector('.dictionary-outdated-button'); + this._integrityButton = fragment.querySelector('.dictionary-integrity-button'); + this._titleNode = fragment.querySelector('.dictionary-title'); + this._versionNode = fragment.querySelector('.dictionary-version'); + this._titleContainer = fragment.querySelector('.dictionary-item-title-container'); } get dictionaryTitle() { @@ -40,98 +42,80 @@ class DictionaryEntry { } prepare() { - const node = this._node; const index = this._index; - const {title, revision, prefixWildcardsSupported, version} = this._dictionaryInfo; - - this._detailsContainer = node.querySelector('.dictionary-details'); - - const enabledCheckbox = node.querySelector('.dictionary-enabled'); - const priorityInput = node.querySelector('.dictionary-priority'); - const menuButton = node.querySelector('.dictionary-menu-button'); - const detailsTable = node.querySelector('.dictionary-details-table'); - const outdatedContainer = node.querySelector('.dictionary-outdated-notification'); - const titleNode = node.querySelector('.dictionary-title'); - const versionNode = node.querySelector('.dictionary-version'); - const wildcardSupportedCheckbox = node.querySelector('.dictionary-prefix-wildcard-searches-supported'); - - const hasDetails = (detailsTable !== null && this._setupDetails(detailsTable)); - this._hasDetails = hasDetails; - - titleNode.textContent = title; - versionNode.textContent = `rev.${revision}`; - if (wildcardSupportedCheckbox !== null) { - wildcardSupportedCheckbox.checked = !!prefixWildcardsSupported; - } - if (outdatedContainer !== null) { - outdatedContainer.hidden = (version >= 3); - } - if (enabledCheckbox !== null) { - enabledCheckbox.dataset.setting = `dictionaries[${index}].enabled`; - this._eventListeners.addEventListener(enabledCheckbox, 'settingChanged', this._onEnabledChanged.bind(this), false); - } - if (priorityInput !== null) { - priorityInput.dataset.setting = `dictionaries[${index}].priority`; - } - if (menuButton !== null) { - this._eventListeners.addEventListener(menuButton, 'menuOpen', this._onMenuOpen.bind(this), false); - this._eventListeners.addEventListener(menuButton, 'menuClose', this._onMenuClose.bind(this), false); - } + const {title, revision, version} = this._dictionaryInfo; + + this._titleNode.textContent = title; + this._versionNode.textContent = `rev.${revision}`; + this._outdatedButton.hidden = (version >= 3); + this._priorityInput.dataset.setting = `dictionaries[${index}].priority`; + this._enabledCheckbox.dataset.setting = `dictionaries[${index}].enabled`; + this._eventListeners.addEventListener(this._enabledCheckbox, 'settingChanged', this._onEnabledChanged.bind(this), false); + this._eventListeners.addEventListener(this._menuButton, 'menuClose', this._onMenuClose.bind(this), false); + this._eventListeners.addEventListener(this._outdatedButton, 'click', this._onOutdatedButtonClick.bind(this), false); + this._eventListeners.addEventListener(this._integrityButton, 'click', this._onIntegrityButtonClick.bind(this), false); } cleanup() { this._eventListeners.removeAllEventListeners(); - const node = this._node; - if (node.parentNode !== null) { - node.parentNode.removeChild(node); + for (const node of this._nodes) { + if (node.parentNode !== null) { + node.parentNode.removeChild(node); + } } + this._nodes = []; } setCounts(counts) { - const node = this._node.querySelector('.dictionary-counts'); - node.textContent = JSON.stringify({info: this._dictionaryInfo, counts}, null, 4); - node.hidden = false; - this._hasCounts = true; + this._counts = counts; + this._integrityButton.hidden = false; } - // Private - - _onMenuOpen(e) { - const bodyNode = e.detail.menu.bodyNode; - const showDetails = bodyNode.querySelector('.popup-menu-item[data-menu-action="showDetails"]'); - const hideDetails = bodyNode.querySelector('.popup-menu-item[data-menu-action="hideDetails"]'); - const hasDetails = (this._detailsContainer !== null); - const detailsVisible = (hasDetails && !this._detailsContainer.hidden); - if (showDetails !== null) { - showDetails.hidden = detailsVisible; - showDetails.disabled = !hasDetails; - } - if (hideDetails !== null) { - hideDetails.hidden = !detailsVisible; - hideDetails.disabled = !hasDetails; - } + setEnabled(value) { + this._enabledCheckbox.checked = value; } + // Private + _onMenuClose(e) { switch (e.detail.action) { case 'delete': this._delete(); break; case 'showDetails': - if (this._detailsContainer !== null) { this._detailsContainer.hidden = false; } - break; - case 'hideDetails': - if (this._detailsContainer !== null) { this._detailsContainer.hidden = true; } + this._showDetails(); break; } } _onEnabledChanged(e) { const {detail: {value}} = e; - this._node.dataset.enabled = `${value}`; + this._titleContainer.dataset.enabled = `${value}`; this._dictionaryController.updateDictionariesEnabled(); } + _onOutdatedButtonClick() { + this._showDetails(); + } + + _onIntegrityButtonClick() { + this._showDetails(); + } + + _showDetails() { + const {title, revision, version} = this._dictionaryInfo; + + const modal = this._dictionaryController.modalController.getModal('dictionary-details'); + + modal.node.querySelector('.dictionary-title').textContent = title; + modal.node.querySelector('.dictionary-version').textContent = `rev.${revision}`; + modal.node.querySelector('.dictionary-outdated-notification').hidden = (version >= 3); + modal.node.querySelector('.dictionary-counts').textContent = this._counts !== null ? JSON.stringify(this._counts, null, 4) : ''; + this._setupDetails(modal.node.querySelector('.dictionary-details-table')); + + modal.setVisible(true); + } + _setupDetails(detailsTable) { const targets = [ ['Author', 'author'], @@ -156,6 +140,7 @@ class DictionaryEntry { any = true; } + detailsTable.textContent = ''; detailsTable.appendChild(fragment); return any; } @@ -165,6 +150,57 @@ class DictionaryEntry { } } +class DictionaryExtraInfo { + constructor(parent, totalCounts, remainders, totalRemainder) { + this._parent = parent; + this._totalCounts = totalCounts; + this._remainders = remainders; + this._totalRemainder = totalRemainder; + this._eventListeners = new EventListenerCollection(); + this._nodes = null; + } + + prepare(container) { + const fragment = this._parent.instantiateTemplateFragment('dictionary-extra'); + this._nodes = [...fragment.childNodes]; + + this._setTitle(fragment.querySelector('.dictionary-total-count')); + this._eventListeners.addEventListener(fragment.querySelector('.dictionary-integrity-button'), 'click', this._onIntegrityButtonClick.bind(this), false); + + container.appendChild(fragment); + } + + cleanup() { + this._eventListeners.removeAllEventListeners(); + for (const node of this._nodes) { + if (node.parentNode !== null) { + node.parentNode.removeChild(node); + } + } + this._nodes = []; + } + + // Private + + _onIntegrityButtonClick() { + this._showDetails(); + } + + _showDetails() { + const modal = this._parent.modalController.getModal('dictionary-extra-data'); + + const info = {counts: this._totalCounts, remainders: this._remainders}; + modal.node.querySelector('.dictionary-counts').textContent = JSON.stringify(info, null, 4); + this._setTitle(modal.node.querySelector('.dictionary-total-count')); + + modal.setVisible(true); + } + + _setTitle(node) { + node.textContent = `${this._totalRemainder} item${this._totalRemainder !== 1 ? 's' : ''}`; + } +} + class DictionaryController { constructor(settingsController, modalController, statusFooter) { this._settingsController = settingsController; @@ -176,34 +212,40 @@ class DictionaryController { this._checkingIntegrity = false; this._checkIntegrityButton = null; this._dictionaryEntryContainer = null; - this._integrityExtraInfoContainer = null; this._dictionaryInstallCountNode = null; this._dictionaryEnabledCountNode = null; this._noDictionariesInstalledWarnings = null; this._noDictionariesEnabledWarnings = null; this._deleteDictionaryModal = null; - this._integrityExtraInfoNode = null; + this._allCheckbox = null; + this._extraInfo = null; this._isDeleting = false; } + get modalController() { + return this._modalController; + } + async prepare() { this._checkIntegrityButton = document.querySelector('#dictionary-check-integrity'); this._dictionaryEntryContainer = document.querySelector('#dictionary-list'); - this._integrityExtraInfoContainer = document.querySelector('#dictionary-list-extra'); this._dictionaryInstallCountNode = document.querySelector('#dictionary-install-count'); this._dictionaryEnabledCountNode = document.querySelector('#dictionary-enabled-count'); this._noDictionariesInstalledWarnings = document.querySelectorAll('.no-dictionaries-installed-warning'); this._noDictionariesEnabledWarnings = document.querySelectorAll('.no-dictionaries-enabled-warning'); this._deleteDictionaryModal = this._modalController.getModal('dictionary-confirm-delete'); + this._allCheckbox = document.querySelector('#all-dictionaries-enabled'); yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this)); this._settingsController.on('optionsChanged', this._onOptionsChanged.bind(this)); - + this._allCheckbox.addEventListener('change', this._onAllCheckboxChange.bind(this), false); document.querySelector('#dictionary-confirm-delete-button').addEventListener('click', this._onDictionaryConfirmDelete.bind(this), false); if (this._checkIntegrityButton !== null) { this._checkIntegrityButton.addEventListener('click', this._onCheckIntegrityButtonClick.bind(this), false); } + this._updateDictionaryEntryCount(); + await this._onDatabaseUpdated(); } @@ -219,6 +261,10 @@ class DictionaryController { return this._settingsController.instantiateTemplate(name); } + instantiateTemplateFragment(name) { + return this._settingsController.instantiateTemplateFragment(name); + } + async updateDictionariesEnabled() { const options = await this._settingsController.getOptions(); this._updateDictionariesEnabledWarnings(options); @@ -303,6 +349,12 @@ class DictionaryController { await this._updateEntries(); } + _onAllCheckboxChange() { + const value = this._allCheckbox.checked; + this._allCheckbox.checked = !value; + this._setAllDictionariesEnabled(value); + } + async _updateEntries() { const dictionaries = this._dictionaries; this._updateMainDictionarySelectOptions(dictionaries); @@ -311,6 +363,7 @@ class DictionaryController { entry.cleanup(); } this._dictionaryEntries = []; + this._updateDictionaryEntryCount(); if (this._dictionaryInstallCountNode !== null) { this._dictionaryInstallCountNode.textContent = `${dictionaries.length}`; @@ -341,29 +394,40 @@ class DictionaryController { } _updateDictionariesEnabledWarnings(options) { - let enabledCount = 0; + const {dictionaries} = options; + let enabledDictionaryCountValid = 0; + let enabledDictionaryCount = 0; + const dictionaryCount = dictionaries.length; if (this._dictionaries !== null) { const enabledDictionaries = new Set(); - for (const {name, enabled} of options.dictionaries) { + for (const {name, enabled} of dictionaries) { if (enabled) { + ++enabledDictionaryCount; enabledDictionaries.add(name); } } for (const {title} of this._dictionaries) { if (enabledDictionaries.has(title)) { - ++enabledCount; + ++enabledDictionaryCountValid; } } } - const hasEnabledDictionary = (enabledCount > 0); + const hasEnabledDictionary = (enabledDictionaryCountValid > 0); for (const node of this._noDictionariesEnabledWarnings) { node.hidden = hasEnabledDictionary; } if (this._dictionaryEnabledCountNode !== null) { - this._dictionaryEnabledCountNode.textContent = `${enabledCount}`; + this._dictionaryEnabledCountNode.textContent = `${enabledDictionaryCountValid}`; + } + + this._allCheckbox.checked = (enabledDictionaryCount >= dictionaryCount); + + const entries = this._dictionaryEntries; + for (let i = 0, ii = Math.min(entries.length, dictionaryCount); i < ii; ++i) { + entries[i].setEnabled(dictionaries[i].enabled); } } @@ -447,43 +511,29 @@ class DictionaryController { totalRemainder += remainders[key]; } - this._cleanupExtra(); - if (totalRemainder > 0) { - this.extra = this._createExtra(totalCounts, remainders, totalRemainder); + if (this._extraInfo !== null) { + this._extraInfo.cleanup(); + this._extraInfo = null; } - } - - _createExtra(totalCounts, remainders, totalRemainder) { - const node = this.instantiateTemplate('dictionary-extra'); - this._integrityExtraInfoNode = node; - - node.querySelector('.dictionary-total-count').textContent = `${totalRemainder} item${totalRemainder !== 1 ? 's' : ''}`; - - const n = node.querySelector('.dictionary-counts'); - n.textContent = JSON.stringify({counts: totalCounts, remainders}, null, 4); - n.hidden = false; - - this._integrityExtraInfoContainer.appendChild(node); - } - _cleanupExtra() { - const node = this._integrityExtraInfoNode; - if (node === null) { return; } - this._integrityExtraInfoNode = null; - - const parent = node.parentNode; - if (parent === null) { return; } - - parent.removeChild(node); + if (totalRemainder > 0) { + this._extraInfo = new DictionaryExtraInfo(this, totalCounts, remainders, totalRemainder); + this._extraInfo.prepare(this._dictionaryEntryContainer); + } } _createDictionaryEntry(index, dictionaryInfo) { - const node = this.instantiateTemplate('dictionary'); - this._dictionaryEntryContainer.appendChild(node); + const fragment = this.instantiateTemplateFragment('dictionary'); - const entry = new DictionaryEntry(this, node, index, dictionaryInfo); + const entry = new DictionaryEntry(this, fragment, index, dictionaryInfo); this._dictionaryEntries.push(entry); entry.prepare(); + + const container = this._dictionaryEntryContainer; + const relative = container.querySelector('.dictionary-item-bottom'); + container.insertBefore(fragment, relative); + + this._updateDictionaryEntryCount(); } async _deleteDictionary(dictionaryTitle) { @@ -589,4 +639,25 @@ class DictionaryController { _triggerStorageChanged() { yomichan.trigger('storageChanged'); } + + _updateDictionaryEntryCount() { + this._dictionaryEntryContainer.dataset.count = `${this._dictionaryEntries.length}`; + } + + async _setAllDictionariesEnabled(value) { + const options = await this._settingsController.getOptions(); + const {dictionaries} = options; + + const targets = []; + for (let i = 0, ii = dictionaries.length; i < ii; ++i) { + targets.push({ + action: 'set', + path: `dictionaries[${i}].enabled`, + value + }); + } + await this._settingsController.modifyProfileSettings(targets); + + await this.updateDictionariesEnabled(); + } } -- cgit v1.2.3