import dayjs from 'dayjs'; import { useAtomValue, useSetAtom } from 'jotai'; import { atomWithStorage } from 'jotai/utils'; import { isEqual, isNil, reduce } from 'lodash-es'; import { useCallback, useMemo } from 'react'; import { v4 } from 'uuid'; import { SchemeContent, SchemeStorage, SchemeType } from '../models'; 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< SchemeContent, 'id' | 'name' | 'createdAt' | 'type' >[] { const schemes = useAtomValue(schemesAtom); const sortedSchemes = useMemo( () => schemes .filter((item) => !isNil(item)) .sort((a, b) => dayjs(b.createdAt).diff(dayjs(a.createdAt))) .map(({ id, name, createdAt, type }) => ({ id, name, createdAt, type })), [schemes], ); return sortedSchemes; } export function useScheme(id?: string | null): SchemeContent | null { const schemes = useAtomValue(schemesAtom); const scheme = useMemo( () => schemes.find((s) => !isNil(id) && isEqual(id, s.id)) ?? null, [schemes, id], ); return scheme; } export function useActiveScheme(): SchemeContent | null { const activeSchemeId = useAtomValue(activeSchemeAtom); const activeScheme = useScheme(activeSchemeId ?? 'UNEXISTS'); return activeScheme; } export function useCreateScheme(): ( name: string, type: SchemeType, description?: string | null, ) => string { const updateSchemes = useSetAtom(schemesAtom); const createSchemeAction = useCallback( (name: string, type: SchemeType, description?: string | null) => { const newId = v4(); updateSchemes((prev) => [ ...prev.filter((s) => !isNil(s)), { id: newId, name, createdAt: dayjs().toISOString(), description: description ?? null, type, schemeStorage: {}, }, ]); return newId; }, [updateSchemes], ); return createSchemeAction; } export function useUpdateScheme( id?: string | null, ): (updater: (prev: SchemeContent) => SchemeContent) => void { const updateSchemes = useSetAtom(schemesAtom); const updateAction = useCallback( (updater: (prev: SchemeContent) => SchemeContent) => { updateSchemes((prev) => reduce( prev, (acc, scheme) => { if (!isNil(scheme)) { if (!isNil(id) && isEqual(id, scheme.id)) { acc.push(updater(scheme)); } else { acc.push(scheme); } } return acc; }, [] as SchemeContent[], ), ); }, [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; }