wip(js): add head.html and assets/js
This commit is contained in:
138
assets/js/modules/theme.js
Normal file
138
assets/js/modules/theme.js
Normal file
@ -0,0 +1,138 @@
|
||||
/**
|
||||
* Theme management class
|
||||
*
|
||||
* To reduce flickering during page load, this script should be loaded synchronously.
|
||||
*/
|
||||
class Theme {
|
||||
static #modeKey = 'mode';
|
||||
static #modeAttr = 'data-mode';
|
||||
static #darkMedia = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
static switchable = !document.documentElement.hasAttribute(this.#modeAttr);
|
||||
|
||||
static get DARK() {
|
||||
return 'dark';
|
||||
}
|
||||
|
||||
static get LIGHT() {
|
||||
return 'light';
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string} Theme mode identifier
|
||||
*/
|
||||
static get ID() {
|
||||
return 'theme-mode';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current visual state of the theme.
|
||||
*
|
||||
* @returns {string} The current visual state, either the mode if it exists,
|
||||
* or the system dark mode state ('dark' or 'light').
|
||||
*/
|
||||
static get visualState() {
|
||||
if (this.#hasMode) {
|
||||
return this.#mode;
|
||||
} else {
|
||||
return this.#sysDark ? this.DARK : this.LIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
static get #mode() {
|
||||
return (
|
||||
sessionStorage.getItem(this.#modeKey) ||
|
||||
document.documentElement.getAttribute(this.#modeAttr)
|
||||
);
|
||||
}
|
||||
|
||||
static get #isDarkMode() {
|
||||
return this.#mode === this.DARK;
|
||||
}
|
||||
|
||||
static get #hasMode() {
|
||||
return this.#mode !== null;
|
||||
}
|
||||
|
||||
static get #sysDark() {
|
||||
return this.#darkMedia.matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps theme modes to provided values
|
||||
* @param {string} light Value for light mode
|
||||
* @param {string} dark Value for dark mode
|
||||
* @returns {Object} Mapped values
|
||||
*/
|
||||
static getThemeMapper(light, dark) {
|
||||
return {
|
||||
[this.LIGHT]: light,
|
||||
[this.DARK]: dark
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the theme based on system preferences or stored mode
|
||||
*/
|
||||
static init() {
|
||||
if (!this.switchable) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.#darkMedia.addEventListener('change', () => {
|
||||
const lastMode = this.#mode;
|
||||
this.#clearMode();
|
||||
|
||||
if (lastMode !== this.visualState) {
|
||||
this.#notify();
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.#hasMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.#isDarkMode) {
|
||||
this.#setDark();
|
||||
} else {
|
||||
this.#setLight();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the current theme mode
|
||||
*/
|
||||
static flip() {
|
||||
if (this.#hasMode) {
|
||||
this.#clearMode();
|
||||
} else {
|
||||
this.#sysDark ? this.#setLight() : this.#setDark();
|
||||
}
|
||||
this.#notify();
|
||||
}
|
||||
|
||||
static #setDark() {
|
||||
document.documentElement.setAttribute(this.#modeAttr, this.DARK);
|
||||
sessionStorage.setItem(this.#modeKey, this.DARK);
|
||||
}
|
||||
|
||||
static #setLight() {
|
||||
document.documentElement.setAttribute(this.#modeAttr, this.LIGHT);
|
||||
sessionStorage.setItem(this.#modeKey, this.LIGHT);
|
||||
}
|
||||
|
||||
static #clearMode() {
|
||||
document.documentElement.removeAttribute(this.#modeAttr);
|
||||
sessionStorage.removeItem(this.#modeKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies other plugins that the theme mode has changed
|
||||
*/
|
||||
static #notify() {
|
||||
window.postMessage({ id: this.ID }, '*');
|
||||
}
|
||||
}
|
||||
|
||||
Theme.init();
|
||||
|
||||
export default Theme;
|
Reference in New Issue
Block a user