98 lines
2.6 KiB
TypeScript
98 lines
2.6 KiB
TypeScript
"use client";
|
|
import { createContext, useCallback, useContext, useMemo, useState } from "react";
|
|
|
|
type Ctx = {
|
|
ids: Set<number>;
|
|
has: (id: number) => boolean;
|
|
toggle: (id: number) => void;
|
|
selectMany: (ids: number[]) => void;
|
|
setMany: (ids: number[]) => void;
|
|
selectRangeTo: (id: number, orderedIds: number[]) => void;
|
|
clear: () => void;
|
|
lastClickedId: number | null;
|
|
};
|
|
|
|
const ActressSelectCtx = createContext<Ctx | null>(null);
|
|
|
|
export function ActressSelectionProvider({ children }: { children: React.ReactNode }) {
|
|
const [ids, setIds] = useState<Set<number>>(new Set());
|
|
const [lastClickedId, setLastClickedId] = useState<number | null>(null);
|
|
|
|
const toggle = useCallback((id: number) => {
|
|
setIds((cur) => {
|
|
const next = new Set(cur);
|
|
if (next.has(id)) next.delete(id); else next.add(id);
|
|
return next;
|
|
});
|
|
setLastClickedId(id);
|
|
}, []);
|
|
|
|
const selectMany = useCallback((newIds: number[]) => {
|
|
setIds((cur) => {
|
|
const next = new Set(cur);
|
|
newIds.forEach((i) => next.add(i));
|
|
return next;
|
|
});
|
|
}, []);
|
|
|
|
const setMany = useCallback((newIds: number[]) => {
|
|
setIds(new Set(newIds));
|
|
}, []);
|
|
|
|
const selectRangeTo = useCallback((id: number, orderedIds: number[]) => {
|
|
const last = lastClickedId;
|
|
if (last == null) {
|
|
setIds((cur) => {
|
|
const next = new Set(cur);
|
|
next.add(id);
|
|
return next;
|
|
});
|
|
setLastClickedId(id);
|
|
return;
|
|
}
|
|
const a = orderedIds.indexOf(last);
|
|
const b = orderedIds.indexOf(id);
|
|
if (a === -1 || b === -1) {
|
|
setIds((cur) => {
|
|
const next = new Set(cur);
|
|
next.add(id);
|
|
return next;
|
|
});
|
|
setLastClickedId(id);
|
|
return;
|
|
}
|
|
const [start, end] = a < b ? [a, b] : [b, a];
|
|
const range = orderedIds.slice(start, end + 1);
|
|
setIds((cur) => {
|
|
const next = new Set(cur);
|
|
range.forEach((i) => next.add(i));
|
|
return next;
|
|
});
|
|
setLastClickedId(id);
|
|
}, [lastClickedId]);
|
|
|
|
const clear = useCallback(() => {
|
|
setIds(new Set());
|
|
setLastClickedId(null);
|
|
}, []);
|
|
|
|
const value = useMemo<Ctx>(() => ({
|
|
ids,
|
|
has: (id) => ids.has(id),
|
|
toggle,
|
|
selectMany,
|
|
setMany,
|
|
selectRangeTo,
|
|
clear,
|
|
lastClickedId,
|
|
}), [ids, toggle, selectMany, setMany, selectRangeTo, clear, lastClickedId]);
|
|
|
|
return <ActressSelectCtx.Provider value={value}>{children}</ActressSelectCtx.Provider>;
|
|
}
|
|
|
|
export function useActressSelection() {
|
|
const ctx = useContext(ActressSelectCtx);
|
|
if (!ctx) throw new Error("useActressSelection must be used within ActressSelectionProvider");
|
|
return ctx;
|
|
}
|