UNPKG

@discoveryjs/discovery

Version:

Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards

180 lines (179 loc) 5.15 kB
import { getLocalStorageEntry, getLocalStorageValue } from "./utils/persistent.js"; export const persistentKey = "discoveryjs:color-scheme"; export const colorSchemeSerializedValues = /* @__PURE__ */ Object.freeze([ "auto", "light", "dark" ]); export const colorSchemeStateValues = /* @__PURE__ */ Object.freeze([ "auto", "light", "dark", "light-only", "dark-only" ]); function isSerializedValue(value) { return colorSchemeSerializedValues.includes(value); } function isColorSchemeDark() { return matchMedia("(prefers-color-scheme:dark)"); } function resolveState(value, persistent) { const inputValue = value; switch (value) { case true: value = "dark"; break; case false: value = "light"; break; case "disabled": case "disable": case "off": value = "light-only"; break; case "only": value = "dark-only"; break; } if (inputValue !== value) { console.warn(`Used legacy value "${inputValue}" for ColorSheme, value replaced for "${value}"`); } if (value !== "light-only" && value !== "dark-only" && persistent) { const persistentValue = getLocalStorageValue(persistentKey); if (isSerializedValue(persistentValue)) { value = persistentValue; } } return value; } function resolveMode(value) { switch (value) { case "light": case "dark": return "manual"; case "light-only": case "dark-only": return "only"; case "auto": return "auto"; default: const check = value; return check; } } function resolveValue(state) { const serializedState = serializeColorSchemeState(state); if (serializedState === "auto") { return isColorSchemeDark().matches ? "dark" : "light"; } return serializedState; } export function serializeColorSchemeState(state) { switch (state) { case "auto": return "auto"; case "dark": case "dark-only": return "dark"; case "light": case "light-only": default: return "light"; } } export function resolveColorSchemeValue(value = "auto", persistent = false) { return resolveValue(resolveState(value, persistent)); } export class ColorScheme { #persistent; #persistentUnsubscribe; #mediaQueryUnsubscribe; #handlers; #state; #value; persistent; state; value; serializedValue; mode; constructor(value = "auto", persistent = false) { this.#persistent = persistent ? getLocalStorageEntry(persistentKey) : null; this.#handlers = []; this.#state = resolveState(value, persistent); this.#value = resolveValue(this.#state); Object.defineProperties(this, { persistent: { get: () => this.#persistent !== null }, state: { get: () => this.#state }, value: { get: () => this.#value }, serializedValue: { get: () => serializeColorSchemeState(this.#state) }, mode: { get: () => resolveMode(this.#state) } }); this.#persistentUnsubscribe = this.#persistent?.subscribe((value2) => { if (isSerializedValue(value2) && value2 !== this.serializedValue && this.mode !== "only") { this.set(value2); } }); this.#mediaQueryUnsubscribe = () => { const mediaQuery = isColorSchemeDark(); const mediaQueryListener = () => { if (this.state === "auto") { this.set("auto"); } }; mediaQuery.addEventListener("change", mediaQueryListener); return () => mediaQuery.removeEventListener("change", mediaQueryListener); }; } subscribe(fn, fire = false) { let entry = { fn }; this.#handlers.push(entry); if (fire) { entry.fn(this.#value, this.#state); } return () => { const index = entry !== null ? this.#handlers.indexOf(entry) : -1; entry = null; if (index !== -1) { this.#handlers.splice(index, 1); } }; } destroy() { this.#persistentUnsubscribe?.(); this.#persistentUnsubscribe = void 0; this.#mediaQueryUnsubscribe?.(); this.#mediaQueryUnsubscribe = void 0; } set(state) { const prevSerializedValue = this.serializedValue; const prevValue = this.#value; const prevState = this.#state; if (!colorSchemeStateValues.includes(state)) { console.warn(`Bad value "${state}" for ColorScheme#state, value ignored`); return; } this.#state = state; this.#value = resolveValue(state); if (this.serializedValue !== prevSerializedValue) { this.#persistent?.set(this.serializedValue); } if (this.#state !== prevState || this.#value !== prevValue) { this.#handlers.forEach(({ fn }) => fn(this.#value, this.#state)); } } toggle(useAutoForManual = false) { switch (this.#state) { case "auto": this.set(isColorSchemeDark().matches ? "dark" : "light"); return; case "dark": this.set(useAutoForManual && !isColorSchemeDark().matches ? "auto" : "light"); return; case "light": this.set(useAutoForManual && isColorSchemeDark().matches ? "auto" : "dark"); return; } console.warn(`ColorScheme is locked for changes (mode=${this.#state})`); } }