import dayjs from 'dayjs'; import { useAtomValue, useSetAtom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; import { isEqual, reduce } from 'lodash-es'; import { useCallback, useMemo } from 'react'; import { v4 } from 'uuid'; type ColorSet = { normal?: string | null; hover?: string | null; active?: string | null; focus?: string | null; disabled?: string | null; lighten?: string | null; darken?: string | null; }; type Scheme = { primary?: ColorSet | null; secondary?: ColorSet | ColorSet[] | null; accent?: ColorSet | null; neutral?: ColorSet | null; foreground?: ColorSet | null; background?: ColorSet | null; danger?: ColorSet | null; warning?: ColorSet | null; success?: ColorSet | null; info?: ColorSet | null; border?: ColorSet | null; }; export type SchemeSet = { id: string; name: string; createdAt: string; description: string | null; lightScheme: Scheme; darkScheme: Scheme; }; const schemesAtom = atomWithStorage('schemes', []); export const activeSchemeAtom = atomWithStorage('activeScheme', null); export function useSchemeList(): Pick[] { const schemes = useAtomValue(schemesAtom); const sortedSchemes = useMemo( () => schemes .sort((a, b) => dayjs(b.createdAt).diff(dayjs(a.createdAt))) .map(({ id, name, createdAt }) => ({ id, name, createdAt })), [schemes], ); return sortedSchemes; } export function useScheme(id: string): SchemeSet | null { const schemes = useAtomValue(schemesAtom); const scheme = useMemo(() => schemes.find((s) => isEqual(id, s.id)) ?? null, [schemes, id]); return scheme; } export function useActiveScheme(): SchemeSet | null { const activeSchemeId = useAtomValue(activeSchemeAtom); const activeScheme = useScheme(activeSchemeId ?? 'UNEXISTS'); return activeScheme; } export function useCreateScheme(): (name: string, description?: string) => string { const updateSchemes = useSetAtom(schemesAtom); const createSchemeAction = useCallback( (name: string, description?: string) => { const newId = v4(); updateSchemes((prev) => [ ...prev, { id: newId, name, createdAt: dayjs().toISOString(), description: description ?? null, lightScheme: {}, darkScheme: {}, }, ]); return newId; }, [updateSchemes], ); return createSchemeAction; } export function useUpdateScheme(id: string): (updater: (prev: SchemeSet) => SchemeSet) => void { const updateSchemes = useSetAtom(schemesAtom); const updateAction = useCallback( (updater: (prev: SchemeSet) => SchemeSet) => { updateSchemes((prev) => reduce( prev, (acc, scheme) => { if (isEqual(id, scheme.id)) { acc.push(updater(scheme)); } else { acc.push(scheme); } return acc; }, [] as SchemeSet[], ), ); }, [id], ); return updateAction; } export function useRemoveScheme(id: string): () => void { const updateSchemes = useSetAtom(schemesAtom); const removeAction = useCallback(() => { updateSchemes((prev) => prev.filter((s) => !isEqual(id, s.id))); }, [updateSchemes, id]); return removeAction; }