summaryrefslogtreecommitdiff
path: root/ext/js
diff options
context:
space:
mode:
authorKuuuube <61125188+Kuuuube@users.noreply.github.com>2024-06-18 14:00:52 -0400
committerGitHub <noreply@github.com>2024-06-18 18:00:52 +0000
commitebd911201df0e6fce95b408935fb35580851170e (patch)
tree1955088b5c5157a02b1d75d8994e20114199830c /ext/js
parent129be78c9bf1b9d378050c6ca6ec722ef456c4ee (diff)
Add full dark mode support (#1079)
* Add full dark mode support * Fix welcome, permissions, and quick start scrollbars * Fix action popup dark mode on mobile * Add theme to info page * Reduce flashbang * Move position of settingsDisplayController to not break things * Fix dictionary import drag drop theming * Make dark shadow color less bad * Prepare themeController to avoid not being able to set browser theme
Diffstat (limited to 'ext/js')
-rw-r--r--ext/js/pages/action-popup-main.js9
-rw-r--r--ext/js/pages/info-main.js12
-rw-r--r--ext/js/pages/permissions-main.js6
-rw-r--r--ext/js/pages/quick-start-guide-main.js35
-rw-r--r--ext/js/pages/settings/popup-preview-frame.js28
-rw-r--r--ext/js/pages/settings/settings-display-controller.js19
-rw-r--r--ext/js/pages/settings/settings-main.js6
-rw-r--r--ext/js/pages/welcome-main.js6
8 files changed, 92 insertions, 29 deletions
diff --git a/ext/js/pages/action-popup-main.js b/ext/js/pages/action-popup-main.js
index cdf8f328..ce29bcf1 100644
--- a/ext/js/pages/action-popup-main.js
+++ b/ext/js/pages/action-popup-main.js
@@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+import {ThemeController} from '../app/theme-controller.js';
import {Application} from '../application.js';
import {getAllPermissions, hasRequiredPermissionsForOptions} from '../data/permissions-util.js';
import {querySelectorNotNull} from '../dom/query-selector.js';
@@ -30,10 +31,15 @@ class DisplayController {
this._api = api;
/** @type {?import('settings').Options} */
this._optionsFull = null;
+ /** @type {ThemeController} */
+ this._themeController = new ThemeController(document.documentElement);
}
/** */
async prepare() {
+ this._themeController.siteTheme = 'light';
+ this._themeController.prepare();
+
const manifest = chrome.runtime.getManifest();
this._showExtensionInfo(manifest);
@@ -201,6 +207,9 @@ class DisplayController {
}
void this._updateDictionariesEnabledWarnings(options);
void this._updatePermissionsWarnings(options);
+
+ this._themeController.theme = options.general.popupTheme;
+ this._themeController.updateTheme();
}
/** */
diff --git a/ext/js/pages/info-main.js b/ext/js/pages/info-main.js
index c0e37261..f431239f 100644
--- a/ext/js/pages/info-main.js
+++ b/ext/js/pages/info-main.js
@@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+import {ThemeController} from '../app/theme-controller.js';
import {Application} from '../application.js';
import {promiseTimeout} from '../core/utilities.js';
import {DocumentFocusController} from '../dom/document-focus-controller.js';
@@ -154,6 +155,17 @@ await Application.main(true, async (application) => {
const settingsController = new SettingsController(application);
await settingsController.prepare();
+ /** @type {ThemeController} */
+ const themeController = new ThemeController(document.documentElement);
+ themeController.prepare();
+ const optionsFull = await application.api.optionsGetFull();
+ const {profiles, profileCurrent} = optionsFull;
+ const primaryProfile = (profileCurrent >= 0 && profileCurrent < profiles.length) ? profiles[profileCurrent] : null;
+ if (primaryProfile !== null) {
+ themeController.theme = primaryProfile.options.general.popupTheme;
+ themeController.updateTheme();
+ }
+
const backupController = new BackupController(settingsController, null);
await backupController.prepare();
diff --git a/ext/js/pages/permissions-main.js b/ext/js/pages/permissions-main.js
index fd92987d..d27f2d79 100644
--- a/ext/js/pages/permissions-main.js
+++ b/ext/js/pages/permissions-main.js
@@ -130,10 +130,10 @@ await Application.main(true, async (application) => {
const persistentStorageController = new PersistentStorageController(application);
void persistentStorageController.prepare();
+ const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
+ settingsDisplayController.prepare();
+
await promiseTimeout(100);
document.documentElement.dataset.loaded = 'true';
-
- const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
- settingsDisplayController.prepare();
});
diff --git a/ext/js/pages/quick-start-guide-main.js b/ext/js/pages/quick-start-guide-main.js
new file mode 100644
index 00000000..41644f51
--- /dev/null
+++ b/ext/js/pages/quick-start-guide-main.js
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 Yomitan 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/>.
+ */
+
+import {ThemeController} from '../app/theme-controller.js';
+import {Application} from '../application.js';
+import {SettingsController} from './settings/settings-controller.js';
+
+await Application.main(true, async (application) => {
+ const settingsController = new SettingsController(application);
+ await settingsController.prepare();
+ /** @type {ThemeController} */
+ const themeController = new ThemeController(document.documentElement);
+ themeController.prepare();
+ const optionsFull = await application.api.optionsGetFull();
+ const {profiles, profileCurrent} = optionsFull;
+ const primaryProfile = (profileCurrent >= 0 && profileCurrent < profiles.length) ? profiles[profileCurrent] : null;
+ if (primaryProfile !== null) {
+ themeController.theme = primaryProfile.options.general.popupTheme;
+ themeController.updateTheme();
+ }
+});
diff --git a/ext/js/pages/settings/popup-preview-frame.js b/ext/js/pages/settings/popup-preview-frame.js
index 37aca898..7abcfe2a 100644
--- a/ext/js/pages/settings/popup-preview-frame.js
+++ b/ext/js/pages/settings/popup-preview-frame.js
@@ -18,6 +18,7 @@
import * as wanakana from '../../../lib/wanakana.js';
import {Frontend} from '../../app/frontend.js';
+import {ThemeController} from '../../app/theme-controller.js';
import {createApiMap, invokeApiMapHandler} from '../../core/api-map.js';
import {querySelectorNotNull} from '../../dom/query-selector.js';
import {TextSourceRange} from '../../dom/text-source-range.js';
@@ -57,6 +58,8 @@ export class PopupPreviewFrame {
this._languageSummaries = [];
/** @type {boolean} */
this._wanakanaBound = false;
+ /** @type {ThemeController} */
+ this._themeController = new ThemeController(document.documentElement);
/* eslint-disable @stylistic/no-multi-spaces */
/** @type {import('popup-preview-frame').ApiMap} */
@@ -74,10 +77,10 @@ export class PopupPreviewFrame {
async prepare() {
window.addEventListener('message', this._onMessage.bind(this), false);
+ this._themeController.siteTheme = 'light';
+ this._themeController.prepare();
+
// Setup events
- /** @type {HTMLInputElement} */
- const darkThemeCheckbox = querySelectorNotNull(document, '#theme-dark-checkbox');
- darkThemeCheckbox.addEventListener('change', this._onThemeDarkCheckboxChanged.bind(this), false);
this._exampleText.addEventListener('click', this._onExampleTextClick.bind(this), false);
this._exampleTextInput.addEventListener('blur', this._onExampleTextInputBlur.bind(this), false);
this._exampleTextInput.addEventListener('input', this._onExampleTextInputInput.bind(this), false);
@@ -137,6 +140,8 @@ export class PopupPreviewFrame {
options.general.popupHorizontalTextPosition = 'below';
options.general.popupVerticalTextPosition = 'before';
options.scanning.selectText = false;
+ this._themeController.theme = options.general.popupTheme;
+ this._themeController.updateTheme();
return options;
}
@@ -165,23 +170,6 @@ export class PopupPreviewFrame {
invokeApiMapHandler(this._windowMessageHandlers, action, params, [], callback);
}
- /**
- * @param {Event} e
- */
- _onThemeDarkCheckboxChanged(e) {
- const element = /** @type {HTMLInputElement} */ (e.currentTarget);
- document.documentElement.classList.toggle('dark', element.checked);
- if (this._themeChangeTimeout !== null) {
- clearTimeout(this._themeChangeTimeout);
- }
- this._themeChangeTimeout = setTimeout(() => {
- this._themeChangeTimeout = null;
- const popup = /** @type {Frontend} */ (this._frontend).popup;
- if (popup === null) { return; }
- void popup.updateTheme();
- }, 300);
- }
-
/** */
_onExampleTextClick() {
if (this._exampleTextInput === null) { return; }
diff --git a/ext/js/pages/settings/settings-display-controller.js b/ext/js/pages/settings/settings-display-controller.js
index f732f1ef..49f7192c 100644
--- a/ext/js/pages/settings/settings-display-controller.js
+++ b/ext/js/pages/settings/settings-display-controller.js
@@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+import {ThemeController} from '../../app/theme-controller.js';
import {isInputElementFocused} from '../../dom/document-util.js';
import {PopupMenu} from '../../dom/popup-menu.js';
import {querySelectorNotNull} from '../../dom/query-selector.js';
@@ -39,10 +40,16 @@ export class SettingsDisplayController {
this._onMoreToggleClickBind = this._onMoreToggleClick.bind(this);
/** @type {(event: MouseEvent) => void} */
this._onMenuButtonClickBind = this._onMenuButtonClick.bind(this);
+ /** @type {ThemeController} */
+ this._themeController = new ThemeController(document.documentElement);
}
/** */
prepare() {
+ this._themeController.siteTheme = 'light';
+ this._themeController.prepare();
+ void this._updateTheme();
+
const onFabButtonClick = this._onFabButtonClick.bind(this);
for (const fabButton of /** @type {NodeListOf<HTMLElement>} */ (document.querySelectorAll('.fab-button'))) {
fabButton.addEventListener('click', onFabButtonClick, false);
@@ -84,6 +91,18 @@ export class SettingsDisplayController {
window.addEventListener('keydown', this._onKeyDown.bind(this), false);
window.addEventListener('popstate', this._onPopState.bind(this), false);
this._updateScrollTarget();
+
+ const themeDropdown = document.querySelector('[data-setting="general.popupTheme"]');
+ if (themeDropdown) {
+ themeDropdown.addEventListener('change', this._updateTheme.bind(this), false);
+ }
+ }
+
+ /** */
+ async _updateTheme() {
+ const options = await this._settingsController.getOptions();
+ this._themeController.theme = options.general.popupTheme;
+ this._themeController.updateTheme();
}
// Private
diff --git a/ext/js/pages/settings/settings-main.js b/ext/js/pages/settings/settings-main.js
index 3ff818ac..a315eac4 100644
--- a/ext/js/pages/settings/settings-main.js
+++ b/ext/js/pages/settings/settings-main.js
@@ -169,10 +169,10 @@ await Application.main(true, async (application) => {
const sortFrequencyDictionaryController = new SortFrequencyDictionaryController(settingsController);
preparePromises.push(sortFrequencyDictionaryController.prepare());
+ const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
+ settingsDisplayController.prepare();
+
await Promise.all(preparePromises);
document.documentElement.dataset.loaded = 'true';
-
- const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
- settingsDisplayController.prepare();
});
diff --git a/ext/js/pages/welcome-main.js b/ext/js/pages/welcome-main.js
index 10a84a59..56a7e7d8 100644
--- a/ext/js/pages/welcome-main.js
+++ b/ext/js/pages/welcome-main.js
@@ -92,10 +92,10 @@ await Application.main(true, async (application) => {
const languagesController = new LanguagesController(settingsController);
preparePromises.push(languagesController.prepare());
+ const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
+ settingsDisplayController.prepare();
+
await Promise.all(preparePromises);
document.documentElement.dataset.loaded = 'true';
-
- const settingsDisplayController = new SettingsDisplayController(settingsController, modalController);
- settingsDisplayController.prepare();
});