Modal refactor (#842)

* Add Modal class

* Use Modal class
This commit is contained in:
toasted-nutbread 2020-09-19 17:14:51 -04:00 committed by GitHub
parent 613c7ebf69
commit 2ff4f83072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 115 additions and 41 deletions

View File

@ -17,6 +17,7 @@
/* global /* global
* AnkiNoteBuilder * AnkiNoteBuilder
* Modal
* TemplateRendererProxy * TemplateRendererProxy
* api * api
*/ */
@ -28,12 +29,15 @@ class AnkiTemplatesController {
this._cachedDefinitionValue = null; this._cachedDefinitionValue = null;
this._cachedDefinitionText = null; this._cachedDefinitionText = null;
this._defaultFieldTemplates = null; this._defaultFieldTemplates = null;
this._fieldTemplateResetModal = null;
this._templateRenderer = new TemplateRendererProxy(); this._templateRenderer = new TemplateRendererProxy();
} }
async prepare() { async prepare() {
this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates(); this._defaultFieldTemplates = await api.getDefaultAnkiFieldTemplates();
this._fieldTemplateResetModal = new Modal(document.querySelector('#field-template-reset-modal'));
const markers = new Set([ const markers = new Set([
...this._ankiController.getFieldMarkers('terms'), ...this._ankiController.getFieldMarkers('terms'),
...this._ankiController.getFieldMarkers('kanji') ...this._ankiController.getFieldMarkers('kanji')
@ -69,13 +73,13 @@ class AnkiTemplatesController {
_onReset(e) { _onReset(e) {
e.preventDefault(); e.preventDefault();
$('#field-template-reset-modal').modal('show'); this._fieldTemplateResetModal.setVisible(true);
} }
_onResetConfirm(e) { _onResetConfirm(e) {
e.preventDefault(); e.preventDefault();
$('#field-template-reset-modal').modal('hide'); this._fieldTemplateResetModal.setVisible(false);
const value = this._defaultFieldTemplates; const value = this._defaultFieldTemplates;

View File

@ -16,6 +16,7 @@
*/ */
/* global /* global
* Modal
* OptionsUtil * OptionsUtil
* api * api
*/ */
@ -26,12 +27,19 @@ class BackupController {
this._settingsExportToken = null; this._settingsExportToken = null;
this._settingsExportRevoke = null; this._settingsExportRevoke = null;
this._currentVersion = 0; this._currentVersion = 0;
this._settingsResetModal = null;
this._settingsImportErrorModal = null;
this._settingsImportWarningModal = null;
this._optionsUtil = new OptionsUtil(); this._optionsUtil = new OptionsUtil();
} }
async prepare() { async prepare() {
await this._optionsUtil.prepare(); await this._optionsUtil.prepare();
this._settingsResetModal = new Modal(document.querySelector('#settings-reset-modal'));
this._settingsImportErrorModal = new Modal(document.querySelector('#settings-import-error-modal'));
this._settingsImportWarningModal = new Modal(document.querySelector('#settings-import-warning-modal'));
document.querySelector('#settings-export').addEventListener('click', this._onSettingsExportClick.bind(this), false); document.querySelector('#settings-export').addEventListener('click', this._onSettingsExportClick.bind(this), false);
document.querySelector('#settings-import').addEventListener('click', this._onSettingsImportClick.bind(this), false); document.querySelector('#settings-import').addEventListener('click', this._onSettingsImportClick.bind(this), false);
document.querySelector('#settings-import-file').addEventListener('change', this._onSettingsImportFileChange.bind(this), false); document.querySelector('#settings-import-file').addEventListener('change', this._onSettingsImportFileChange.bind(this), false);
@ -153,14 +161,14 @@ class BackupController {
_showSettingsImportError(error) { _showSettingsImportError(error) {
yomichan.logError(error); yomichan.logError(error);
document.querySelector('#settings-import-error-modal-message').textContent = `${error}`; document.querySelector('#settings-import-error-modal-message').textContent = `${error}`;
$('#settings-import-error-modal').modal('show'); this._settingsImportErrorModal.setVisible(true);
} }
async _showSettingsImportWarnings(warnings) { async _showSettingsImportWarnings(warnings) {
const modalNode = $('#settings-import-warning-modal'); const modal = this._settingsImportWarningModal;
const buttons = document.querySelectorAll('.settings-import-warning-modal-import-button'); const buttons = document.querySelectorAll('.settings-import-warning-modal-import-button');
const messageContainer = document.querySelector('#settings-import-warning-modal-message'); const messageContainer = document.querySelector('#settings-import-warning-modal-message');
if (modalNode.length === 0 || buttons.length === 0 || messageContainer === null) { if (buttons.length === 0 || messageContainer === null) {
return {result: false}; return {result: false};
} }
@ -175,7 +183,7 @@ class BackupController {
messageContainer.appendChild(fragment); messageContainer.appendChild(fragment);
// Show modal // Show modal
modalNode.modal('show'); modal.setVisible(true);
// Wait for modal to close // Wait for modal to close
return new Promise((resolve) => { return new Promise((resolve) => {
@ -185,9 +193,10 @@ class BackupController {
result: true, result: true,
sanitize: e.currentTarget.dataset.importSanitize === 'true' sanitize: e.currentTarget.dataset.importSanitize === 'true'
}); });
modalNode.modal('hide'); modal.setVisible(false);
}; };
const onModalHide = () => { const onModalVisibilityChanged = ({visible}) => {
if (visible) { return; }
complete({result: false}); complete({result: false});
}; };
@ -196,7 +205,7 @@ class BackupController {
if (completed) { return; } if (completed) { return; }
completed = true; completed = true;
modalNode.off('hide.bs.modal', onModalHide); modal.off('visibilityChanged', onModalVisibilityChanged);
for (const button of buttons) { for (const button of buttons) {
button.removeEventListener('click', onButtonClick, false); button.removeEventListener('click', onButtonClick, false);
} }
@ -205,7 +214,7 @@ class BackupController {
}; };
// Hook events // Hook events
modalNode.on('hide.bs.modal', onModalHide); modal.on('visibilityChanged', onModalVisibilityChanged);
for (const button of buttons) { for (const button of buttons) {
button.addEventListener('click', onButtonClick, false); button.addEventListener('click', onButtonClick, false);
} }
@ -368,11 +377,11 @@ class BackupController {
// Resetting // Resetting
_onSettingsResetClick() { _onSettingsResetClick() {
$('#settings-reset-modal').modal('show'); this._settingsResetModal.setVisible(true);
} }
async _onSettingsResetConfirmClick() { async _onSettingsResetConfirmClick() {
$('#settings-reset-modal').modal('hide'); this._settingsResetModal.setVisible(false);
// Get default options // Get default options
const optionsFull = this._optionsUtil.getDefault(); const optionsFull = this._optionsUtil.getDefault();

View File

@ -16,6 +16,7 @@
*/ */
/* global /* global
* Modal
* ObjectPropertyAccessor * ObjectPropertyAccessor
* api * api
*/ */
@ -164,7 +165,7 @@ class DictionaryController {
this._checkIntegrityButton = document.querySelector('#dict-check-integrity'); this._checkIntegrityButton = document.querySelector('#dict-check-integrity');
this._dictionaryEntryContainer = document.querySelector('#dict-groups'); this._dictionaryEntryContainer = document.querySelector('#dict-groups');
this._integrityExtraInfoContainer = document.querySelector('#dict-groups-extra'); this._integrityExtraInfoContainer = document.querySelector('#dict-groups-extra');
this._deleteDictionaryModal = document.querySelector('#dict-delete-modal'); this._deleteDictionaryModal = new Modal(document.querySelector('#dict-delete-modal'));
yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this)); yomichan.on('databaseUpdated', this._onDatabaseUpdated.bind(this));
@ -177,9 +178,9 @@ class DictionaryController {
deleteDictionary(dictionaryTitle) { deleteDictionary(dictionaryTitle) {
if (this._isDeleting) { return; } if (this._isDeleting) { return; }
const modal = this._deleteDictionaryModal; const modal = this._deleteDictionaryModal;
modal.dataset.dictionaryTitle = dictionaryTitle; modal.node.dataset.dictionaryTitle = dictionaryTitle;
modal.querySelector('#dict-remove-modal-dict-name').textContent = dictionaryTitle; modal.node.querySelector('#dict-remove-modal-dict-name').textContent = dictionaryTitle;
this._setModalVisible(modal, true); modal.setVisible(true);
} }
// Private // Private
@ -209,11 +210,11 @@ class DictionaryController {
e.preventDefault(); e.preventDefault();
const modal = this._deleteDictionaryModal; const modal = this._deleteDictionaryModal;
this._setModalVisible(modal, false); modal.setVisible(false);
const title = modal.dataset.dictionaryTitle; const title = modal.node.dataset.dictionaryTitle;
if (typeof title !== 'string') { return; } if (typeof title !== 'string') { return; }
delete modal.dataset.dictionaryTitle; delete modal.node.dataset.dictionaryTitle;
this._deleteDictionary(title); this._deleteDictionary(title);
} }
@ -223,10 +224,6 @@ class DictionaryController {
this._checkIntegrity(); this._checkIntegrity();
} }
_setModalVisible(node, visible) {
$(node).modal(visible ? 'show' : 'hide');
}
_updateMainDictionarySelectOptions(dictionaries) { _updateMainDictionarySelectOptions(dictionaries) {
const fragment = document.createDocumentFragment(); const fragment = document.createDocumentFragment();

View File

@ -18,6 +18,7 @@
/* global /* global
* DictionaryDatabase * DictionaryDatabase
* DictionaryImporter * DictionaryImporter
* Modal
* ObjectPropertyAccessor * ObjectPropertyAccessor
* api * api
*/ */
@ -53,7 +54,7 @@ class DictionaryImportController {
this._purgeConfirmButton = document.querySelector('#dict-purge-confirm'); this._purgeConfirmButton = document.querySelector('#dict-purge-confirm');
this._importFileButton = document.querySelector('#dict-file-button'); this._importFileButton = document.querySelector('#dict-file-button');
this._importFileInput = document.querySelector('#dict-file'); this._importFileInput = document.querySelector('#dict-file');
this._purgeConfirmModal = document.querySelector('#dict-purge-modal'); this._purgeConfirmModal = new Modal(document.querySelector('#dict-purge-modal'));
this._errorContainer = document.querySelector('#dict-error'); this._errorContainer = document.querySelector('#dict-error');
this._spinner = document.querySelector('#dict-spinner'); this._spinner = document.querySelector('#dict-spinner');
this._progressContainer = document.querySelector('#dict-import-progress'); this._progressContainer = document.querySelector('#dict-import-progress');
@ -75,12 +76,12 @@ class DictionaryImportController {
_onPurgeButtonClick(e) { _onPurgeButtonClick(e) {
e.preventDefault(); e.preventDefault();
this._setPurgeModalVisible(true); this._purgeConfirmModal.setVisible(true);
} }
_onPurgeConfirmButtonClick(e) { _onPurgeConfirmButtonClick(e) {
e.preventDefault(); e.preventDefault();
this._setPurgeModalVisible(false); this._purgeConfirmModal.setVisible(false);
this._purgeDatabase(); this._purgeDatabase();
} }
@ -220,11 +221,6 @@ class DictionaryImportController {
return await this._modifyGlobalSettings(targets); return await this._modifyGlobalSettings(targets);
} }
_setPurgeModalVisible(visible) {
const node = $(this._purgeConfirmModal);
node.modal(visible ? 'show' : 'hide');
}
_setSpinnerVisible(visible) { _setSpinnerVisible(visible) {
this._spinner.hidden = !visible; this._spinner.hidden = !visible;
} }

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2020 Yomichan Authors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class Modal extends EventDispatcher {
constructor(node) {
super();
this._node = node;
this._eventListeners = new EventListenerCollection();
}
get node() {
return this._node;
}
setVisible(value) {
this._getWrappedNode().modal(value ? 'show' : 'hide');
}
on(eventName, callback) {
if (eventName === 'visibilityChanged') {
if (this._eventListeners.size === 0) {
const wrappedNode = this._getWrappedNode();
this._eventListeners.on(wrappedNode, 'hidden.bs.modal', this._onModalHide.bind(this));
this._eventListeners.on(wrappedNode, 'shown.bs.modal', this._onModalShow.bind(this));
}
}
return super.on(eventName, callback);
}
off(eventName, callback) {
const result = super.off(eventName, callback);
if (eventName === 'visibilityChanged' && !this.hasListeners(eventName)) {
this._eventListeners.removeAllEventListeners();
}
return result;
}
// Private
_onModalHide() {
this.trigger('visibilityChanged', {visible: false});
}
_onModalShow() {
this.trigger('visibilityChanged', {visible: true});
}
_getWrappedNode() {
return $(this._node);
}
}

View File

@ -16,6 +16,7 @@
*/ */
/* global /* global
* Modal
* ProfileConditionsUI * ProfileConditionsUI
* api * api
*/ */
@ -51,8 +52,8 @@ class ProfileController {
this._profileCopyButton = document.querySelector('#profile-copy'); this._profileCopyButton = document.querySelector('#profile-copy');
this._profileMoveUpButton = document.querySelector('#profile-move-up'); this._profileMoveUpButton = document.querySelector('#profile-move-up');
this._profileMoveDownButton = document.querySelector('#profile-move-down'); this._profileMoveDownButton = document.querySelector('#profile-move-down');
this._profileRemoveModal = document.querySelector('#profile-remove-modal'); this._profileRemoveModal = new Modal(document.querySelector('#profile-remove-modal'));
this._profileCopyModal = document.querySelector('#profile-copy-modal'); this._profileCopyModal = new Modal(document.querySelector('#profile-copy-modal'));
this._profileActiveSelect.addEventListener('change', this._onProfileActiveChange.bind(this), false); this._profileActiveSelect.addEventListener('change', this._onProfileActiveChange.bind(this), false);
this._profileTargetSelect.addEventListener('change', this._onProfileTargetChange.bind(this), false); this._profileTargetSelect.addEventListener('change', this._onProfileTargetChange.bind(this), false);
@ -135,11 +136,11 @@ class ProfileController {
const profileIndex = this._settingsController.profileIndex; const profileIndex = this._settingsController.profileIndex;
const profile = this._optionsFull.profiles[profileIndex]; const profile = this._optionsFull.profiles[profileIndex];
this._removeProfileNameElement.textContent = profile.name; this._removeProfileNameElement.textContent = profile.name;
this._setModalVisible(this._profileRemoveModal, true); this._profileRemoveModal.setVisible(true);
} }
_onRemoveConfirm() { _onRemoveConfirm() {
this._setModalVisible(this._profileRemoveModal, false); this._profileRemoveModal.setVisible(false);
if (this._optionsFull.profiles.length <= 1) { return; } if (this._optionsFull.profiles.length <= 1) { return; }
const profileIndex = this._settingsController.profileIndex; const profileIndex = this._settingsController.profileIndex;
this._removeProfile(profileIndex); this._removeProfile(profileIndex);
@ -160,11 +161,11 @@ class ProfileController {
} }
this._profileCopySourceSelect.value = `${copyFromIndex}`; this._profileCopySourceSelect.value = `${copyFromIndex}`;
this._setModalVisible(this._profileCopyModal, true); this._profileCopyModal.setVisible(true);
} }
_onCopyConfirm() { _onCopyConfirm() {
this._setModalVisible(this._profileCopyModal, false); this._profileCopyModal.setVisible(false);
const profileIndex = this._settingsController.profileIndex; const profileIndex = this._settingsController.profileIndex;
const max = this._optionsFull.profiles.length; const max = this._optionsFull.profiles.length;
@ -265,10 +266,6 @@ class ProfileController {
return null; return null;
} }
_setModalVisible(node, visible) {
$(node).modal(visible ? 'show' : 'hide');
}
async _addProfile() { async _addProfile() {
const profileIndex = this._settingsController.profileIndex; const profileIndex = this._settingsController.profileIndex;
const profiles = this._optionsFull.profiles; const profiles = this._optionsFull.profiles;

View File

@ -1216,6 +1216,7 @@
<script src="/bg/js/template-renderer-proxy.js"></script> <script src="/bg/js/template-renderer-proxy.js"></script>
<script src="/bg/js/settings/keyboard-mouse-input-field.js"></script> <script src="/bg/js/settings/keyboard-mouse-input-field.js"></script>
<script src="/bg/js/settings/modal.js"></script>
<script src="/bg/js/settings/profile-conditions-ui.js"></script> <script src="/bg/js/settings/profile-conditions-ui.js"></script>
<script src="/bg/js/settings/anki-controller.js"></script> <script src="/bg/js/settings/anki-controller.js"></script>

View File

@ -325,6 +325,11 @@ class EventDispatcher {
} }
return false; return false;
} }
hasListeners(eventName) {
const callbacks = this._eventMap.get(eventName);
return (typeof callbacks !== 'undefined' && callbacks.length > 0);
}
} }
class EventListenerCollection { class EventListenerCollection {