Files
webawesome/docs/assets/scripts/theme-picker.js
2025-04-14 12:52:00 -04:00

112 lines
2.8 KiB
JavaScript

import { domChange } from './util/dom-change.js';
export { domChange };
export function nextFrame() {
return new Promise(resolve => requestAnimationFrame(resolve));
}
export class ThemeAspect {
constructor(options) {
Object.assign(this, options);
this.set();
// Update when local storage changes.
// That way changes in one window will propagate to others (including iframes).
window.addEventListener('storage', event => {
if (event.key === this.key) {
this.set();
}
});
// Listen for selections
document.addEventListener('change', event => {
const picker = event.target.closest(this.picker);
if (picker) {
this.set(picker.value);
}
});
['turbo:before-render', 'turbo:before-stream-render', 'turbo:before-frame-render'].forEach(eventName => {
document.addEventListener(eventName, e => {
const newElement = e.detail.newBody || e.detail.newFrame || e.detail.newStream;
if (newElement) {
this.syncUI(newElement);
}
});
});
}
get() {
return localStorage.getItem(this.key) ?? this.defaultValue;
}
computed = {};
get computedValue() {
if (this.value in this.computed) {
return this.computed[this.value];
}
return this.value;
}
set(value = this.get()) {
if (value === this.value) {
return;
}
this.value = value;
if (this.value === this.defaultValue) {
localStorage.removeItem(this.key);
} else {
localStorage.setItem(this.key, this.value);
}
this.applyChange();
this.syncUI();
}
syncUI(container = document) {
for (let picker of container.querySelectorAll(this.picker)) {
picker.setAttribute('value', this.value);
picker.value = this.value;
}
}
}
const colorScheme = new ThemeAspect({
defaultValue: 'auto',
key: 'colorScheme',
picker: 'wa-select.color-scheme-selector',
computed: {
get auto() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
},
},
applyChange() {
// Toggle the dark mode class
domChange(() => {
let dark = this.computedValue === 'dark';
document.documentElement.classList.toggle(`wa-dark`, dark);
document.documentElement.dispatchEvent(new CustomEvent('wa-color-scheme-change', { detail: { dark } }));
});
},
});
// Update the color scheme when the preference changes
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => colorScheme.set());
// Toggle color scheme with backslash
document.addEventListener('keydown', event => {
if (
event.key === '\\' &&
!event.composedPath().some(el => ['input', 'textarea'].includes(el?.tagName?.toLowerCase()))
) {
event.preventDefault();
colorScheme.set(theming.colorScheme.resolvedValue === 'dark' ? 'light' : 'dark');
}
});