46 lines
2.3 KiB
TypeScript
46 lines
2.3 KiB
TypeScript
// Per-device UI preferences (theme, motion, language, onboarding seen). Only conveniences live in localStorage;
|
|
// nothing about projects or accounts.
|
|
export type Theme = 'system' | 'light' | 'dark';
|
|
export type Motion = 'system' | 'reduce';
|
|
export type Lang = 'system' | 'en' | 'tr';
|
|
/** Tables: comfortable rows, or compact (smaller padding) for wide data on small windows. */
|
|
export type Density = 'comfortable' | 'compact';
|
|
export interface Prefs { theme: Theme; motion: Motion; lang: Lang; onboarded: boolean; density?: Density }
|
|
|
|
export const DEFAULT_PREFS: Prefs = { theme: 'system', motion: 'system', lang: 'system', onboarded: false };
|
|
const KEY = 'storebound.prefs.v1';
|
|
|
|
export function parsePrefs(raw: string | null): Prefs {
|
|
try {
|
|
const v = JSON.parse(raw ?? '{}') as Partial<Prefs>;
|
|
return {
|
|
theme: v.theme === 'light' || v.theme === 'dark' ? v.theme : 'system',
|
|
motion: v.motion === 'reduce' ? 'reduce' : 'system',
|
|
lang: v.lang === 'en' || v.lang === 'tr' ? v.lang : 'system',
|
|
onboarded: v.onboarded === true,
|
|
...(v.density === 'compact' ? { density: 'compact' as const } : {}),
|
|
};
|
|
} catch { return { ...DEFAULT_PREFS }; }
|
|
}
|
|
|
|
export function loadPrefs(): Prefs {
|
|
try { return parsePrefs(localStorage.getItem(KEY)); } catch { return { ...DEFAULT_PREFS }; }
|
|
}
|
|
|
|
export function savePrefs(p: Prefs): void {
|
|
try { localStorage.setItem(KEY, JSON.stringify(p)); } catch { /* private mode: keep it for this run only */ }
|
|
}
|
|
|
|
/** Puts the preferences on <html>: data-theme (light / dark, absent = follow the system), data-motion and lang. */
|
|
export function applyPrefs(p: Prefs, root: { dataset: DOMStringMap; lang?: string } = document.documentElement, systemLang = 'en'): void {
|
|
if (p.theme === 'system') delete root.dataset.theme; else root.dataset.theme = p.theme;
|
|
if (p.motion === 'reduce') root.dataset.motion = 'reduce'; else delete root.dataset.motion;
|
|
if (p.density === 'compact') root.dataset.density = 'compact'; else delete root.dataset.density;
|
|
root.lang = resolveLang(p.lang, systemLang);
|
|
}
|
|
|
|
/** The language to show: the preference, else the system's when we have it (tr), else English. */
|
|
export function resolveLang(l: Lang, systemLang: string): 'en' | 'tr' {
|
|
if (l !== 'system') return l;
|
|
return /^tr\b/i.test(systemLang) ? 'tr' : 'en';
|
|
}
|