@gridkitjs/core 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -74,12 +74,19 @@ interface ColumnDefinition<Row, Node = string> {
74
74
  * same field differently.
75
75
  */
76
76
  id?: string;
77
+ /**
78
+ * The column's name as plain text. The header shows it when there is no
79
+ * `headerTemplate`, and anything that needs the name as text rather than
80
+ * markup — a screen-reader announcement, an export — reads it even when
81
+ * there is one, since a template's output need not be text at all.
82
+ */
83
+ header?: string | undefined;
77
84
  /** Header content, or a function returning it at render time. */
78
85
  headerTemplate?: Node | (() => Node) | undefined;
79
86
  /**
80
- * Renders this column's cells in place of the raw value at `field`. The
81
- * value is still resolved and handed over, so a template that only formats
82
- * it never repeats the field path.
87
+ * Renders this column's cells in place of the text `formatCellValue` makes
88
+ * of the value at `field`. The raw value is still resolved and handed over,
89
+ * so a template never repeats the field path.
83
90
  */
84
91
  cellTemplate?: ((context: CellTemplateContext<Row>) => Node) | undefined;
85
92
  /**
@@ -94,6 +101,22 @@ interface ColumnDefinition<Row, Node = string> {
94
101
  * defaults to `string`;
95
102
  */
96
103
  type?: ColumnType;
104
+ /**
105
+ * `Intl` options for how this column's values read as text: number options
106
+ * for a numeric `type`, date options for a date or time one. Passed to
107
+ * `Intl.NumberFormat`/`Intl.DateTimeFormat` with the grid's `locale` — see
108
+ * `formatCellValue` for the defaults each `type` starts from. Setting it,
109
+ * or the grid's `locale`, is what turns that formatting on: without either,
110
+ * numbers print as-is and dates through `toLocaleString()`.
111
+ */
112
+ format?: Intl.NumberFormatOptions | Intl.DateTimeFormatOptions | undefined;
113
+ /**
114
+ * Turns a value into the text shown for it, in place of `format` and
115
+ * `type`. Applies wherever this column's values are shown as text — its
116
+ * cells and its aggregates. `row` is the cell's row, and `undefined` for a
117
+ * value no single row owns: an aggregate result, or a group header's value.
118
+ */
119
+ valueFormatter?: ((value: unknown, row: Row | undefined) => string) | undefined;
97
120
  /**
98
121
  * Alignment of this column's cells. This package is framework-agnostic so it
99
122
  * defaults to `left` (`right` for numbers);
@@ -156,6 +179,14 @@ interface ColumnDefinition<Row, Node = string> {
156
179
  * stays a plain string rather than taking the row.
157
180
  */
158
181
  cellClassName?: string;
182
+ /**
183
+ * Whether this column starts out hidden. A hidden column is not rendered
184
+ * but is still a column of the grid: a sort, filter or grouping on it
185
+ * still applies, and it keeps its place in the column order. The user
186
+ * showing or hiding it lives in `ColumnVisibilityState`, which wins over
187
+ * this for as long as it holds an entry for the column.
188
+ */
189
+ hidden?: boolean | undefined;
159
190
  }
160
191
  /** Which parts of a column opt out of the grid's default single-line text. */
161
192
  interface ColumnWrapConfig {
@@ -202,6 +233,26 @@ interface ColumnResolveOptions {
202
233
  /** Whether columns may be dragged into the group-by bar, unless a column says otherwise. */
203
234
  groupByDraggable?: boolean | undefined;
204
235
  }
236
+ /**
237
+ * How a column's width relates to the grid's.
238
+ *
239
+ * `"fit"` keeps the columns filling the grid: the space a column gives up is
240
+ * handed to the others, and the space it takes comes out of them. `"fixed"`
241
+ * lets every column keep its own width, so a resize moves one column and
242
+ * nothing else, and the grid scrolls or leaves a gap accordingly.
243
+ */
244
+ type ResizeMode = "fit" | "fixed";
245
+ /**
246
+ * A column as the data stages read it — filtering, sorting, grouping and
247
+ * aggregating need its id and its definition's `field` and `type`, never its
248
+ * width. Kept apart from `ResolvedColumn` so that a resize or a viewport
249
+ * change, which re-resolves every width, leaves these stages' input — and so
250
+ * their output — untouched. `ResolvedColumn` satisfies it structurally.
251
+ */
252
+ interface DataColumn<Row, Node = string> {
253
+ readonly id: string;
254
+ readonly column: ColumnDefinition<Row, Node>;
255
+ }
205
256
  /**
206
257
  * A column paired with everything it takes to render it. Each field is a
207
258
  * decision made once here rather than in each adapter, so a second framework
@@ -218,9 +269,9 @@ interface ResolvedColumn<Row, Node = string> {
218
269
  */
219
270
  sized: boolean;
220
271
  /**
221
- * What the header shows: the column's own `headerTemplate`, or a label read
222
- * off the field path. `string` is in the union because that fallback is text
223
- * whatever `Node` is bound to.
272
+ * What the header shows: the column's own `headerTemplate`, else its
273
+ * `header`, else a label read off the field path. `string` is in the
274
+ * union because both fallbacks are text whatever `Node` is bound to.
224
275
  */
225
276
  label: Node | string;
226
277
  /** Whether this column can be resized, after the grid-level default. */
@@ -252,6 +303,22 @@ interface ColumnResizeEvent {
252
303
  readonly sizing: ColumnSizingState;
253
304
  readonly phase: "move" | "end";
254
305
  }
306
+ /**
307
+ * Columns shown (`true`) or hidden (`false`) since the grid started, keyed by
308
+ * column id. Holding only what changed, the way `ColumnSizingState` does: a
309
+ * column absent from it follows its definition's `hidden`, so a definition
310
+ * edited later still takes effect for every column the user never touched.
311
+ */
312
+ type ColumnVisibilityState = Readonly<Record<string, boolean>>;
313
+ /**
314
+ * Reports columns shown or hidden. `columnIds` lists every column the one
315
+ * change touched — showing three columns at once is one event, not three —
316
+ * and `visibility` is the whole state to persist from.
317
+ */
318
+ interface ColumnVisibilityEvent {
319
+ readonly columnIds: readonly string[];
320
+ readonly visibility: ColumnVisibilityState;
321
+ }
255
322
  /**
256
323
  * Column ids in the order they render. It need not list every column: one
257
324
  * absent from it keeps its position among the definitions and follows those
@@ -484,13 +551,84 @@ interface GroupFilterEntry<Row> {
484
551
  readonly combinator: "and" | "or";
485
552
  readonly entries: FilterState<Row>;
486
553
  }
487
- type FilterEntry<Row> = TextFilterEntry<Row> | ValueFilterEntry<Row> | PredicateFilterEntry<Row> | GroupFilterEntry<Row>;
554
+ /**
555
+ * What an `OperatorFilterEntry` asks of a cell. `contains`, `notContains`,
556
+ * `startsWith` and `endsWith` read the value as text; the rest compare it as
557
+ * the column's `type` says.
558
+ */
559
+ type FilterOperator = "eq" | "ne" | "contains" | "notContains" | "startsWith" | "endsWith" | "gt" | "gte" | "lt" | "lte" | "between" | "in" | "isEmpty" | "isNotEmpty";
560
+ /** A value an `OperatorFilterEntry` compares a cell against. */
561
+ type FilterScalar = string | number | boolean | Date | null;
562
+ /**
563
+ * Matches one column's value by a named operator — plain data, unlike a
564
+ * predicate, so a filter built from these can be saved and restored.
565
+ *
566
+ * `value` is one `FilterScalar` for most operators, a list of them for
567
+ * `in`, a `[low, high]` pair for `between` (both ends inclusive), and
568
+ * absent for `isEmpty`/`isNotEmpty`.
569
+ *
570
+ * - An empty cell is `null`, `undefined` or `""`. `isEmpty` matches it,
571
+ * `isNotEmpty` does not, and so does no other operator — except `ne` and
572
+ * `notContains`, which match it: a "not" filter must not quietly drop the
573
+ * blanks. Each of those two is the exact opposite of `eq`/`contains`.
574
+ * - The text operators compare the stringified value, case-insensitively.
575
+ * - `eq`, `ne`, `gt`, `gte`, `lt`, `lte` and `between` compare as
576
+ * `compareValues` does under the column's `type`: numerically,
577
+ * chronologically, or as text (case-insensitively, like a query). A cell
578
+ * of another kind — a string in a number column — is never equal, greater
579
+ * or less.
580
+ * - `in` matches the values grouping would put in the same group.
581
+ * - A date column reads a string `value` as `new Date(value)`.
582
+ * - A `value` of the wrong kind for the column — a string against a number
583
+ * column, a missing pair for `between` — matches no row at all, as a
584
+ * `ValueFilterEntry` of the wrong type doesn't.
585
+ */
586
+ interface OperatorFilterEntry<Row> {
587
+ readonly columnId: FieldPath<Row> | (string & {});
588
+ readonly operator: FilterOperator;
589
+ readonly value?: FilterScalar | readonly FilterScalar[];
590
+ }
591
+ type FilterEntry<Row> = TextFilterEntry<Row> | ValueFilterEntry<Row> | PredicateFilterEntry<Row> | OperatorFilterEntry<Row> | GroupFilterEntry<Row>;
488
592
  /**
489
593
  * The active filter, every entry ANDed together at the top level — nest a
490
594
  * `GroupFilterEntry` for anything that needs OR. Empty for an unfiltered
491
595
  * grid, mirroring `ColumnSortState`'s empty array for "no sort".
492
596
  */
493
597
  type FilterState<Row> = readonly FilterEntry<Row>[];
598
+ /**
599
+ * A `ValueFilterEntry` as JSON holds it: a `Date` value becomes its ISO
600
+ * string, which restoring turns back into a `Date`.
601
+ */
602
+ interface SerializedValueFilterEntry<Row> extends FilterEntryBase<Row> {
603
+ readonly value: number | boolean | string;
604
+ }
605
+ /** A `FilterScalar` as JSON holds it: a `Date` becomes its ISO string. */
606
+ type SerializedFilterScalar = string | number | boolean | null;
607
+ /**
608
+ * An `OperatorFilterEntry` as JSON holds it: each `Date` becomes its ISO
609
+ * string, which restoring turns back into a `Date` when the column is a
610
+ * date, date-time or time column.
611
+ */
612
+ interface SerializedOperatorFilterEntry<Row> {
613
+ readonly columnId: FieldPath<Row> | (string & {});
614
+ readonly operator: FilterOperator;
615
+ readonly value?: SerializedFilterScalar | readonly SerializedFilterScalar[];
616
+ }
617
+ /** A `GroupFilterEntry` whose entries are serialised too. */
618
+ interface SerializedGroupFilterEntry<Row> {
619
+ readonly combinator: "and" | "or";
620
+ readonly entries: SerializedFilterState<Row>;
621
+ }
622
+ /**
623
+ * A filter entry that survives `JSON.stringify`: every kind but a predicate,
624
+ * which is a function and cannot be written down.
625
+ */
626
+ type SerializedFilterEntry<Row> = TextFilterEntry<Row> | SerializedValueFilterEntry<Row> | SerializedOperatorFilterEntry<Row> | SerializedGroupFilterEntry<Row>;
627
+ type SerializedFilterState<Row> = readonly SerializedFilterEntry<Row>[];
628
+ /** Reports a filter change whole — the filter to persist from. */
629
+ interface FilterChangeEvent<Row> {
630
+ readonly filter: FilterState<Row>;
631
+ }
494
632
  /**
495
633
  * Ids selected, in the order they were selected — so the most recent is last,
496
634
  * and a consumer wanting only that reads `at(-1)` rather than tracking it
@@ -523,6 +661,13 @@ interface SelectableConfig {
523
661
  rows?: SelectionMode | undefined;
524
662
  columns?: SelectionMode | undefined;
525
663
  cells?: CellSelectionMode | undefined;
664
+ /**
665
+ * Whether a plain click on a row toggles it rather than selecting it
666
+ * alone — a checklist, where picking rows one after another should not
667
+ * need Ctrl held down. Shift still selects a range and Ctrl/Cmd still
668
+ * toggles. Rows only: a column or cell click is unchanged.
669
+ */
670
+ toggleOnClick?: boolean | undefined;
526
671
  }
