@dvcol/neo-svelte
Version:
Neomorphic ui library for svelte 5
127 lines (126 loc) • 4.79 kB
JavaScript
import { wait } from '@dvcol/common-utils/common/promise';
import { getContext, setContext, untrack } from 'svelte';
import { getRemember, getReset, getSource, getTheme, NeoThemeRoot, NeoThemeStorageKey } from './neo-theme-provider.model.js';
import { NeoErrorThemeContextNotFound, NeoErrorThemeInvalidTarget, NeoErrorThemeTargetNotFound } from '../utils/error.utils.js';
export class NeoThemeProviderContext {
#reset = $state(getReset());
#theme = $state(getTheme());
#source = $state(getSource());
#remember = $state(getRemember());
#root = $state(document?.documentElement);
get reset() {
return this.#reset;
}
get theme() {
return this.#theme;
}
get source() {
return this.#source;
}
get remember() {
return this.#remember;
}
get root() {
return typeof this.#root === 'function' ? this.#root() : this.#root;
}
get state() {
return {
reset: this.reset,
theme: this.theme,
source: this.source,
remember: this.remember,
root: this.root,
};
}
constructor({ reset, theme, source, remember, root }) {
this.#reset = reset ?? this.reset;
this.#theme = theme ?? this.theme;
this.#source = source ?? this.source;
this.#remember = remember ?? this.remember;
this.#root = root ?? this.root;
this.sync();
}
update(partial) {
untrack(() => {
if (partial.reset !== undefined)
this.#reset = partial.reset;
if (partial.theme !== undefined)
this.#theme = partial.theme;
if (partial.source !== undefined)
this.#source = partial.source;
if (partial.remember !== undefined)
this.#remember = partial.remember;
if (partial.root !== undefined)
this.#root = partial.root;
this.sync();
});
}
async setTheme(theme) {
if (!this.root)
throw new NeoErrorThemeTargetNotFound();
if (!('setAttribute' in this.root))
throw new NeoErrorThemeInvalidTarget();
if (this.theme === this.root.getAttribute(NeoThemeStorageKey.Theme))
return;
this.root.setAttribute(NeoThemeStorageKey.Transition, 'false');
this.root.setAttribute(NeoThemeStorageKey.Theme, theme);
await wait();
this.root.removeAttribute(NeoThemeStorageKey.Transition);
}
setSource(source) {
if (!this.root)
throw new NeoErrorThemeTargetNotFound();
if (!('setAttribute' in this.root))
throw new NeoErrorThemeInvalidTarget();
if (this.source === this.root.getAttribute(NeoThemeStorageKey.Source))
return;
this.root.setAttribute(NeoThemeStorageKey.Source, source);
}
sync() {
if (!this.root)
throw new NeoErrorThemeTargetNotFound();
if (!('setAttribute' in this.root))
throw new NeoErrorThemeInvalidTarget();
this.root.setAttribute(NeoThemeRoot, '');
void this.setTheme(this.theme);
this.setSource(this.source);
if (this.reset)
this.root.setAttribute(NeoThemeStorageKey.Reset, '');
else
this.root.removeAttribute(NeoThemeStorageKey.Reset);
if (!localStorage)
return;
localStorage.setItem(NeoThemeStorageKey.Remember, Boolean(this.remember).toString());
if (this.remember) {
localStorage.setItem(NeoThemeStorageKey.Reset, Boolean(this.reset).toString());
localStorage.setItem(NeoThemeStorageKey.Theme, this.theme);
localStorage.setItem(NeoThemeStorageKey.Source, this.source);
}
else {
localStorage.removeItem(NeoThemeStorageKey.Reset);
localStorage.removeItem(NeoThemeStorageKey.Theme);
localStorage.removeItem(NeoThemeStorageKey.Source);
}
}
destroy() {
if (!this.root)
return;
if (!('removeAttribute' in this.root))
return;
this.root.removeAttribute(NeoThemeRoot);
this.root.removeAttribute(NeoThemeStorageKey.Reset);
this.root.removeAttribute(NeoThemeStorageKey.Theme);
this.root.removeAttribute(NeoThemeStorageKey.Source);
}
}
const NeoContextKey = Symbol('NeoThemeProviderContext');
export function setNeoThemeContext(context) {
return setContext(NeoContextKey, new NeoThemeProviderContext(context));
}
export const getNeoThemeContext = () => getContext(NeoContextKey);
export function useNeoThemeContext() {
const context = getNeoThemeContext();
if (!context)
throw new NeoErrorThemeContextNotFound();
return context;
}