feat(drill): DrillStackProvider — zustand-backed peek stack with 2-level cap

This commit is contained in:
Tyler
2026-06-21 11:38:02 -06:00
parent feb15bf48d
commit be93dbff72
2 changed files with 98 additions and 0 deletions
@@ -0,0 +1,53 @@
import { createContext, useContext, useMemo, type ReactNode } from "react";
import { create } from "zustand";
export type PeekPayload =
| { kind: "payer"; payerId: string }
| { kind: "rule"; rule: string };
interface DrillState {
stack: PeekPayload[];
openPeek: (p: PeekPayload) => void;
closeTop: () => void;
closeAll: () => void;
}
// One zustand store per provider instance (factory) so multiple
// providers (e.g. in tests) don't share state.
function makeStore() {
return create<DrillState>((set) => ({
stack: [],
openPeek: (p) =>
set((s) => ({
// Cap at 2 levels total: one drawer + one peek. When called and
// the stack already has one peek, replace it.
stack: s.stack.length >= 1 ? [p] : [p],
})),
closeTop: () => set((s) => ({ stack: s.stack.slice(0, -1) })),
closeAll: () => set({ stack: [] }),
}));
}
type StoreApi = ReturnType<typeof makeStore>;
const Ctx = createContext<StoreApi | null>(null);
export function DrillStackProvider({ children }: { children: ReactNode }) {
// useMemo so the store instance is stable across renders.
const store = useMemo(makeStore, []);
return <Ctx.Provider value={store}>{children}</Ctx.Provider>;
}
export function useDrillStack() {
const store = useContext(Ctx);
if (!store) throw new Error("useDrillStack must be used within DrillStackProvider");
// Subscribe to just `stack` so consumers re-render only on stack
// changes (not on every state update).
const stack = store((s) => s.stack);
return {
stack,
openPeek: store.getState().openPeek,
closeTop: store.getState().closeTop,
closeAll: store.getState().closeAll,
};
}