Import source: web/src/lib/ui

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:20:23 +03:30
parent 31cab9b24b
commit d5cf428c89
+101
View File
@@ -0,0 +1,101 @@
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)",
};