import type { Table } from '@tanstack/react-table' import type { ColumnFiltersState, RowSelectionState, SortingState } from '@tanstack/react-table' import type { Virtualizer } from '@tanstack/react-virtual' import { createSyncStore } from '@warkypublic/zustandsyncstore' import type { DataAdapter, GriddyColumn, GriddyProps, GriddyUIState, GroupingConfig, PaginationConfig, SearchConfig, SelectionConfig } from './types' // ─── Store State ───────────────────────────────────────────────────────────── /** * Full store state: UI state + synced props + internal references. * Props from GriddyProps are synced automatically via createSyncStore's $sync. * Fields from GriddyProps must be declared here so TypeScript can see them. */ export interface GriddyStoreState extends GriddyUIState { _scrollRef: HTMLDivElement | null // ─── Internal refs (set imperatively) ─── _table: null | Table _virtualizer: null | Virtualizer className?: string columnFilters?: ColumnFiltersState columns?: GriddyColumn[] data?: any[] dataAdapter?: DataAdapter getRowId?: (row: any, index: number) => string grouping?: GroupingConfig height?: number | string keyboardNavigation?: boolean onColumnFiltersChange?: (filters: ColumnFiltersState) => void onEditCommit?: (rowId: string, columnId: string, value: unknown) => Promise | void onRowSelectionChange?: (selection: RowSelectionState) => void onSortingChange?: (sorting: SortingState) => void overscan?: number pagination?: PaginationConfig persistenceKey?: string rowHeight?: number rowSelection?: RowSelectionState search?: SearchConfig selection?: SelectionConfig setScrollRef: (el: HTMLDivElement | null) => void // ─── Internal ref setters ─── setTable: (table: Table) => void setVirtualizer: (virtualizer: Virtualizer) => void sorting?: SortingState // ─── Synced from GriddyProps (written by $sync) ─── uniqueId?: string } // ─── Create Store ──────────────────────────────────────────────────────────── export const { Provider: GriddyProvider, useStore: useGriddyStore } = createSyncStore< GriddyStoreState, GriddyProps >( (set, get) => ({ _scrollRef: null, // ─── Internal Refs ─── _table: null, _virtualizer: null, focusedColumnId: null, // ─── Focus State ─── focusedRowIndex: null, // ─── Mode State ─── isEditing: false, isSearchOpen: false, isSelecting: false, moveFocus: (direction, amount) => { const { focusedRowIndex, totalRows } = get() const current = focusedRowIndex ?? 0 const delta = direction === 'down' ? amount : -amount const next = Math.max(0, Math.min(current + delta, totalRows - 1)) set({ focusedRowIndex: next }) }, moveFocusToEnd: () => { const { totalRows } = get() set({ focusedRowIndex: Math.max(0, totalRows - 1) }) }, moveFocusToStart: () => set({ focusedRowIndex: 0 }), setEditing: (editing) => set({ isEditing: editing }), setFocusedColumn: (id) => set({ focusedColumnId: id }), // ─── Actions ─── setFocusedRow: (index) => set({ focusedRowIndex: index }), setScrollRef: (el) => set({ _scrollRef: el }), setSearchOpen: (open) => set({ isSearchOpen: open }), setSelecting: (selecting) => set({ isSelecting: selecting }), // ─── Internal Ref Setters ─── setTable: (table) => set({ _table: table }), setTotalRows: (count) => set({ totalRows: count }), setVirtualizer: (virtualizer) => set({ _virtualizer: virtualizer }), // ─── Row Count ─── totalRows: 0, }), )