Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
102 lines
2.6 KiB
TypeScript
102 lines
2.6 KiB
TypeScript
import {
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
useMemo,
|
|
useState,
|
|
type ReactNode,
|
|
} from "react";
|
|
import { useReducedMotion } from "motion/react";
|
|
import { STRINGS, type Lang } from "./i18n";
|
|
|
|
export type ViewMode = "internal" | "public";
|
|
export type Window = "24h" | "7d";
|
|
export type Theme = "dark" | "light";
|
|
|
|
interface UIState {
|
|
lang: Lang;
|
|
dir: "ltr" | "rtl";
|
|
mode: ViewMode;
|
|
window: Window;
|
|
theme: Theme;
|
|
reduced: boolean;
|
|
t: (typeof STRINGS)["en"];
|
|
setLang: (l: Lang) => void;
|
|
setMode: (m: ViewMode) => void;
|
|
setWindow: (w: Window) => void;
|
|
setTheme: (t: Theme) => void;
|
|
}
|
|
|
|
const UIContext = createContext<UIState | null>(null);
|
|
|
|
export function UIProvider({ children }: { children: ReactNode }) {
|
|
const [lang, setLang] = useState<Lang>(
|
|
() => (localStorage.getItem("dash.lang") as Lang) || "en",
|
|
);
|
|
const [mode, setMode] = useState<ViewMode>(
|
|
() => (localStorage.getItem("dash.mode") as ViewMode) || "internal",
|
|
);
|
|
const [window, setWindow] = useState<Window>("24h");
|
|
const [theme, setTheme] = useState<Theme>(
|
|
() => (localStorage.getItem("dash.theme") as Theme) || "dark",
|
|
);
|
|
const reduced = useReducedMotion() ?? false;
|
|
|
|
const dir = lang === "fa" ? "rtl" : "ltr";
|
|
|
|
useEffect(() => {
|
|
document.documentElement.dir = dir;
|
|
document.documentElement.lang = lang;
|
|
localStorage.setItem("dash.lang", lang);
|
|
}, [lang, dir]);
|
|
|
|
useEffect(() => {
|
|
document.documentElement.dataset.theme = theme;
|
|
localStorage.setItem("dash.theme", theme);
|
|
}, [theme]);
|
|
|
|
useEffect(() => {
|
|
localStorage.setItem("dash.mode", mode);
|
|
}, [mode]);
|
|
|
|
const value = useMemo<UIState>(
|
|
() => ({
|
|
lang,
|
|
dir,
|
|
mode,
|
|
window,
|
|
theme,
|
|
reduced,
|
|
t: STRINGS[lang],
|
|
setLang,
|
|
setMode,
|
|
setWindow,
|
|
setTheme,
|
|
}),
|
|
[lang, dir, mode, window, theme, reduced],
|
|
);
|
|
|
|
return <UIContext.Provider value={value}>{children}</UIContext.Provider>;
|
|
}
|
|
|
|
export function useUI(): UIState {
|
|
const ctx = useContext(UIContext);
|
|
if (!ctx) throw new Error("useUI outside UIProvider");
|
|
return ctx;
|
|
}
|
|
|
|
/** Spring presets — physical, interruptible; inert under reduced motion. */
|
|
export const springs = {
|
|
arc: { type: "spring", stiffness: 48, damping: 16, mass: 1.2 } as const,
|
|
snappy: { type: "spring", stiffness: 360, damping: 28 } as const,
|
|
gentle: { type: "spring", stiffness: 120, damping: 22 } as const,
|
|
layout: { type: "spring", stiffness: 230, damping: 26 } as const,
|
|
};
|
|
|
|
export const STATE_COLOR: Record<string, string> = {
|
|
up: "var(--color-up)",
|
|
degraded: "var(--color-warn)",
|
|
down: "var(--color-down)",
|
|
unknown: "var(--color-idle)",
|
|
};
|