import type { WritableAtom } from "jotai"; import { useAtomValue, useStore } from "jotai"; import { selectAtom } from "jotai/utils"; import { createContext, useCallback, useContext, useMemo } from "react"; type CollapsedMap = Record; type SetAction = CollapsedMap | ((prev: CollapsedMap) => CollapsedMap); export type CollapsedAtom = WritableAtom; export const CollapsedAtomContext = createContext(null); export function useCollapsedAtom(): CollapsedAtom { const atom = useContext(CollapsedAtomContext); if (!atom) throw new Error("CollapsedAtomContext not provided"); return atom; } export function useIsCollapsed(itemId: string | undefined) { const collapsedAtom = useCollapsedAtom(); const derivedAtom = useMemo( () => selectAtom(collapsedAtom, (map) => !!map[itemId ?? "n/a"], Object.is), [collapsedAtom, itemId], ); return useAtomValue(derivedAtom); } export function useSetCollapsed(itemId: string | undefined) { const collapsedAtom = useCollapsedAtom(); const store = useStore(); return useCallback( (next: boolean | ((prev: boolean) => boolean)) => { const key = itemId ?? "n/a"; const prevMap = store.get(collapsedAtom); const prevValue = !!prevMap[key]; const value = typeof next === "function" ? next(prevValue) : next; if (value === prevValue) return; store.set(collapsedAtom, { ...prevMap, [key]: value }); }, [collapsedAtom, itemId, store], ); } export function useCollapsedMap() { const collapsedAtom = useCollapsedAtom(); return useAtomValue(collapsedAtom); } export function useIsAncestorCollapsed(ancestorIds: string[]) { const collapsedAtom = useCollapsedAtom(); const derivedAtom = useMemo( () => selectAtom( collapsedAtom, (collapsed) => ancestorIds.some((id) => collapsed[id]), (a, b) => a === b, ), [collapsedAtom, ancestorIds], ); return useAtomValue(derivedAtom); }