527
672
  /**
528
673
  * What an interaction means for the selection, read off its modifiers once at
@@ -582,6 +727,24 @@ interface SelectedCell<Row, Node = string> {
582
727
  /** Read off the column's field path — the value a `cellTemplate` receives. */
583
728
  readonly value: unknown;
584
729
  }
730
+ /**
731
+ * Reports a click or double-click on a data row. Carries the modifiers and
732
+ * button rather than the browser's own event, so a handler reads the same
733
+ * payload whichever renderer drew the row.
734
+ */
735
+ interface RowPointerEvent<Row> {
736
+ readonly row: ResolvedRow<Row>;
737
+ /** The column of the cell the pointer was on, or `null` when it was on no cell. */
738
+ readonly columnId: string | null;
739
+ readonly native: {
740
+ readonly ctrlKey: boolean;
741
+ readonly metaKey: boolean;
742
+ readonly shiftKey: boolean;
743
+ readonly altKey: boolean;
744
+ /** Which button was pressed, numbered as `MouseEvent.button` numbers them — `0` for the main one. */
745
+ readonly button: number;
746
+ };
747
+ }
585
748
  /** Reports one row selected or deselected. */
586
749
  interface RowSelectEvent<Row> {
587
750
  readonly row: ResolvedRow<Row>;
@@ -639,6 +802,828 @@ interface CellSelectionChangeEvent<Row, Node = string> {
639
802
  readonly deselected: SelectedCell<Row, Node> | null;
640
803
  readonly selection: CellSelectionState;
641
804
  }
805
+ /**
806
+ * Everything about a grid the user can change, one slice per feature. Each
807
+ * slice is the same `*State` type its own `default*` prop and change event
808
+ * already use, so state read from here can be fed straight back as a default.
809
+ */
810
+ interface GridState<Row> {
811
+ readonly columnSizing: ColumnSizingState;
812
+ readonly columnOrder: ColumnOrderState;
813
+ readonly columnVisibility: ColumnVisibilityState;
814
+ readonly columnSort: ColumnSortState;
815
+ readonly filter: FilterState<Row>;
816
+ readonly groupBy: GroupByState;
817
+ readonly groupExpansion: GroupExpansionState;
818
+ readonly pagination: PaginationState;
819
+ readonly rowSelection: SelectionState;
820
+ readonly columnSelection: SelectionState;
821
+ readonly cellSelection: CellSelectionState;
822
+ }
823
+ /**
824
+ * `GridState` as `serializeGridState` writes it: safe to pass through
825
+ * `JSON.stringify` and back, and to hand straight to `restoreState`.
826
+ */
827
+ interface SerializedGridState<Row> extends Omit<GridState<Row>, "filter"> {
828
+ readonly filter: SerializedFilterState<Row>;
829
+ }
830
+ /**
831
+ * The slices the view is derived from: every one but the selections, which
832
+ * change what is highlighted and never what is shown. A whole `GridState`
833
+ * is one.
834
+ */
835
+ type GridViewState<Row> = Omit<GridState<Row>, "rowSelection" | "columnSelection" | "cellSelection">;
836
+ /**
837
+ * The settings that shape the view but are not user state: what the grid is
838
+ * configured to do, plus the one measurement — `viewportWidth` — that fitting
839
+ * columns needs from the page. Every field falls back to the same default
840
+ * the React grid's matching prop does.
841
+ */
842
+ interface GridViewOptions<Row> {
843
+ /** Whether `pageRows` is one page of `displayRows` rather than all of it. Defaults to `false`. */
844
+ readonly paginated?: boolean | undefined;
845
+ readonly aggregates?: AggregateState<Row> | undefined;
846
+ /** Defaults to `"inline"`. */
847
+ readonly groupAggregateDisplay?: GroupAggregateDisplay | undefined;
848
+ readonly columnSizeDefaults?: Partial<ColumnSizeDefaults> | undefined;
849
+ readonly resizableColumns?: boolean | undefined;
850
+ readonly reorderableColumns?: boolean | undefined;
851
+ readonly groupByDraggableColumns?: boolean | undefined;
852
+ /** Defaults to `"fit"`, which only takes effect once `viewportWidth` is known. */
853
+ readonly resizeMode?: ResizeMode | undefined;
854
+ /**
855
+ * The width, in px, `"fit"` fills. `null` or absent before it has been
856
+ * measured, or where there is no viewport at all (an export in Node), and
857
+ * then columns keep their resolved widths.
858
+ */
859
+ readonly viewportWidth?: number | null | undefined;
860
+ }
861
+ /** What `resolveGridView` derives the view from. */
862
+ interface GridViewInput<Row, Node = string> {
863
+ readonly rows: readonly Row[];
864
+ readonly getRowId?: ((row: Row, index: number) => string) | undefined;
865
+ readonly columns: readonly ColumnDefinition<Row, Node>[];
866
+ readonly state: GridViewState<Row>;
867
+ readonly options: GridViewOptions<Row>;
868
+ }
869
+ /**
870
+ * What the user is looking at, derived once from `GridViewInput` — every
871
+ * list a renderer draws, and the ones an export, a toolbar or a status line
872
+ * reads, so none of them composes the pipeline for itself.
873
+ */
874
+ interface GridView<Row, Node = string> {
875
+ /** Every column, hidden ones included, ordered but not sized — what the data stages read. */
876
+ readonly dataColumns: readonly DataColumn<Row, Node>[];
877
+ /** The columns as rendered: visible, ordered and sized. */
878
+ readonly columns: readonly ResolvedColumn<Row, Node>[];
879
+ /** The rows, filtered then sorted. */
880
+ readonly rows: readonly ResolvedRow<Row>[];
881
+ /** `rows` grouped, with each group's aggregates attached — every page of it. */
882
+ readonly displayRows: readonly DisplayRow<Row>[];
883
+ /** The current page of `displayRows`; all of it when not paginated. */
884
+ readonly pageRows: readonly DisplayRow<Row>[];
885
+ /** `pageRows` narrowed to its data rows, in display order — what a range selection spans. */
886
+ readonly displayDataRows: readonly ResolvedRow<Row>[];
887
+ /** `1` when not paginated. */
888
+ readonly pageCount: number;
889
+ /** `state.pagination.pageIndex` clamped into range; `0` when not paginated. */
890
+ readonly pageIndex: number;
891
+ /** Every aggregate over all of `rows`, ungrouped and unpaginated. */
892
+ readonly grandTotal: AggregateResults;
893
+ }
894
+
895
+ /**
896
+ * Every string a grid shows or announces of its own — the pager, the group-by
897
+ * bar, the loading row, the text of a boolean or missing value, and the
898
+ * live-region announcements — so a grid can speak a language other than
899
+ * English. A message that names something takes a function, so a translation
900
+ * can put the name where its own grammar wants it.
901
+ *
902
+ * Text the data supplies (cells, headers, labels) is not here: it is already
903
+ * the app's to choose. Nor is formatting — dates and numbers follow
904
+ * `locale`.
905
+ */
906
+ interface GridMessages {
907
+ /** The pager's accessible name. */
908
+ pagerLabel: string;
909
+ previousPage: string;
910
+ nextPage: string;
911
+ /** The page-size control's label. */
912
+ pageSizeLabel: string;
913
+ /** A numbered pager button's accessible name. */
914
+ pageButtonLabel: (page: number) => string;
915
+ /**
916
+ * The compact pager's status, and what a page change announces. `page`
917
+ * counts from one, and is `0` while there are no pages.
918
+ */
919
+ pageStatus: (page: number, pageCount: number) => string;
920
+ /** The row shown while `infiniteScroll` loads more. */
921
+ loadingMore: string;
922
+ /** A boolean cell's text. */
923
+ booleanTrue: string;
924
+ booleanFalse: string;
925
+ /** What a group header names a group of missing values. */
926
+ blankGroupValue: string;
927
+ /** The group-by bar's accessible name. */
928
+ groupByBarLabel: string;
929
+ /** Shown in an empty group-by bar a header can be dragged into. */
930
+ groupByBarPlaceholder: string;
931
+ /** A group-by chip's accessible name. `level` counts from one. */
932
+ groupByChipLabel: (columnName: string, level: number, levelCount: number) => string;
933
+ /** A group-by chip's remove button's accessible name. */
934
+ removeGroupBy: (columnName: string) => string;
935
+ /** Announced when a resize ends, `width` in whole pixels. */
936
+ columnResized: (columnName: string, width: number) => string;
937
+ /** Announced when a column is moved. `position` counts from one. */
938
+ columnMoved: (columnName: string, position: number, columnCount: number) => string;
939
+ /** Announced when a column is sorted. `key` is its place in the sort, from one. */
940
+ columnSorted: (columnName: string, direction: SortDirection, key: number, keyCount: number) => string;
941
+ /** Announced when a column leaves the sort. */
942
+ sortCleared: (columnName: string) => string;
943
+ /** Announced when a column is grouped by. `level` counts from one. */
944
+ groupedBy: (columnName: string, level: number, levelCount: number) => string;
945
+ /** Announced when a column leaves the grouping. */
946
+ groupingRemoved: (columnName: string) => string;
947
+ groupExpanded: string;
948
+ groupCollapsed: string;
949
+ allGroupsExpanded: string;
950
+ allGroupsCollapsed: string;
951
+ /** Announced when one change selects or deselects more than one row or column. */
952
+ selectedCount: (count: number, target: "rows" | "columns") => string;
953
+ /** Announced when a cell is selected. `rowNumber` counts from one. */
954
+ cellSelected: (columnName: string, rowNumber: number) => string;
955
+ }
956
+ /** The grid's own English, which `messages` replaces key by key. */
957
+ declare const defaultMessages: GridMessages;
958
+ /**
959
+ * `messages` over the defaults — every key the grid needs, whichever the
960
+ * caller gave. The defaults themselves, same object, when it gave none, so a
961
+ * renderer memoising on the result does not see a change that is not one.
962
+ */
963
+ declare function resolveMessages(messages: Partial<GridMessages> | undefined): GridMessages;
964
+
965
+ /** The locale(s) values are formatted for — `undefined` is the runtime's own. */
966
+ type FormatLocale = string | readonly string[] | undefined;
967
+ /**
968
+ * The text a value reads as in `column` — what a cell shows when the column
969
+ * has no `cellTemplate`, and what an export or a print writes, so the two
970
+ * cannot disagree.
971
+ *
972
+ * A `valueFormatter` decides outright. `null`/`undefined` read `""`, a
973
+ * boolean `"true"`/`"false"` — or `messages.booleanTrue`/`booleanFalse`
974
+ * when given, for a grid speaking another language — a string as it is, a
975
+ * plain object or array as JSON.
976
+ *
977
+ * Dates and numbers are formatted through `Intl` only once asked to — the
978
+ * column has a `format`, or a `locale` is given. Until then they print as a
979
+ * grid always printed them, so adopting this changes nothing on screen: a
980
+ * `Date` through `toLocaleString()`, a number as-is (`1234.5`, never
981
+ * `1,234.5`). Once asked:
982
+ *
983
+ * - A `Date` → `Intl.DateTimeFormat`: the date alone under `"date"`, the time
984
+ * alone under `"time"`, both otherwise.
985
+ * - A number under a numeric `type` → `Intl.NumberFormat`: `"currency"` with
986
+ * a currency symbol once `format.currency` names one (two fraction digits
987
+ * until then), `"percent"` reading `0.25` as 25 %. Under any other type it
988
+ * is still printed as-is, the way a number id should be.
989
+ *
990
+ * `format` and `locale` go to the `Intl` constructor unchanged, so any
991
+ * option it accepts is honoured.
992
+ */
993
+ declare function formatCellValue<Row>(value: unknown, column: ColumnDefinition<Row, unknown>, locale?: FormatLocale, row?: Row, messages?: Pick<GridMessages, "booleanTrue" | "booleanFalse">): string;
994
+ /**
995
+ * The text an aggregate result reads as — a footer cell, a group's subtotal
996
+ * — through `formatCellValue`, so once a `format` or `locale` asks for it a
997
+ * `sum` of a currency column reads as currency and a `max` of a date column
998
+ * as a date. `column` is `undefined` for a spec naming a column the grid
999
+ * does not have, which then formats by its value alone.
1000
+ *
1001
+ * Until then a numeric result reads through `toLocaleString()`, with digit
1002
+ * grouping, where a cell prints its number as-is: that is how a grid has
1003
+ * always shown its totals, and a sum easily runs into the thousands.
1004
+ */
1005
+ declare function formatAggregateValue<Row>(value: unknown, spec: AggregateSpec<Row>, column: ColumnDefinition<Row, unknown> | undefined, locale?: FormatLocale): string;
1006
+
1007
+ /** Which cell borders a grid draws. */
1008
+ type Borders = "horizontal" | "vertical" | "all" | "none";
1009
+ /** Which hover highlighting a grid shows. Every member defaults to on. */
1010
+ interface HoverableConfig {
1011
+ rows?: boolean;
1012
+ columns?: boolean;
1013
+ cells?: boolean;
1014
+ }
1015
+ /**
1016
+ * How the group-by bar's visibility follows the active grouping. `"always"`
1017
+ * renders it even with an empty `groupBy` — a fixed drop target and a
1018
+ * constant reminder grouping exists. `"never"` never renders it, for a grid
1019
+ * driving `groupBy` entirely by its own UI (a header toggle, `Alt+ArrowDown`)
1020
+ * or programmatically. `"auto"` renders it once `groupBy` is non-empty, or
1021
+ * while a header drag eligible to drop into it is in progress — so there is
1022
+ * always somewhere to drop the very first column, even from a fully empty
1023
+ * grouping.
1024
+ */
1025
+ type GroupByBarVisibility = "always" | "auto" | "never";
1026
+ /**
1027
+ * What `getRowProps` adds to a data row: a class for the theme or the
1028
+ * app's own stylesheet to target, and `data-*` attributes for a test or a
1029
+ * script to find the row by. No inline style — styling belongs to the
1030
+ * stylesheet, which a class already reaches.
1031
+ */
1032
+ interface RowProps {
1033
+ className?: string | undefined;
1034
+ /**
1035
+ * Set on the row alongside the grid's own `data-gridkit-*` attributes,
1036
+ * which win over one of the same name — the grid finds its rows by them.
1037
+ */
1038
+ attributes?: Readonly<Record<`data-${string}`, string>> | undefined;
1039
+ }
1040
+ /**
1041
+ * Every setting of a grid that names no renderer's types: the data, what the
1042
+ * user may do with it, the state it starts in and the callbacks that report
1043
+ * changes. A renderer's own props are this plus what only it can express —
1044
+ * React's `columns` (whose templates return `ReactNode`), its pager and
1045
+ * infinite-scroll templates, its `ref` — so two renderers cannot drift apart
1046
+ * on what a setting is called or what it means.
1047
+ */
1048
+ interface GridOptions<Row> {
1049
+ dataSource?: readonly Row[] | undefined;
1050
+ /**
1051
+ * A row's stable identity, for state keyed by it. Defaults to the row's
1052
+ * position, which is enough for a static grid but ties that state to where a
1053
+ * row sits rather than to the row — so anything sorting, filtering or paging
1054
+ * its data should give one.
1055
+ *
1056
+ * Called for every row on every change to `dataSource`, so it should be
1057
+ * cheap and stable; an inline arrow is fine, one that re-reads the data is
1058
+ * not.
1059
+ */
1060
+ getRowId?: ((row: Row, index: number) => string) | undefined;
1061
+ /**
1062
+ * A class and `data-*` attributes for each data row, worked out from the
1063
+ * row — an `is-overdue` class, a `data-status` to style or find it by.
1064
+ * Group headers and summary rows are not passed to it.
1065
+ *
1066
+ * Called for each data row as it renders, so it should be cheap. A row
1067
+ * whose result reads the same as last time is not redrawn for it, so an
1068
+ * inline arrow returning a fresh object each time is fine.
1069
+ */
1070
+ getRowProps?: ((row: ResolvedRow<Row>) => RowProps) | undefined;
1071
+ /**
1072
+ * Called when a data row is clicked, after the click has changed the
1073
+ * selection — so a handler reading the selection sees the new one. Not
1074
+ * called for a group header, whose click expands or collapses it.
1075
+ */
1076
+ onRowClick?: ((event: RowPointerEvent<Row>) => void) | undefined;
1077
+ /**
1078
+ * Called once when a data row is double-clicked — the usual "open this
1079
+ * record" gesture. The two clicks before it still report through
1080
+ * `onRowClick` and select as clicks do.
1081
+ */
1082
+ onRowDoubleClick?: ((event: RowPointerEvent<Row>) => void) | undefined;
1083
+ borders?: Borders | undefined;
1084
+ hoverable?: HoverableConfig | undefined;
1085
+ /**
1086
+ * Which parts of the grid the user may select, and how many of each —
1087
+ * `{ rows: "multiple", cells: "single" }`. A cell addresses one value, so it
1088
+ * has no `"multiple"`.
1089
+ *
1090
+ * Off by default, unlike `hoverable`: selection claims the click, which a
1091
+ * grid that only displays data should not do. Give `getRowId` alongside it
1092
+ * for data that sorts, filters or pages, or a selection follows the position
1093
+ * rather than the row.
1094
+ */
1095
+ selectable?: SelectableConfig | undefined;
1096
+ /** Row ids selected to start with, keyed as `getRowId` resolves them. Uncontrolled. */
1097
+ defaultRowSelection?: SelectionState | undefined;
1098
+ /**
1099
+ * The selected row ids, controlled — applied as given, changed only through
1100
+ * the row selection callbacks (`onRowSelectionChange` carries the whole
1101
+ * next state). Leave it `undefined` for the grid to keep it itself,
1102
+ * starting from `defaultRowSelection`.
1103
+ */
1104
+ rowSelection?: SelectionState | undefined;
1105
+ /** Column ids selected to start with. Uncontrolled. */
1106
+ defaultColumnSelection?: SelectionState | undefined;
1107
+ /**
1108
+ * The selected column ids, controlled — changed only through the column
1109
+ * selection callbacks. Leave it `undefined` for the grid to keep it.
1110
+ */
1111
+ columnSelection?: SelectionState | undefined;
1112
+ /** The cell selected to start with. Uncontrolled. */
1113
+ defaultCellSelection?: CellSelectionState | undefined;
1114
+ /**
1115
+ * The selected cell, controlled — `null` for none, changed only through
1116
+ * the cell selection callbacks. Leave it `undefined` for the grid to keep
1117
+ * it.
1118
+ */
1119
+ cellSelection?: CellSelectionState | undefined;
1120
+ /** Whether columns can be dragged wider, unless a column says otherwise. */
1121
+ resizableColumns?: boolean | undefined;
1122
+ /**
1123
+ * Whether columns can be dragged into a new position, unless a column says
1124
+ * otherwise. Turning it off leaves an order the user already made in place.
1125
+ */
1126
+ reorderableColumns?: boolean | undefined;
1127
+ /**
1128
+ * Whether columns fill the grid's width or sit at their own. Defaults to
1129
+ * `"fit"`, under which columns the user has sized keep their width and the
1130
+ * rest share what is left.
1131
+ */
1132
+ resizeMode?: ResizeMode | undefined;
1133
+ /** Column widths to start from, keyed by column id. Uncontrolled. */
1134
+ defaultColumnSizing?: ColumnSizingState | undefined;
1135
+ /**
1136
+ * Column widths, controlled — applied as given, changed only through
1137
+ * `onColumnResize`, whose `sizing` is the next state. Leave it
1138
+ * `undefined` for the grid to keep the widths itself, starting from
1139
+ * `defaultColumnSizing`.
1140
+ */
1141
+ columnSizing?: ColumnSizingState | undefined;
1142
+ /**
1143
+ * Column ids in the order to start in. Uncontrolled, and partial: ids it
1144
+ * omits keep their position among `columns` and follow those it lists.
1145
+ */
1146
+ defaultColumnOrder?: ColumnOrderState | undefined;
1147
+ /**
1148
+ * The column order, controlled — applied as given, changed only through
1149
+ * `onColumnOrderChange`. Leave it `undefined` for the grid to keep the
1150
+ * order itself, starting from `defaultColumnOrder`.
1151
+ */
1152
+ columnOrder?: ColumnOrderState | undefined;
1153
+ /**
1154
+ * Columns shown (`true`) or hidden (`false`) to start with, keyed by
1155
+ * column id, over each definition's own `hidden`. Uncontrolled.
1156
+ */
1157
+ defaultColumnVisibility?: ColumnVisibilityState | undefined;
1158
+ /**
1159
+ * Which columns are shown, controlled — applied as given, changed only
1160
+ * through `onColumnVisibilityChange`. Leave it `undefined` for the grid to
1161
+ * keep it itself, starting from `defaultColumnVisibility`.
1162
+ */
1163
+ columnVisibility?: ColumnVisibilityState | undefined;
1164
+ /**
1165
+ * Called once when columns are shown or hidden, with every column that one
1166
+ * change touched. A call that changes nothing does not call it.
1167
+ */
1168
+ onColumnVisibilityChange?: ((event: ColumnVisibilityEvent) => void) | undefined;
1169
+ /**
1170
+ * Sizes applied to columns that do not set their own — the width they start
1171
+ * at and the bounds they may be resized between. Distinct from
1172
+ * `defaultColumnSizing`, which sets specific columns' starting widths.
1173
+ */
1174
+ columnSizeDefaults?: Partial<ColumnSizeDefaults> | undefined;
1175
+ /**
1176
+ * Called as the user resizes a column. Fires continuously with
1177
+ * `phase: "move"` and once on release with `phase: "end"` — the latter being
1178
+ * the one to persist. Auto-fit does not call it; it reports user intent only.
1179
+ */
1180
+ onColumnResize?: ((event: ColumnResizeEvent) => void) | undefined;
1181
+ /**
1182
+ * Called once when the user drops a column somewhere new. A drop that leaves
1183
+ * the order as it was does not call it.
1184
+ */
1185
+ onColumnOrderChange?: ((event: ColumnOrderEvent) => void) | undefined;
1186
+ /**
1187
+ * Whether columns can be sorted by clicking their header toggle, unless a
1188
+ * column says otherwise. Shift-click stacks a column into the sort instead
1189
+ * of replacing it.
1190
+ */
1191
+ sortableColumns?: boolean | undefined;
1192
+ /** The sort to start with, in priority order. Uncontrolled. */
1193
+ defaultColumnSort?: ColumnSortState | undefined;
1194
+ /**
1195
+ * The sort, controlled: the grid shows exactly this, and a header click
1196
+ * only asks for a change through `onColumnSortChange`, which takes effect
1197
+ * once this prop follows it. Leave it `undefined` for the grid to keep the
1198
+ * sort itself, starting from `defaultColumnSort`.
1199
+ */
1200
+ columnSort?: ColumnSortState | undefined;
1201
+ /**
1202
+ * Called once when the user changes the sort — a toggle, a stack, or a
1203
+ * clear back to "none".
1204
+ */
1205
+ onColumnSortChange?: ((event: ColumnSortEvent) => void) | undefined;
1206
+ /**
1207
+ * Whether columns can be grouped by — a header's click/`Alt+ArrowDown`
1208
+ * toggle — unless a column says otherwise. The grouping itself still
1209
+ * applies when this is off, via `defaultGroupBy` or the imperative API;
1210
+ * this only gates that one interaction, the same way `sortableColumns`
1211
+ * gates the sort toggle without disabling `defaultColumnSort`. Independent
1212
+ * of `groupByBarVisibility` and `groupByDraggableColumns`, which gate the
1213
+ * bar and the drag-in gesture respectively.
1214
+ */
1215
+ groupableColumns?: boolean | undefined;
1216
+ /**
1217
+ * Whether a groupable header shows its group-toggle icon, unless a column
1218
+ * says otherwise. Purely a rendering choice: `Alt+ArrowDown` keeps working
1219
+ * on a groupable header with the icon hidden.
1220
+ */
1221
+ groupToggleIconColumns?: boolean | undefined;
1222
+ /**
1223
+ * Whether a column's header may be dragged into the group-by bar to add
1224
+ * it to the grouping, unless a column says otherwise. Independent of
1225
+ * `groupableColumns`: a column can be groupable via its header's
1226
+ * click/keyboard toggle, via this drag, both, or neither.
1227
+ */
1228
+ groupByDraggableColumns?: boolean | undefined;
1229
+ /** How the group-by bar's visibility follows the active grouping. */
1230
+ groupByBarVisibility?: GroupByBarVisibility | undefined;
1231
+ /** The group-by stack to start with, outer to inner. Uncontrolled. */
1232
+ defaultGroupBy?: GroupByState | undefined;
1233
+ /**
1234
+ * The group-by stack, controlled — applied as given, changed only through
1235
+ * `onGroupByChange`. Leave it `undefined` for the grid to keep it itself,
1236
+ * starting from `defaultGroupBy`.
1237
+ */
1238
+ groupBy?: GroupByState | undefined;
1239
+ /**
1240
+ * Called once when the user changes the group-by stack — adding, removing,
1241
+ * or reordering a level.
1242
+ */
1243
+ onGroupByChange?: ((event: GroupByEvent) => void) | undefined;
1244
+ /** Group keys collapsed to start with — every other group starts expanded. Uncontrolled. */
1245
+ defaultGroupExpansion?: GroupExpansionState | undefined;
1246
+ /**
1247
+ * The collapsed groups, controlled — applied as given, changed only
1248
+ * through `onGroupExpansionChange`. Leave it `undefined` for the grid to
1249
+ * keep it itself, starting from `defaultGroupExpansion`.
1250
+ */
1251
+ groupExpansion?: GroupExpansionState | undefined;
1252
+ /** Called once when the user expands or collapses a group, or every group at once. */
1253
+ onGroupExpansionChange?: ((event: GroupExpansionEvent) => void) | undefined;
1254
+ /** The filter to start with — every applied entry, ANDed together. Uncontrolled. */
1255
+ defaultFilter?: FilterState<Row> | undefined;
1256
+ /**
1257
+ * The filter, controlled — applied as given, changed only through
1258
+ * `onFilterChange`. Leave it `undefined` for the grid to keep it itself,
1259
+ * starting from `defaultFilter`.
1260
+ */
1261
+ filter?: FilterState<Row> | undefined;
1262
+ /**
1263
+ * Called once when the filter changes — through `setFilter` for now, as
1264
+ * the grid has no filter UI of its own yet.
1265
+ */
1266
+ onFilterChange?: ((event: FilterChangeEvent<Row>) => void) | undefined;
1267
+ /**
1268
+ * Whether the grid's rows are split into pages. A page's unit is a
1269
+ * top-level group or a bare data row, never a leaf row — a group is never
1270
+ * split across a page boundary. Off by default, matching `sortableColumns`.
1271
+ */
1272
+ paginated?: boolean | undefined;
1273
+ /**
1274
+ * The page and page size to start on. Uncontrolled. Defaults to
1275
+ * `{ pageIndex: 0, pageSize: 25 }` when `paginated` is on and this is
1276
+ * omitted.
1277
+ */
1278
+ defaultPagination?: PaginationState | undefined;
1279
+ /**
1280
+ * The page and page size, controlled — applied as given, changed only
1281
+ * through `onPaginationChange`. Leave it `undefined` for the grid to keep
1282
+ * them itself, starting from `defaultPagination`.
1283
+ *
1284
+ * The grid sends an uncontrolled pagination back to the first page when
1285
+ * the filter, sort or grouping changes; a controlled one is the parent's
1286
+ * to reset alongside that change. Until it does, the grid shows the last
1287
+ * page that exists rather than an empty one.
1288
+ */
1289
+ pagination?: PaginationState | undefined;
1290
+ /** Called once when the user changes the page or the page size. */
1291
+ onPaginationChange?: ((event: PaginationChangeEvent) => void) | undefined;
1292
+ /**
1293
+ * Aggregates to compute — a subtotal per group (rendered in that group's
1294
+ * header) plus a grand total over the whole filtered/grouped dataset
1295
+ * (rendered in a footer). Always computed over every row, never scoped to
1296
+ * the current page. A plain controlled prop, unlike `sort`/`filter`/
1297
+ * `groupBy`/`pagination`: there is no built-in UI for a user to add or
1298
+ * remove an aggregate interactively, so there is no `defaultAggregates`/
1299
+ * `onAggregatesChange` pair to go with it.
1300
+ */
1301
+ aggregates?: AggregateState<Row> | undefined;
1302
+ /**
1303
+ * Where a group's own aggregate results render. `"inline"` (the default)
1304
+ * keeps them as text in the group header, next to its leaf-row count.
1305
+ * `"row"` instead renders a dedicated row after that group's last visible
1306
+ * entry, with each aggregate's value in the `<td>` for its own column —
1307
+ * the same alignment the grand-total footer's own cells have. Has no
1308
+ * effect when `aggregates` is empty or omitted.
1309
+ */
1310
+ groupAggregateDisplay?: GroupAggregateDisplay | undefined;
1311
+ /**
1312
+ * Bounds the row area's height, independently scrollable — the grid's own
1313
+ * chrome (group-by bar, header, footer, pager) stays outside it and
1314
+ * always visible. A number is pixels; a string is passed through as a CSS
1315
+ * length (e.g. "60vh"). Omitted, the body's height stays content-driven,
1316
+ * same as today.
1317
+ */
1318
+ height?: number | string | undefined;
1319
+ /**
1320
+ * Renders only the rows near the current scroll position rather than every
1321
+ * row at once — for a dataset too large to mount in full. Requires
1322
+ * `height` to be set; a grid without a bounded body has no viewport to
1323
+ * window rows against (a dev-time console warning fires when it's
1324
+ * missing). Off by default, matching every other opt-in behavior here.
1325
+ */
1326
+ virtualized?: boolean | undefined;
1327
+ /**
1328
+ * Rows rendered outside the visible range on each side, to reduce
1329
+ * blank-frame flashes on fast scrolling. Only meaningful with `virtualized`
1330
+ * on.
1331
+ */
1332
+ overscan?: number | undefined;
1333
+ /**
1334
+ * Assumed height for a row never yet measured — only matters for the very
1335
+ * first paint and for `scrollToRow`'s initial jump before it corrects
1336
+ * itself against that row's real, measured height. Only meaningful with
1337
+ * `virtualized` on.
1338
+ */
1339
+ estimatedRowHeight?: number | undefined;
1340
+ /**
1341
+ * The grid's accessible name, announced when it takes focus. A grid without
1342
+ * one is read only as "grid", which says nothing about which grid.
1343
+ */
1344
+ label?: string | undefined;
1345
+ /**
1346
+ * The id of an element naming the grid, for a heading already on the page.
1347
+ * Takes precedence over `label`, as `aria-labelledby` does.
1348
+ */
1349
+ labelledBy?: string | undefined;
1350
+ /**
1351
+ * The locale values are formatted for — a BCP 47 tag such as `"de-DE"`, or
1352
+ * a list to fall back through. Setting it turns on `Intl` formatting of
1353
+ * dates and numbers in every cell and aggregate shown without a template;
1354
+ * without it, only a column with its own `format` is formatted, in the
1355
+ * runtime's locale, and the rest print as they always have.
1356
+ */
1357
+ locale?: FormatLocale;
1358
+ /**
1359
+ * The grid's own strings, replacing its English key by key — the pager's
1360
+ * buttons, the group-by bar, the loading row, boolean and blank values,
1361
+ * and every live-region announcement. Keys left out keep their default.
1362
+ * Formatting is `locale`'s, not this.
1363
+ */
1364
+ messages?: Partial<GridMessages> | undefined;
1365
+ }
1366
+ /**
1367
+ * Reported as the user selects. Every one of these is fanned out from a single
1368
+ * diff of the transition it describes, so no two can disagree about what one
1369
+ * interaction did.
1370
+ *
1371
+ * Kept apart from `GridOptions` because the column payloads carry resolved
1372
+ * columns, whose labels are whatever a renderer's `headerTemplate` returns —
1373
+ * `Node` is that renderer's node type.
1374
+ */
1375
+ interface SelectionCallbacks<Row, Node = string> {
1376
+ /** Called once per row newly selected. The last call names the last row. */
1377
+ onRowSelect?: ((event: RowSelectEvent<Row>) => void) | undefined;
1378
+ /** Called once with every row one interaction selected. */
1379
+ onRowsSelect?: ((event: RowsSelectEvent<Row>) => void) | undefined;
1380
+ onRowDeselect?: ((event: RowSelectEvent<Row>) => void) | undefined;
1381
+ onRowsDeselect?: ((event: RowsSelectEvent<Row>) => void) | undefined;
1382
+ /**
1383
+ * Called once per change with what it added, what it removed and everything
1384
+ * selected after it — the one to persist from.
1385
+ */
1386
+ onRowSelectionChange?: ((event: RowSelectionChangeEvent<Row>) => void) | undefined;
1387
+ onColumnSelect?: ((event: ColumnSelectEvent<Row, Node>) => void) | undefined;
1388
+ onColumnsSelect?: ((event: ColumnsSelectEvent<Row, Node>) => void) | undefined;
1389
+ onColumnDeselect?: ((event: ColumnSelectEvent<Row, Node>) => void) | undefined;
1390
+ onColumnsDeselect?: ((event: ColumnsSelectEvent<Row, Node>) => void) | undefined;
1391
+ onColumnSelectionChange?: ((event: ColumnSelectionChangeEvent<Row, Node>) => void) | undefined;
1392
+ onCellSelect?: ((event: CellSelectEvent<Row, Node>) => void) | undefined;
1393
+ onCellDeselect?: ((event: CellSelectEvent<Row, Node>) => void) | undefined;
1394
+ onCellSelectionChange?: ((event: CellSelectionChangeEvent<Row, Node>) => void) | undefined;
1395
+ }
1396
+
1397
+ /**
1398
+ * A row or column selection change by id — see
1399
+ * `GridControllerOptions.onSelectionDiff`.
1400
+ */
1401
+ interface SelectionDiffEvent extends SelectionDiff {
1402
+ /** Which selection changed. */
1403
+ readonly target: "rows" | "columns";
1404
+ /** The whole selection after the change. */
1405
+ readonly selection: SelectionState;
1406
+ }
1407
+ /**
1408
+ * What `createGridController` is given: the grid's options and selection
1409
+ * callbacks, plus what a renderer supplies that is not a setting — the column
1410
+ * definitions, whose templates name the renderer's own node type, the width
1411
+ * the viewport measured, and a hook for its own selection feedback.
1412
+ */
1413
+ interface GridControllerOptions<Row, Node = string> extends GridOptions<Row>, SelectionCallbacks<Row, Node> {
1414
+ /**
1415
+ * The columns to show. Omitted or empty, one column is inferred per field
1416
+ * of `dataSource` — see `defineColumnsFromRows`.
1417
+ */
1418
+ columns?: readonly ColumnDefinition<Row, Node>[] | undefined;
1419
+ /**
1420
+ * The width, in px, `resizeMode: "fit"` fills. `null` or absent before it
1421
+ * has been measured, or where there is no viewport at all (an export in
1422
+ * Node), and then columns keep their resolved widths.
1423
+ */
1424
+ viewportWidth?: number | null | undefined;
1425
+ /**
1426
+ * Called once per row or column selection change, after the selection
1427
+ * callbacks, with the change by id — every id it added or removed,
1428
+ * including those with no row on screen (on another page, in a collapsed
1429
+ * group, filtered out), which the callbacks' resolved rows leave out. For a
1430
+ * renderer's own feedback, such as a live region announcing how many rows
1431
+ * are selected; an application reads the selection callbacks instead.
1432
+ */
1433
+ onSelectionDiff?: ((event: SelectionDiffEvent) => void) | undefined;
1434
+ }
1435
+ /**
1436
+ * A grid without a renderer: its state, the view derived from it, and every
1437
+ * action that changes it. A renderer creates one per grid, hands it the
1438
+ * current options on each update, draws `getView()` and calls the actions
1439
+ * from its event handlers, so no renderer decides anything the user can see.
1440
+ *
1441
+ * Each slice of `GridState` is controlled or not on its own, the way a form
1442
+ * input is: given as an option (`columnSort`), the grid shows that value and
1443
+ * an action only asks for a change through the slice's callback
1444
+ * (`onColumnSortChange`); left out, the controller keeps the slice itself,
1445
+ * starting from its default (`defaultColumnSort`).
1446
+ *
1447
+ * Every action follows the same steps: work out the next value; stop if it
1448
+ * is the very object the slice already holds; keep it, unless the slice is
1449
+ * controlled; report it through the slice's callback; then call every
1450
+ * subscriber, synchronously. The setters (`setColumnSort` and the like) also
1451
+ * stop for a value equal to the current one, so restoring what is already
1452
+ * there reports nothing.
1453
+ *
1454
+ * Every member is a plain function bound to its grid, safe to hand around on
1455
+ * its own — `onClick={controller.nextPage}`.
1456
+ */
1457
+ interface GridController<Row, Node = string> {
1458
+ readonly getOptions: () => GridControllerOptions<Row, Node>;
1459
+ /**
1460
+ * Replaces the options, as a renderer does with its current props on every
1461
+ * update; calling it again with equal options changes nothing. A controlled
1462
+ * slice takes its new value from here.
1463
+ *
1464
+ * Does not call subscribers: a renderer calls this while it updates, and
1465
+ * draws the result itself — a subscriber called mid-update would update
1466
+ * mid-update too. A renderer that also tells other listeners about it does
1467
+ * so once its update is done.
1468
+ */
1469
+ readonly setOptions: (options: GridControllerOptions<Row, Node>) => void;
1470
+ /** Every slice as the grid now applies it. The same object until a slice changes. */
1471
+ readonly getState: () => GridState<Row>;
1472
+ /** What the user is looking at — see `resolveGridView`. The same object until something in it changes. */
1473
+ readonly getView: () => GridView<Row, Node>;
1474
+ /**
1475
+ * Calls `listener` after each action that got as far as reporting a
1476
+ * change. Returns the function that unsubscribes it.
1477
+ */
1478
+ readonly subscribe: (listener: () => void) => () => void;
1479
+ /**
1480
+ * Puts back state saved earlier — from `getState()`, or from
1481
+ * `serializeGridState` and back through JSON. Every slice given is set as
1482
+ * its own `set*` action would set it, so each that changes fires its
1483
+ * callback and a controlled one only asks; slices left out keep their value.
1484
+ * Subscribers are called once, after every slice is in place, and a
1485
+ * restored pagination is kept even when the same call changed the sort,
1486
+ * filter or grouping that would otherwise send it back to the first page.
1487
+ */
1488
+ readonly restoreState: (saved: Partial<GridState<Row>> | Partial<SerializedGridState<Row>>) => void;
1489
+ /**
1490
+ * Replaces the column widths. Reported through `onColumnResize` as the
1491
+ * `"end"` of a resize of the first column whose width changed, at its new
1492
+ * width — or, when its entry was removed, the width its definition gives
1493
+ * it. Equal widths report nothing.
1494
+ */
1495
+ readonly setColumnSizing: (sizing: ColumnSizingState) => void;
1496
+ /**
1497
+ * Sets one column's width, as a resize gesture does: `"move"` while the
1498
+ * gesture lasts, `"end"` once it is over. Never skipped for an unchanged
1499
+ * width, so a drag held against a column's minimum still reports every
1500
+ * move.
1501
+ */
1502
+ readonly resizeColumn: (columnId: string, width: number, phase: "move" | "end") => void;
1503
+ /**
1504
+ * Ends a resize gesture by putting its column back as it was when the
1505
+ * gesture began — `startSizing` is the sizing state then, `startWidth` the
1506
+ * width the column was drawn at — keeping any other column's change made in
1507
+ * the meantime. Reported as the gesture's `"end"`, at `startWidth`.
1508
+ */
1509
+ readonly cancelColumnResize: (columnId: string, startSizing: ColumnSizingState, startWidth: number) => void;
1510
+ /**
1511
+ * Replaces the column order. Reported through `onColumnOrderChange` naming
1512
+ * the first column whose position changed; an equal order reports nothing.
1513
+ */
1514
+ readonly setColumnOrder: (order: ColumnOrderState) => void;
1515
+ /**
1516
+ * Moves a column in front of `beforeId`, or to the end when that is
1517
+ * `null`, among every column — hidden ones keep their place. A move that
1518
+ * changes nothing reports nothing.
1519
+ */
1520
+ readonly moveColumn: (columnId: string, beforeId: string | null) => void;
1521
+ /**
1522
+ * Replaces the column visibility state. Reported through
1523
+ * `onColumnVisibilityChange` with every column whose entry changed; an
1524
+ * equal state reports nothing.
1525
+ */
1526
+ readonly setColumnVisibility: (visibility: ColumnVisibilityState) => void;
1527
+ /** Shows the given columns, each in the place it held when hidden. One report for the whole call. */
1528
+ readonly showColumns: (columnIds: readonly string[]) => void;
1529
+ /** Hides the given columns. A hidden column still sorts, filters and groups. */
1530
+ readonly hideColumns: (columnIds: readonly string[]) => void;
1531
+ /**
1532
+ * Replaces the filter. Reported through `onFilterChange`; a filter equal to
1533
+ * the current one — the same entries in the same order, a `Date` value
1534
+ * compared by time and a predicate by identity — reports nothing.
1535
+ */
1536
+ readonly setFilter: (filter: FilterState<Row>) => void;
1537
+ /**
1538
+ * Replaces the selected row ids as given, whatever `selectable` allows —
1539
+ * the way a controlled or default value is applied. The same ids in the
1540
+ * same order report nothing.
1541
+ */
1542
+ readonly setRowSelection: (selection: SelectionState) => void;
1543
+ /**
1544
+ * Selects rows as a user could, within what `selectable.rows` allows:
1545
+ * `"replace"` (the default) selects exactly these — only the last of them
1546
+ * under `"single"` — and `"toggle"` flips each one. An id need not name a
1547
+ * row on screen. A call that leaves the selection as it was reports
1548
+ * nothing.
1549
+ */
1550
+ readonly selectRows: (rowIds: readonly string[], intent?: "replace" | "toggle") => void;
1551
+ /**
1552
+ * Applies one click or key press to a row: `"replace"`, `"toggle"` or
1553
+ * `"range"` from the last row selected by anything but a range, across
1554
+ * the data rows on the current page.
1555
+ */
1556
+ readonly selectRow: (rowId: string, intent: SelectIntent) => void;
1557
+ /** Selects every data row on the current page, when `selectable.rows` is `"multiple"`. */
1558
+ readonly selectAllRows: () => void;
1559
+ /** Lets go of every row, column and cell selection at once, as Escape does. */
1560
+ readonly clearSelection: () => void;
1561
+ /** Replaces the selected column ids as given — see `setRowSelection`. */
1562
+ readonly setColumnSelection: (selection: SelectionState) => void;
1563
+ /** `selectRow`, for a column among the columns as rendered. */
1564
+ readonly selectColumn: (columnId: string, intent: SelectIntent) => void;
1565
+ /** Replaces the selected cell as given, `null` for none. */
1566
+ readonly setCellSelection: (selection: CellSelectionState) => void;
1567
+ /**
1568
+ * Applies one click or key press to a cell: `"toggle"` lets go of it when
1569
+ * it is the one selected; anything else selects it. A cell holds one value,
1570
+ * so a range over cells reads as a plain click.
1571
+ */
1572
+ readonly selectCell: (cell: SelectedCellRef, intent: SelectIntent) => void;
1573
+ /**
1574
+ * Replaces the sort. Reported through `onColumnSortChange` naming the
1575
+ * first column whose place or direction changed; a sort equal to the
1576
+ * current one changes nothing and reports nothing.
1577
+ */
1578
+ readonly setColumnSort: (sort: ColumnSortState) => void;
1579
+ /**
1580
+ * Cycles a column's sort, as its header toggle does: ascending, descending,
1581
+ * off. `stack` adds it to the sort rather than replacing the sort with it.
1582
+ */
1583
+ readonly toggleColumnSort: (columnId: string, options?: {
1584
+ readonly stack?: boolean | undefined;
1585
+ }) => void;
1586
+ /**
1587
+ * Replaces the group-by stack. Reported through `onGroupByChange` naming
1588
+ * the first level that changed; an equal stack reports nothing.
1589
+ */
1590
+ readonly setGroupBy: (groupBy: GroupByState) => void;
1591
+ /** Adds a column to the end of the group-by stack, or removes it if it is already there. */
1592
+ readonly toggleGroupBy: (columnId: string) => void;
1593
+ /**
1594
+ * Puts a column into the group-by stack in front of `beforeColumnId`, or at
1595
+ * the end when that is `null` — adding it, or moving it if it is already
1596
+ * there.
1597
+ */
1598
+ readonly moveGroupBy: (columnId: string, beforeColumnId: string | null) => void;
1599
+ /**
1600
+ * Replaces the collapsed groups. Reported with `groupId: null`, as a change
1601
+ * to every group at once; the same ids in the same order report nothing.
1602
+ */
1603
+ readonly setGroupExpansion: (expansion: GroupExpansionState) => void;
1604
+ readonly toggleGroupExpansion: (groupId: string) => void;
1605
+ readonly expandAllGroups: () => void;
1606
+ /** Collapses every group currently shown — see `collapseAllGroups`. */
1607
+ readonly collapseAllGroups: () => void;
1608
+ /**
1609
+ * Replaces the page and page size as given; the view clamps a page past
1610
+ * the last one. An equal pagination reports nothing.
1611
+ */
1612
+ readonly setPagination: (pagination: PaginationState) => void;
1613
+ /** Moves to the given page, clamped into range. */
1614
+ readonly goToPage: (pageIndex: number) => void;
1615
+ readonly nextPage: () => void;
1616
+ readonly previousPage: () => void;
1617
+ /** Changes the page size and goes back to the first page. */
1618
+ readonly setPageSize: (pageSize: number) => void;
1619
+ }
1620
+
1621
+ /**
1622
+ * Creates the state container for one grid — see `GridController`. Hold one
1623
+ * per grid for its whole life: it keeps the uncontrolled slices, and its view
1624
+ * is memoised across calls the way `createGridViewResolver`'s is.
1625
+ */
1626
+ declare function createGridController<Row, Node = string>(initial: GridControllerOptions<Row, Node>): GridController<Row, Node>;
642
1627
 
643
1628
  /**
644
1629
  * How a column of `type` aligns by default. Numeric types align right so that
@@ -654,8 +1639,11 @@ declare function alignmentForType(type: ColumnType): ColumnAlignment;
654
1639
  *
655
1640
  * Order follows `Object.entries`, which lists integer-like keys first
656
1641
  * regardless of the order they were written in.
1642
+ *
1643
+ * `Node` is whatever the caller's renderer draws: an inferred column sets no
1644
+ * template, so it suits any.
657
1645
  */
658
- declare function defineColumnsFromRows<Row>(rows: readonly Row[]): readonly ColumnDefinition<Row>[];
1646
+ declare function defineColumnsFromRows<Row, Node = string>(rows: readonly Row[]): readonly ColumnDefinition<Row, Node>[];
659
1647
  /**
660
1648
  * The key state about a column is stored under, and the key it renders with.
661
1649
  * Every such lookup goes through here so the two can never disagree.
@@ -674,10 +1662,18 @@ declare function getColumnId<Row>(column: ColumnDefinition<Row, unknown>): strin
674
1662
  declare function resolveRowId<Row>(row: Row, index: number, getRowId?: (row: Row, index: number) => string): string;
675
1663
  /**
676
1664
  * What a column's header shows: its own `headerTemplate`, called first if it is
677
- * a function, or else a label read off the field path — `"Application.Id"`
678
- * reading as `"Application Id"`.
1665
+ * a function, or else its plain-text `header`, or else a label read off the
1666
+ * field path.
679
1667
  */
680
1668
  declare function resolveColumnLabel<Row, Node>(column: ColumnDefinition<Row, Node>): Node | string;
1669
+ /**
1670
+ * A column's name as text, for wherever markup will not do — an
1671
+ * announcement, an export. Its `header` first, even beside a
1672
+ * `headerTemplate`, since a template's output need not be text; then the
1673
+ * label `resolveColumnLabel` gives, when that is a string; then the label
1674
+ * read off the field path.
1675
+ */
1676
+ declare function resolveColumnHeaderText<Row>(column: ColumnDefinition<Row, unknown>): string;
681
1677
  /** Which of a column header's keyboard interactions to announce. */
682
1678
  interface KeyShortcutCapabilities {
683
1679
  reorderable: boolean;
@@ -751,6 +1747,14 @@ declare function nextFocusForKey(key: string, modifiers: NavigationModifiers, fo
751
1747
  * was made against.
752
1748
  */
753
1749
  declare function applyColumnOrder<Row, Node>(columns: readonly ColumnDefinition<Row, Node>[], order: ColumnOrderState): readonly ColumnDefinition<Row, Node>[];
1750
+ /**
1751
+ * `columns` in the order `order` gives, each paired with its id — the
1752
+ * width-free column list the data stages (filter, sort, group, aggregate)
1753
+ * read. Ordered here, ahead of sizing, so every later stage sees the one
1754
+ * already-ordered list and no part of the grid has to know a reorder
1755
+ * happened.
1756
+ */
1757
+ declare function resolveDataColumns<Row, Node>(columns: readonly ColumnDefinition<Row, Node>[], order: ColumnOrderState): readonly DataColumn<Row, Node>[];
754
1758
  /**
755
1759
  * The order with `movedId` lifted out and put back in front of `beforeId`, or
756
1760
  * at the end when that is `null`.
@@ -840,11 +1844,19 @@ declare function selectCell(selection: CellSelectionState, cell: SelectedCellRef
840
1844
  * Ctrl-click, matching `toggleSelection`.
841
1845
  */
842
1846
  declare function toggleCellSelection(selection: CellSelectionState, cell: SelectedCellRef, mode: CellSelectionMode): CellSelectionState;
843
- /** The intent a click or key press carries, by the modifiers held with it. */
1847
+ /**
1848
+ * The intent a click or key press carries, by the modifiers held with it.
1849
+ *
1850
+ * `toggleOnClick` makes the unmodified press a toggle — the
1851
+ * `selectable.toggleOnClick` checklist behaviour — leaving Shift and Ctrl
1852
+ * to mean what they always do.
1853
+ */
844
1854
  declare function intentOf(event: {
845
1855
  ctrlKey: boolean;
846
1856
  metaKey: boolean;
847
1857
  shiftKey: boolean;
1858
+ }, options?: {
1859
+ toggleOnClick?: boolean | undefined;
848
1860
  }): SelectIntent;
849
1861
  /**
850
1862
  * The transform an intent asks for. A function rather than a branch inside
@@ -970,7 +1982,7 @@ declare function toggleColumnSort(sort: ColumnSortState, columnId: string, optio
970
1982
  * columns absent from `columns` — a stored sort tolerates a column removed
971
1983
  * from the definition the same way a stored `ColumnOrderState` does.
972
1984
  */
973
- declare function sortRows<Row>(rows: readonly ResolvedRow<Row>[], sort: ColumnSortState, columns: readonly ResolvedColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
1985
+ declare function sortRows<Row>(rows: readonly ResolvedRow<Row>[], sort: ColumnSortState, columns: readonly DataColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
974
1986
 
975
1987
  /**
976
1988
  * Whether `value`, stringified, matches `query`'s SQL LIKE-style pattern —
@@ -987,8 +1999,8 @@ declare function matchesQuery(value: unknown, query: string): boolean;
987
1999
  * This column's current text query, or the global entry's when `columnId`
988
2000
  * is omitted; `null` if no `TextFilterEntry` names it — including when the
989
2001
  * entry that exists for that key is a `ValueFilterEntry`/
990
- * `PredicateFilterEntry`/`GroupFilterEntry` instead, since none of those
991
- * hold a query string to report.
2002
+ * `PredicateFilterEntry`/`OperatorFilterEntry`/`GroupFilterEntry` instead,
2003
+ * since none of those hold a query string to report.
992
2004
  */
993
2005
  declare function filterQueryFor<Row>(filter: FilterState<Row>, columnId?: FieldPath<Row> | (string & {})): string | null;
994
2006
  /**
@@ -1022,7 +2034,7 @@ declare function clearAllFilters<Row>(filter: FilterState<Row>): FilterState<Row
1022
2034
  * columns absent from `columns` — a stored filter tolerates a column
1023
2035
  * removed from the definition the same way a stored `ColumnSortState` does.
1024
2036
  */
1025
- declare function filterRows<Row>(rows: readonly ResolvedRow<Row>[], filter: FilterState<Row>, columns: readonly ResolvedColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
2037
+ declare function filterRows<Row>(rows: readonly ResolvedRow<Row>[], filter: FilterState<Row>, columns: readonly DataColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
1026
2038
 
1027
2039
  /**
1028
2040
  * This group's id, from its path. Every group-id producer/consumer in this
@@ -1055,7 +2067,7 @@ declare function groupRowId(path: readonly unknown[]): string;
1055
2067
  * columns absent from `columns` — a stored grouping tolerates a column
1056
2068
  * removed from the definition the same way a stored `ColumnSortState` does.
1057
2069
  */
1058
- declare function groupRows<Row>(rows: readonly ResolvedRow<Row>[], groupBy: GroupByState, expansion: GroupExpansionState, columns: readonly ResolvedColumn<Row, unknown>[]): readonly DisplayRow<Row>[];
2070
+ declare function groupRows<Row>(rows: readonly ResolvedRow<Row>[], groupBy: GroupByState, expansion: GroupExpansionState, columns: readonly DataColumn<Row, unknown>[]): readonly DisplayRow<Row>[];
1059
2071
  /**
1060
2072
  * The expansion with `groupId` collapsed if it was expanded, or expanded if
1061
2073
  * it was collapsed.
@@ -1070,6 +2082,9 @@ declare function expandAllGroups(expansion: GroupExpansionState): GroupExpansion
1070
2082
  * absent from `rows`) keeps whatever expansion it already had rather than
1071
2083
  * being forced closed too; expanding that ancestor again reveals it exactly
1072
2084
  * as it was left.
2085
+ *
2086
+ * Group headers only: a summary row (`groupAggregateDisplay: "row"`) carries
2087
+ * its group's id too, and counting it would list that group twice.
1073
2088
  */
1074
2089
  declare function collapseAllGroups<Row>(rows: readonly DisplayRow<Row>[]): GroupExpansionState;
1075
2090
  /**
@@ -1111,7 +2126,7 @@ declare function movesGroupBy(groupBy: GroupByState, columnId: string, beforeCol
1111
2126
  * order-preserving predicate and sort is a stable total order over whatever
1112
2127
  * it's handed, so the final order is identical either way.
1113
2128
  */
1114
- declare function resolveShownRows<Row>(rows: readonly ResolvedRow<Row>[], filter: FilterState<Row>, sort: ColumnSortState, columns: readonly ResolvedColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
2129
+ declare function resolveShownRows<Row>(rows: readonly ResolvedRow<Row>[], filter: FilterState<Row>, sort: ColumnSortState, columns: readonly DataColumn<Row, unknown>[]): readonly ResolvedRow<Row>[];
1115
2130
  /**
1116
2131
  * `ids` resolved back to the rows behind them, keyed by `rowsById`.
1117
2132
  *
@@ -1187,8 +2202,223 @@ declare function paginationWindow(currentPage: number, pageCount: number, option
1187
2202
  * shared singleton — matching this codebase's general avoidance of shared
1188
2203
  * mutable module state.
1189
2204
  */
1190
- declare function computeAggregates<Row>(rows: readonly Row[], specs: AggregateState<Row>, columns: readonly ResolvedColumn<Row, unknown>[]): AggregateResults;
1191
- declare function withGroupAggregates<Row>(displayRows: readonly DisplayRow<Row>[], flatRows: readonly ResolvedRow<Row>[], groupBy: GroupByState, specs: AggregateState<Row>, columns: readonly ResolvedColumn<Row, unknown>[], display?: GroupAggregateDisplay): readonly DisplayRow<Row>[];
2205
+ declare function computeAggregates<Row>(rows: readonly Row[], specs: AggregateState<Row>, columns: readonly DataColumn<Row, unknown>[]): AggregateResults;
2206
+ declare function withGroupAggregates<Row>(displayRows: readonly DisplayRow<Row>[], flatRows: readonly ResolvedRow<Row>[], groupBy: GroupByState, specs: AggregateState<Row>, columns: readonly DataColumn<Row, unknown>[], display?: GroupAggregateDisplay): readonly DisplayRow<Row>[];
2207
+
2208
+ /**
2209
+ * Whether `column` is shown: what the user last chose for it, if anything,
2210
+ * else what its definition says. The one place that precedence is decided,
2211
+ * so the view and an imperative "is it showing?" cannot disagree.
2212
+ */
2213
+ declare function isColumnVisible<Row>(column: ColumnDefinition<Row, unknown>, visibility: ColumnVisibilityState): boolean;
2214
+ /**
2215
+ * `columns` without the hidden ones, in the same order. `columns` itself when
2216
+ * every column is shown — the common case — so a stage memoised on this
2217
+ * output sees nothing change.
2218
+ */
2219
+ declare function applyColumnVisibility<Row, Node>(columns: readonly DataColumn<Row, Node>[], visibility: ColumnVisibilityState): readonly DataColumn<Row, Node>[];
2220
+ /**
2221
+ * `visibility` with each of `columnIds` shown (`visible: true`) or hidden.
2222
+ * Only a column whose visibility actually changes gets an entry, and ids
2223
+ * naming no column are ignored, so asking to show what is already showing
2224
+ * hands back `visibility` itself — the same reference, for a caller deciding
2225
+ * whether anything happened.
2226
+ */
2227
+ declare function setColumnVisibility<Row>(columns: readonly DataColumn<Row, unknown>[], visibility: ColumnVisibilityState, columnIds: readonly string[], visible: boolean): ColumnVisibilityState;
2228
+
2229
+ /**
2230
+ * `resolveGridView`, memoised per stage: each stage remembers its last
2231
+ * inputs and returns its last output when none of them changed identity, so
2232
+ * a call that changes one slice of state recomputes only what depends on
2233
+ * it. The data stages (filter → sort → group → aggregate → paginate) read
2234
+ * `dataColumns`, which carry no width, so a resize or a viewport change
2235
+ * leaves `rows`, `displayRows`, `pageRows`, `displayDataRows` and
2236
+ * `grandTotal` reference-equal to the previous call's — only `columns` is
2237
+ * new. The view object itself is reused when every field is unchanged, so a
2238
+ * snapshot-based renderer sees nothing to re-render.
2239
+ *
2240
+ * Hold one resolver per grid and call it with the current input every time
2241
+ * the view is needed; the memo only pays off across calls to the same one.
2242
+ */
2243
+ declare function createGridViewResolver<Row, Node = string>(): (input: GridViewInput<Row, Node>) => GridView<Row, Node>;
2244
+ /**
2245
+ * What the user is looking at, derived from the rows, the columns, the
2246
+ * grid's state and its options in one call: rows filtered → sorted →
2247
+ * grouped → aggregated → paginated, and columns ordered → hidden → sized.
2248
+ * The one function a renderer, an export or a toolbar calls instead of
2249
+ * composing the stages itself, so none of them can disagree about what is
2250
+ * shown.
2251
+ *
2252
+ * Unmemoised — a one-off resolver. A renderer that derives the view on every
2253
+ * change holds a `createGridViewResolver()` instead.
2254
+ */
2255
+ declare function resolveGridView<Row, Node = string>(input: GridViewInput<Row, Node>): GridView<Row, Node>;
2256
+
2257
+ /**
2258
+ * `state` in a form that survives `JSON.stringify` and back unchanged, for
2259
+ * saving a layout — to local storage, a URL, a user's profile — and handing
2260
+ * it to `restoreState` later. Every slice is plain data already except the
2261
+ * filter: a `Date` value or operand is written as its ISO string, and a
2262
+ * predicate entry, being a function, cannot be written at all and is left
2263
+ * out with a `console.error` naming its column.
2264
+ */
2265
+ declare function serializeGridState<Row>(state: GridState<Row>): SerializedGridState<Row>;
2266
+
2267
+ /**
2268
+ * What one column's filter looks like to an editor — which of its forms can
2269
+ * show it, or that none can.
2270
+ *
2271
+ * - `"none"`: no entry touches the column.
2272
+ * - `"values"`: a checklist — one `in` entry, optionally OR-grouped with an
2273
+ * `isEmpty` on the same column (`includeBlanks`).
2274
+ * - `"conditions"`: one or two operator entries on the column (any but
2275
+ * `in`), at the top level (ANDed) or alone in one group with its
2276
+ * combinator.
2277
+ * - `"custom"`: anything else that touches the column — a text query, a
2278
+ * predicate, a group spanning several columns. An editor shows it as
2279
+ * such, and applying an editor's draft replaces it.
2280
+ */
2281
+ type ColumnFilterShape<Row> = {
2282
+ readonly kind: "none";
2283
+ } | {
2284
+ readonly kind: "values";
2285
+ readonly values: readonly FilterScalar[];
2286
+ readonly includeBlanks: boolean;
2287
+ } | {
2288
+ readonly kind: "conditions";
2289
+ readonly conditions: readonly OperatorFilterEntry<Row>[];
2290
+ readonly combinator: "and" | "or";
2291
+ } | {
2292
+ readonly kind: "custom";
2293
+ };
2294
+ /** One distinct value a column holds, as a checklist offers it. */
2295
+ interface ColumnValueOption {
2296
+ readonly value: Exclude<FilterScalar, null>;
2297
+ /** The value as the column prints it — what a search matches against. */
2298
+ readonly label: string;
2299
+ /** How many rows hold it, among the rows the other columns' filters keep. */
2300
+ readonly count: number;
2301
+ }
2302
+ /** What `columnValueOptions` narrows its list by. */
2303
+ interface ColumnValueQuery {
2304
+ /** Keeps the values whose label contains this, ignoring case. */
2305
+ readonly search?: string;
2306
+ /** Keeps at most this many values, first in sorted order. */
2307
+ readonly limit?: number;
2308
+ /** The locale labels are formatted for — the grid's own `locale`. */
2309
+ readonly locale?: FormatLocale;
2310
+ }
2311
+ interface ColumnValueOptions {
2312
+ /** The distinct values, sorted as the column sorts, searched and cut to `limit`. */
2313
+ readonly options: readonly ColumnValueOption[];
2314
+ /** How many values matched the search, before `limit` cut them. */
2315
+ readonly total: number;
2316
+ /** Whether `limit` left values out. */
2317
+ readonly truncated: boolean;
2318
+ /** How many of the same rows hold a blank — `null`, `undefined` or `""`. */
2319
+ readonly blanks: number;
2320
+ }
2321
+ /**
2322
+ * A checklist or condition editor's working copy of one column's filter,
2323
+ * changed by `filterEditorReducer` and turned into a filter by
2324
+ * `draftToFilter` only when the user applies it — so cancelling is just
2325
+ * dropping the draft.
2326
+ */
2327
+ interface FilterEditorDraft<Row> {
2328
+ /** Which half of the draft `draftToFilter` applies: the last one changed. */
2329
+ readonly mode: "values" | "conditions";
2330
+ /** Every value the editor offers, from `columnValueOptions`. */
2331
+ readonly options: readonly ColumnValueOption[];
2332
+ /** How many rows hold a blank; the blanks entry only matters when some do. */
2333
+ readonly blanks: number;
2334
+ readonly search: string;
2335
+ /** The checked values, keyed as grouping keys them. */
2336
+ readonly checked: ReadonlyMap<string, FilterScalar>;
2337
+ readonly includeBlanks: boolean;
2338
+ readonly conditions: readonly OperatorFilterEntry<Row>[];
2339
+ readonly combinator: "and" | "or";
2340
+ }
2341
+ type FilterEditorAction<Row> = {
2342
+ readonly type: "search";
2343
+ readonly search: string;
2344
+ } | {
2345
+ readonly type: "toggleValue";
2346
+ readonly value: FilterScalar;
2347
+ }
2348
+ /** Checks every value the search shows, or unchecks them once all are. */
2349
+ | {
2350
+ readonly type: "toggleAll";
2351
+ } | {
2352
+ readonly type: "setIncludeBlanks";
2353
+ readonly includeBlanks: boolean;
2354
+ } | {
2355
+ readonly type: "setConditions";
2356
+ readonly conditions: readonly OperatorFilterEntry<Row>[];
2357
+ /** Keeps the draft's own when omitted. */
2358
+ readonly combinator?: "and" | "or";
2359
+ }
2360
+ /** Back to "no filter": every value checked, no conditions, no search. */
2361
+ | {
2362
+ readonly type: "clear";
2363
+ };
2364
+ /** A select-all checkbox's state over the values the search shows. */
2365
+ type SelectAllState = "checked" | "unchecked" | "mixed";
2366
+ /**
2367
+ * How `filter` filters `columnId`, as one of the forms an editor can show —
2368
+ * see `ColumnFilterShape`. Entries about other columns don't count, nor do
2369
+ * entries across every column (a global search).
2370
+ */
2371
+ declare function describeColumnFilter<Row>(filter: FilterState<Row>, columnId: string): ColumnFilterShape<Row>;
2372
+ /**
2373
+ * The distinct values `columnId` holds, for a checklist editor: each with
2374
+ * its label and how many rows hold it.
2375
+ *
2376
+ * Counted over the rows every *other* column's filter keeps — every entry
2377
+ * except those `describeColumnFilter` would count for this column — so a
2378
+ * column's list cascades from the other filters without its own pick
2379
+ * hiding the values the user unchecked. Values are told apart as grouping
2380
+ * tells them apart, ordered as the column sorts, labelled as its cells
2381
+ * print; blanks are counted apart, in `blanks`.
2382
+ *
2383
+ * Costs one pass over `rows` (tens of milliseconds at 100k rows), so compute
2384
+ * it when an editor opens rather than on every render, and hand the whole
2385
+ * list to `initialFilterEditorDraft`, whose search needs no recount.
2386
+ */
2387
+ declare function columnValueOptions<Row>(rows: readonly Row[], dataColumns: readonly DataColumn<Row, unknown>[], filter: FilterState<Row>, columnId: string, query?: ColumnValueQuery): ColumnValueOptions;
2388
+ /**
2389
+ * A draft showing `shape` over `values` — what an editor opens with. A
2390
+ * column with no filter, or one only a custom editor could show, opens with
2391
+ * every value checked; a checklist with its own values checked, kept even
2392
+ * when the other filters now hide them; conditions as they are.
2393
+ */
2394
+ declare function initialFilterEditorDraft<Row>(shape: ColumnFilterShape<Row>, values: ColumnValueOptions): FilterEditorDraft<Row>;
2395
+ /** The options the draft's search shows — those a select-all acts on. */
2396
+ declare function visibleFilterOptions<Row>(draft: FilterEditorDraft<Row>): readonly ColumnValueOption[];
2397
+ /**
2398
+ * The select-all checkbox's state: whether every value the search shows is
2399
+ * checked, none is, or some are — blanks included while nothing is
2400
+ * searched.
2401
+ */
2402
+ declare function selectAllState<Row>(draft: FilterEditorDraft<Row>): SelectAllState;
2403
+ /**
2404
+ * The next draft after `action` — a plain reducer, so a renderer can hold
2405
+ * the draft in whatever state it has (`useReducer`, a signal, a store).
2406
+ * Changing a value or the blanks switches the draft to its checklist;
2407
+ * setting conditions switches it to them.
2408
+ */
2409
+ declare function filterEditorReducer<Row>(draft: FilterEditorDraft<Row>, action: FilterEditorAction<Row>): FilterEditorDraft<Row>;
2410
+ /**
2411
+ * `current` with `columnId`'s filter replaced by what `draft` holds — what
2412
+ * applying an editor sets. Every entry about the column goes (custom ones
2413
+ * included); the draft's entries take the place of the first, so the rest
2414
+ * keep their order.
2415
+ *
2416
+ * A checklist with everything checked is no filter at all, and a partial
2417
+ * pick is one `in` entry, OR-grouped with an `isEmpty` when blanks are
2418
+ * kept. One condition is stored as itself, two as a group with the
2419
+ * draft's combinator. Returns `current` itself when nothing changes.
2420
+ */
2421
+ declare function draftToFilter<Row>(draft: FilterEditorDraft<Row>, columnId: string, current: FilterState<Row>): FilterState<Row>;
1192
2422
 
1193
2423
  /**
1194
2424
  * One entry per row: its measured height once known, `undefined` until then.
@@ -1217,11 +2447,11 @@ interface VisibleRange {
1217
2447
  *
1218
2448
  * `O(n)` in `heights.length`. Deliberately not a Fenwick tree or a
1219
2449
  * binary-searchable prefix-sum structure — that optimization belongs in the
1220
- * mutable React-side cache that already owns mutability
1221
- * (`useRowVirtualizer`), not in this pure function's contract, and is not
2450
+ * mutable height cache that already owns mutability (`createRowVirtualizer`
2451
+ * in `@gridkitjs/dom`), not in this pure function's contract, and is not
1222
2452
  * worth taking on until a profiled scenario actually shows it matters at
1223
2453
  * realistic row counts.
1224
2454
  */
1225
2455
  declare function visibleRange(heights: RowHeights, estimatedHeight: number, scrollTop: number, viewportHeight: number, overscan: number): VisibleRange;
1226
2456
 
1227
- export { type AggregateFn, type AggregateResults, type AggregateSpec, type AggregateState, type BuiltInAggregate, type CellSelectEvent, type CellSelectionChangeEvent, type CellSelectionMode, type CellSelectionState, type CellTemplateContext, type ColumnAlignment, type ColumnConstraints, type ColumnDefinition, type ColumnOrderEvent, type ColumnOrderState, type ColumnResizeEvent, type ColumnResizeSession, type ColumnResolveOptions, type ColumnSelectEvent, type ColumnSelectionChangeEvent, type ColumnSizeDefaults, type ColumnSizingState, type ColumnSortEntry, type ColumnSortEvent, type ColumnSortState, type ColumnType, type ColumnWrapConfig, type ColumnsSelectEvent, DEFAULT_COLUMN_SIZES, type DisplayRow, type DropSide, type FieldPath, type FilterEntry, type FilterPredicate, type FilterState, type FooterTemplateContext, type GridFocus, type GroupAggregateDisplay, type GroupByEntry, type GroupByEvent, type GroupByState, type GroupExpansionEvent, type GroupExpansionState, type GroupFilterEntry, HEADER_ROW, KEYBOARD_STEP, type KeyShortcutCapabilities, type NavigationModifiers, type PaginatedRows, type PaginationChangeEvent, type PaginationState, type PaginationWindowEntry, type PredicateFilterEntry, type ResolvedColumn, type ResolvedGroupRow, type ResolvedGroupSummaryRow, type ResolvedRow, type RowHeights, type RowSelectEvent, type RowSelectionChangeEvent, type RowsSelectEvent, type SelectIntent, type SelectableConfig, type SelectedCell, type SelectedCellRef, type SelectedColumn, type SelectionDiff, type SelectionMode, type SelectionState, type SortDirection, type TextFilterEntry, type ValueFilterEntry, type VisibleRange, accessDotted, alignmentForType, applyColumnOrder, applyColumnResize, applySelectionIntent, beginColumnResize, buildKeyShortcuts, clampColumnWidth, clampFocus, clearAllFilters, clearSelection, collapseAllGroups, compareValues, computeAggregates, defineColumnsFromRows, diffSelection, expandAllGroups, filterQueryFor, filterRows, fitColumnsToWidth, getColumnId, groupRowId, groupRows, intentOf, isSameCell, matchesQuery, moveColumnBefore, moveGroupByBefore, movesColumn, movesGroupBy, nextFocusForKey, paginateRows, paginationWindow, resolveCell, resolveColumnConstraints, resolveColumnLabel, resolveColumnWidths, resolveColumns, resolveDropBefore, resolveKeyboardDropTarget, resolveRowId, resolveRows, resolveShownRows, revertColumnSize, selectAll, selectCell, selectOnly, selectRange, setColumnFilter, sizeColumnToContent, sortDirectionFor, sortPriorityFor, sortRows, toggleCellSelection, toggleColumnSort, toggleGroupExpansion, toggleSelection, totalColumnWidth, visibleRange, withGroupAggregates };
2457
+ export { type AggregateFn, type AggregateResults, type AggregateSpec, type AggregateState, type Borders, type BuiltInAggregate, type CellSelectEvent, type CellSelectionChangeEvent, type CellSelectionMode, type CellSelectionState, type CellTemplateContext, type ColumnAlignment, type ColumnConstraints, type ColumnDefinition, type ColumnFilterShape, type ColumnOrderEvent, type ColumnOrderState, type ColumnResizeEvent, type ColumnResizeSession, type ColumnResolveOptions, type ColumnSelectEvent, type ColumnSelectionChangeEvent, type ColumnSizeDefaults, type ColumnSizingState, type ColumnSortEntry, type ColumnSortEvent, type ColumnSortState, type ColumnType, type ColumnValueOption, type ColumnValueOptions, type ColumnValueQuery, type ColumnVisibilityEvent, type ColumnVisibilityState, type ColumnWrapConfig, type ColumnsSelectEvent, DEFAULT_COLUMN_SIZES, type DataColumn, type DisplayRow, type DropSide, type FieldPath, type FilterChangeEvent, type FilterEditorAction, type FilterEditorDraft, type FilterEntry, type FilterOperator, type FilterPredicate, type FilterScalar, type FilterState, type FooterTemplateContext, type FormatLocale, type GridController, type GridControllerOptions, type GridFocus, type GridMessages, type GridOptions, type GridState, type GridView, type GridViewInput, type GridViewOptions, type GridViewState, type GroupAggregateDisplay, type GroupByBarVisibility, type GroupByEntry, type GroupByEvent, type GroupByState, type GroupExpansionEvent, type GroupExpansionState, type GroupFilterEntry, HEADER_ROW, type HoverableConfig, KEYBOARD_STEP, type KeyShortcutCapabilities, type NavigationModifiers, type OperatorFilterEntry, type PaginatedRows, type PaginationChangeEvent, type PaginationState, type PaginationWindowEntry, type PredicateFilterEntry, type ResizeMode, type ResolvedColumn, type ResolvedGroupRow, type ResolvedGroupSummaryRow, type ResolvedRow, type RowHeights, type RowPointerEvent, type RowProps, type RowSelectEvent, type RowSelectionChangeEvent, type RowsSelectEvent, type SelectAllState, type SelectIntent, type SelectableConfig, type SelectedCell, type SelectedCellRef, type SelectedColumn, type SelectionCallbacks, type SelectionDiff, type SelectionDiffEvent, type SelectionMode, type SelectionState, type SerializedFilterEntry, type SerializedFilterScalar, type SerializedFilterState, type SerializedGridState, type SerializedGroupFilterEntry, type SerializedOperatorFilterEntry, type SerializedValueFilterEntry, type SortDirection, type TextFilterEntry, type ValueFilterEntry, type VisibleRange, accessDotted, alignmentForType, applyColumnOrder, applyColumnResize, applyColumnVisibility, applySelectionIntent, beginColumnResize, buildKeyShortcuts, clampColumnWidth, clampFocus, clearAllFilters, clearSelection, collapseAllGroups, columnValueOptions, compareValues, computeAggregates, createGridController, createGridViewResolver, defaultMessages, defineColumnsFromRows, describeColumnFilter, diffSelection, draftToFilter, expandAllGroups, filterEditorReducer, filterQueryFor, filterRows, fitColumnsToWidth, formatAggregateValue, formatCellValue, getColumnId, groupRowId, groupRows, initialFilterEditorDraft, intentOf, isColumnVisible, isSameCell, matchesQuery, moveColumnBefore, moveGroupByBefore, movesColumn, movesGroupBy, nextFocusForKey, paginateRows, paginationWindow, resolveCell, resolveColumnConstraints, resolveColumnHeaderText, resolveColumnLabel, resolveColumnWidths, resolveColumns, resolveDataColumns, resolveDropBefore, resolveGridView, resolveKeyboardDropTarget, resolveMessages, resolveRowId, resolveRows, resolveShownRows, revertColumnSize, selectAll, selectAllState, selectCell, selectOnly, selectRange, serializeGridState, setColumnFilter, setColumnVisibility, sizeColumnToContent, sortDirectionFor, sortPriorityFor, sortRows, toggleCellSelection, toggleColumnSort, toggleGroupExpansion, toggleSelection, totalColumnWidth, visibleFilterOptions, visibleRange, withGroupAggregates };