Files
pinkudex/components/actress/ActressSelectionProvider.tsx
2026-05-26 22:46:00 +02:00

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;
}