color-q/src/stores/schemes.ts
2024-12-27 06:46:18 +08:00

124 lines
3.3 KiB
TypeScript

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<SchemeSet[]>('schemes', []);
export const activeSchemeAtom = atomWithStorage<string | null>('activeScheme', null);
export function useSchemeList(): Pick<SchemeSet, 'id' | 'name' | 'createdAt'>[] {
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;
}