/** * Headless, transport-neutral state for DataTable. * * The controller intentionally stores only view preferences. It never stores * rows, callbacks, query objects, principals, or persistence adapters. */ export type DataTableRowId = string | number; /** A caller-owned binding for an all-matching server-side selection. */ export interface DataTableQueryRevision { queryFingerprint: string; queryRevision: string; } /** * Selection is deliberately a tagged union. `allMatching` never serializes * loaded IDs: the receiving action must verify its query binding before use. */ export type DataTableSelection = { scope: 'page'; rowIds: DataTableRowId[]; } | { scope: 'explicit'; rowIds: DataTableRowId[]; } | ({ scope: 'allMatching'; expectedCount: number; } & DataTableQueryRevision); export type DataTableJsonPrimitive = string | number | boolean | null; export type DataTableJsonValue = DataTableJsonPrimitive | DataTableJsonValue[] | { [key: string]: DataTableJsonValue; }; export type DataTableOperationMode = 'local' | 'manual'; /** Which layer owns each transformation. `manual` means the caller supplied it. */ export interface DataTableModes { filtering: DataTableOperationMode; sorting: DataTableOperationMode; pagination: DataTableOperationMode; } export interface DataTableSortRule { columnId: string; direction: 'asc' | 'desc'; } export type DataTableFilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'startsWith' | 'endsWith' | 'in' | 'notIn' | 'gt' | 'gte' | 'lt' | 'lte' | 'isNull' | 'isNotNull'; /** A serializable, declarative filter. Filters are combined with AND semantics. */ export interface DataTableFilter { columnId: string; operator: DataTableFilterOperator; value?: DataTableJsonValue; } /** A column visibility entry. The array form keeps snapshots JSON-safe. */ export interface DataTableColumnVisibility { columnId: string; visible: boolean; } /** A persisted width in CSS pixels. Static column widths remain presentation defaults. */ export interface DataTableColumnWidth { columnId: string; width: number; } /** A persisted edge pin. Column order is retained within each pin partition. */ export interface DataTableColumnPinning { columnId: string; position: 'start' | 'end'; } /** * The JSON-safe, persistable portion of a table view. Selection and expansion * are serialized as canonical arrays rather than Sets. */ export interface DataTableViewState { search: string; filters: DataTableFilter[]; sorting: DataTableSortRule[]; page: number; pageSize: number | null; columnOrder: string[]; columnVisibility: DataTableColumnVisibility[]; columnWidths: DataTableColumnWidth[]; columnPinning: DataTableColumnPinning[]; selection: DataTableSelection; /** * Legacy shorthand for row-ID selections. It is always derived from * `selection`, and is empty for `allMatching` selections. */ selectedRowIds: DataTableRowId[]; expandedRowIds: DataTableRowId[]; } /** * Accepts version-1 and version-2 controlled state while the controller emits * normalized version-3 state with explicit selection and column layout. */ export type DataTableViewStateInput = Omit & { selection?: DataTableSelection; /** Optional while restoring version-1 or version-2 state. */ columnWidths?: DataTableColumnWidth[]; /** Optional while restoring version-1 or version-2 state. */ columnPinning?: DataTableColumnPinning[]; }; /** The stable envelope intended for URL and saved-view adapters. */ export interface DataTableSnapshot { version: 3; modes: DataTableModes; state: DataTableViewState; } /** Every mutable table interaction has a plain-data command representation. */ export type DataTableCommand = { type: 'setSearch'; search: string; } | { type: 'setFilters'; filters: DataTableFilter[]; } | { type: 'setSorting'; sorting: DataTableSortRule[]; } | { type: 'toggleSorting'; columnId: string; multi?: boolean; } | { type: 'setPage'; page: number; } | { type: 'setPageSize'; pageSize: number | null; } | { type: 'setColumnOrder'; columnIds: string[]; } | { type: 'setColumnVisibility'; columns: DataTableColumnVisibility[]; } | { type: 'setColumnWidths'; columns: DataTableColumnWidth[]; } | { type: 'setColumnWidth'; columnId: string; width: number | null; } | { type: 'setColumnPinning'; columns: DataTableColumnPinning[]; } | { type: 'setColumnPin'; columnId: string; position: DataTableColumnPinning['position'] | null; } | { type: 'setSelection'; selection: DataTableSelection; } | { type: 'setPageSelection'; rowIds: DataTableRowId[]; } | ({ type: 'selectAllMatching'; expectedCount: number; } & DataTableQueryRevision) | { type: 'setSelectedRows'; rowIds: DataTableRowId[]; } | { type: 'toggleRowSelection'; rowId: DataTableRowId; } | { type: 'setExpandedRows'; rowIds: DataTableRowId[]; } | { type: 'toggleRowExpansion'; rowId: DataTableRowId; } | { type: 'reset'; }; export interface DataTableTransition { command: DataTableCommand | null; previous: DataTableSnapshot; next: DataTableSnapshot; changed: boolean; } export interface DataTableControllerOptions { /** Used by uncontrolled controllers. */ initialState?: Partial; /** Makes transitions proposals until `replaceState` supplies the next value. */ state?: DataTableViewStateInput; modes?: Partial; /** Optional known columns let the controller ignore stale saved-view fields. */ columnIds?: readonly string[]; /** Static schema constraints that saved or controlled state may not override. */ hiddenColumnIds?: readonly string[]; onStateChange?: (state: DataTableViewState, command: DataTableCommand) => void; } export type DataTableStateListener = (transition: DataTableTransition) => void; export declare function assertDataTableRowId(value: unknown): DataTableRowId; export declare function compareDataTableRowIds(left: DataTableRowId, right: DataTableRowId): number; export declare function dataTableRowIdKey(value: DataTableRowId): string; /** * Refuse an all-matching selection when the action's query has changed since * selection. Domain actions must call this before a destructive operation. */ export declare function assertDataTableSelectionCurrent(selection: DataTableSelection, currentQuery: DataTableQueryRevision): void; /** Apply one command without mutating the supplied state. */ export declare function transitionDataTableState(state: DataTableViewState, command: DataTableCommand): DataTableViewState; /** Parse persisted state defensively before an external adapter restores it. */ export declare function hydrateDataTableSnapshot(value: unknown): DataTableSnapshot; /** A headless state owner used by both rendered controls and programmatic commands. */ export declare class DataTableController { private state; private modes; private columnIds?; private hiddenColumnIds?; /** * Static schema visibility is a rendering constraint, not a saved-view * preference. Keep the latter while a column is constrained so removing the * constraint restores the caller's prior intent. */ private readonly visibilityBeforeStaticHide; private controlled; private readonly listeners; private readonly onStateChange?; private pendingControlledState?; constructor(options?: DataTableControllerOptions); getState(): DataTableViewState; getModes(): DataTableModes; /** Whether commands are proposals until an owning host supplies state. */ isControlled(): boolean; snapshot(): DataTableSnapshot; subscribe(listener: DataTableStateListener): () => void; /** Dispatches a serializable command. Controlled controllers emit a proposal only. */ dispatch(command: DataTableCommand): DataTableTransition; /** Supplies state from a controlled host or an external persistence adapter. */ replaceState(state: DataTableViewStateInput): DataTableTransition; /** Changes ownership without treating it as a user command. */ setControlled(controlled: boolean): void; /** Configures transformation ownership; this remains outside persisted state. */ setModes(modes: Partial): DataTableTransition; /** Reconciles stale saved-view column IDs with the renderer's current columns. */ setColumnIds(columnIds: readonly string[], hiddenColumnIds?: readonly string[]): DataTableTransition; private rememberStaticVisibility; /** Clamp against a reliable total. A missing total intentionally does not guess. */ clampPage(totalRows: number | null | undefined): DataTableTransition; private emit; } export declare function createDataTableController(options?: DataTableControllerOptions): DataTableController; //# sourceMappingURL=DataTableController.d.ts.map