color-q/src/stores/schemes.ts
2025-02-10 14:28:34 +08:00

140 lines
3.8 KiB
TypeScript

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<SchemeContent<SchemeStorage>[]>('schemes', []);
export const activeSchemeAtom = atomWithStorage<string | null>('activeScheme', null);
export function useSchemeList(): Pick<
SchemeContent<SchemeStorage>,
'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<SchemeStorage> | 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<SchemeStorage> | 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<SchemeStorage>) => SchemeContent<SchemeStorage>) => void {
const updateSchemes = useSetAtom(schemesAtom);
const updateAction = useCallback(
(updater: (prev: SchemeContent<SchemeStorage>) => SchemeContent<SchemeStorage>) => {
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<SchemeStorage>[],
),
);
},
[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;
}