Files
webawesome/docs/assets/scripts/theme-picker.js
Cory LaViska e1bf5471bf Use native events when possible (#590)
* fix filename

* <wa-input> input + change (change not working)

* compose that horribly misleading change event

* use native blur/focus events

* update checkbox

* update color picker events

* update color picker events

* update radio group events

* update button events

* remove event options

* update select events

* update image comparer events

* update icon button events

* update slider events

* update rating events

* update radio events

* update switch event

* update textarea events

* update radio button events

* remove unused events

* cleanup

* fix react wrappers

* fix react events for real this time

* update changelog

* add note

* Update src/components/input/input.ts

Co-authored-by: Lea Verou <lea@verou.me>

* delete unused close event

* use same event type

* fix tests

* remove wa- from events in docs and examples

* fix comment

* Update docs/docs/resources/changelog.md

Co-authored-by: Lea Verou <lea@verou.me>

---------

Co-authored-by: Lea Verou <lea@verou.me>
2025-01-23 17:52:41 -05:00

120 lines
3.0 KiB
JavaScript

// Helper for view transitions
export function domChange(fn, { behavior = 'smooth' } = {}) {
const canUseViewTransitions =
document.startViewTransition && !window.matchMedia('(prefers-reduced-motion: reduce)').matches;
if (canUseViewTransitions && behavior === 'smooth') {
document.startViewTransition(fn);
} else {
fn(true);
}
}
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);
});
},
});
// 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');
}
});