@discoveryjs/discovery
Version:
Frontend framework for rapid data (JSON) analysis, shareable serverless reports and dashboards
180 lines (179 loc) • 5.15 kB
JavaScript
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})`);
}
}