feat(timeline): add pulse animation for in-flight drag mutations
Allocation bars that have active optimistic overrides (post-drag, awaiting server confirmation) now pulse subtly via animate-pulse. The pending set is derived from the existing optimisticAllocations map keys, requiring no additional state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useCallback, useEffect } from "react";
|
||||
import { useLocalStorage } from "./useLocalStorage.js";
|
||||
|
||||
export type ThemeMode = "light" | "dark";
|
||||
export type AccentColor = "sky" | "indigo" | "violet" | "emerald" | "rose" | "amber";
|
||||
@@ -13,17 +14,6 @@ export interface ThemePreferences {
|
||||
const STORAGE_KEY = "capakraken_theme";
|
||||
const DEFAULT: ThemePreferences = { mode: "light", accent: "sky" };
|
||||
|
||||
function readStorage(): ThemePreferences {
|
||||
if (typeof window === "undefined") return DEFAULT;
|
||||
try {
|
||||
const raw = localStorage.getItem(STORAGE_KEY);
|
||||
if (!raw) return DEFAULT;
|
||||
return { ...DEFAULT, ...(JSON.parse(raw) as Partial<ThemePreferences>) };
|
||||
} catch {
|
||||
return DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
function applyTheme(prefs: ThemePreferences) {
|
||||
const html = document.documentElement;
|
||||
if (prefs.mode === "dark") html.classList.add("dark");
|
||||
@@ -32,32 +22,20 @@ function applyTheme(prefs: ThemePreferences) {
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const [prefs, setPrefs] = useState<ThemePreferences>(DEFAULT);
|
||||
const [prefs, setPrefs] = useLocalStorage<ThemePreferences>(STORAGE_KEY, DEFAULT);
|
||||
|
||||
// Read from storage on mount
|
||||
// Apply theme to DOM whenever prefs change
|
||||
useEffect(() => {
|
||||
const stored = readStorage();
|
||||
setPrefs(stored);
|
||||
applyTheme(stored);
|
||||
}, []);
|
||||
applyTheme(prefs);
|
||||
}, [prefs]);
|
||||
|
||||
const setMode = useCallback((mode: ThemeMode) => {
|
||||
setPrefs((prev) => {
|
||||
const next = { ...prev, mode };
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
applyTheme(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
setPrefs((prev) => ({ ...prev, mode }));
|
||||
}, [setPrefs]);
|
||||
|
||||
const setAccent = useCallback((accent: AccentColor) => {
|
||||
setPrefs((prev) => {
|
||||
const next = { ...prev, accent };
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(next));
|
||||
applyTheme(next);
|
||||
return next;
|
||||
});
|
||||
}, []);
|
||||
setPrefs((prev) => ({ ...prev, accent }));
|
||||
}, [setPrefs]);
|
||||
|
||||
return { prefs, setMode, setAccent };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user