@kanunilabs/datagrid-react 1.0.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.
@@ -0,0 +1,1761 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, RefObject } from 'react';
3
+ import { GridRowData, DataGridController, StateStoringConfig, RowKey, RowNode, GridColumnDef, DataGridCoreConfig, InferColumnsOptions, DescribeFilterOptions, ColumnRuntime, ColumnLayout, SortDescriptor, ColumnModel, SummaryDef, FilterNode, GridDataType, SummaryType, GridDictionary, ColumnState, BusyPhase, DataRowNode, VirtualWindow, ChartSpec, ChartModel } from '@kanunilabs/datagrid-core';
4
+ export { BusyChangedEvent, BusyPhase, ClientRowModel, ColumnLayout, ColumnLookup, ColumnModel, ColumnPin, ColumnRuntime, ColumnState, ColumnStateChangedEvent, ContentReadyEvent, DEFAULT_ROW_HEIGHT, DataGridController, DataGridCoreConfig, DataRowNode, DataSource, FilterChangedEvent, FilterCondition, FilterGroupNode, FilterNode, FilterOperator, FocusedRowChangedEvent, GridColumnDef, GridDataType, GridEventMap, GridRowData, GridStateStore, GridViewState, GroupRowNode, GroupingConfig, HeaderBandCell, InferColumnsOptions, LoadOptions, LoadResult, LookupItem, NewRowPosition, RowDropPosition, RowKey, RowNode, ScrollingConfig, SelectionDescriptor, SortChangedEvent, SortDescriptor, SortDirection, StateStoringConfig, SummaryDef, SummaryType, ViewportChangedEvent, VirtualWindow, VirtualWindowEngine, andFilters, attachStateStoring, bandDepth, bandPath, computeColumnLayout, computeHeaderBands, createRestDataSource, describeFilter, downloadBlob, humanizeField, importCsv, inferColumns, isDataRow, isDataSource, isFilterGroup, isGroupRow, lookupLabel, moveRowInArray, parseCsv, sniffDelimiter, summaryKey } from '@kanunilabs/datagrid-core';
5
+
6
+ /** Formats an export menu can offer. */
7
+ type ExportKind = 'csv' | 'excel' | 'excel-selected' | 'pdf';
8
+ interface ExportOutcome {
9
+ /** False when the user cancelled, or when the writer refused. */
10
+ completed: boolean;
11
+ /** Set only for a genuine failure — a cancel reports `completed: false`. */
12
+ error?: string;
13
+ }
14
+ /**
15
+ * Run one export and download it.
16
+ *
17
+ * Never throws: both callers render the failure rather than letting it reach an
18
+ * error boundary, because the common cause is a missing optional peer
19
+ * (`exceljs`, `jspdf`) — a message the user can act on, not a crash.
20
+ */
21
+ declare function runGridExport<TRow = GridRowData>(controller: DataGridController<TRow>, kind: ExportKind): Promise<ExportOutcome>;
22
+
23
+ declare function useStateStoring<TRow = GridRowData>(controller: DataGridController<TRow>, config: StateStoringConfig | undefined): void;
24
+
25
+ interface PrintLayoutOptions {
26
+ /**
27
+ * Stay in print layout permanently instead of only while printing. For
28
+ * rendering a grid INTO a report page, where there is no print dialog to
29
+ * react to and the whole result is meant to be in the document.
30
+ */
31
+ always?: boolean;
32
+ /**
33
+ * Above this many rows the grid does NOT switch (default 5.000).
34
+ *
35
+ * Print layout puts every row in the DOM. At report sizes that is a beat;
36
+ * at a hundred thousand it is a frozen tab, and a browser that stops
37
+ * responding during printing is worse than a short printout. Past the cap the
38
+ * grid prints what it always did — the visible rows — and says so under
39
+ * `debugMode`, because the right tool at that size is `exportPdf`, which
40
+ * streams instead.
41
+ */
42
+ maxRows?: number;
43
+ }
44
+ /** Default row cap. Report-sized results print; result sets do not. */
45
+ declare const PRINT_MAX_ROWS = 5000;
46
+ interface PrintLayoutState {
47
+ /** Render every row and let the box grow. */
48
+ active: boolean;
49
+ /** Enter print layout by hand — for an app driving its own print button. */
50
+ begin: () => void;
51
+ /** Leave it again. */
52
+ end: () => void;
53
+ }
54
+ /**
55
+ * Suspend virtualization while the browser is printing.
56
+ *
57
+ * `flushSync` is not decoration here. `beforeprint` is the last moment the page
58
+ * can be changed, and the browser snapshots it the instant the handler returns
59
+ * — an ordinary `setState` would land after that and print the old, clipped
60
+ * view. This is the case the API exists for.
61
+ */
62
+ declare function usePrintLayout<TRow = GridRowData>(controller: DataGridController<TRow>, options: boolean | PrintLayoutOptions | undefined): PrintLayoutState;
63
+
64
+ /** MIME type carrying the payload. Custom so nothing else claims it. */
65
+ declare const ROW_DRAG_MIME = "application/x-kanuni-row";
66
+ /** What travels in `dataTransfer` when a row leaves its grid. */
67
+ interface RowDragPayload {
68
+ /** Grid the row started in — a same-grid drop is a REORDER, not a move. */
69
+ gridId: string;
70
+ /** Only grids sharing a group exchange rows. */
71
+ group: string | null;
72
+ rowKey: RowKey;
73
+ /** The row itself, so the receiving grid can insert it without a round trip. */
74
+ row: unknown;
75
+ }
76
+ interface RowReorderEvent<TRow = GridRowData> {
77
+ dragKey: RowKey;
78
+ /** Row it was dropped onto, or null for the empty space past the last row. */
79
+ targetKey: RowKey | null;
80
+ position: 'before' | 'after';
81
+ row: TRow | undefined;
82
+ }
83
+ interface RowDropEvent<TRow = GridRowData> {
84
+ /** Row that crossed over, as the source grid had it. */
85
+ row: TRow;
86
+ rowKey: RowKey;
87
+ /** Grid it came from / went to. */
88
+ otherGridId: string;
89
+ targetKey: RowKey | null;
90
+ position: 'before' | 'after';
91
+ }
92
+ interface RowDragConfig<TRow = GridRowData> {
93
+ enabled?: boolean;
94
+ /**
95
+ * Grids sharing a group can exchange rows. Without one a grid only reorders
96
+ * its own rows — dropping into an unrelated grid on the same page is almost
97
+ * never what the user meant.
98
+ */
99
+ group?: string;
100
+ /** Reordering within this grid. Default true. */
101
+ allowReordering?: boolean;
102
+ /** Accept rows from another grid in the group. Default true when `group` is set. */
103
+ allowDropInsert?: boolean;
104
+ /**
105
+ * A row was moved within this grid. Without a handler the grid reorders its
106
+ * own source array; with one, YOU own the order (persist it, update your
107
+ * sort field) and the grid does nothing on its own.
108
+ */
109
+ onReorder?: (event: RowReorderEvent<TRow>) => void;
110
+ /** A row arrived from another grid. */
111
+ onAdd?: (event: RowDropEvent<TRow>) => void;
112
+ /** A row of THIS grid was accepted by another one. */
113
+ onRemove?: (event: RowDropEvent<TRow>) => void;
114
+ }
115
+ /** What `GridRow` needs to render and drive the gesture. */
116
+ interface RowDragHandlers {
117
+ /** True while the pointer is on a grip — the row is armed for dragging. */
118
+ draggable: boolean;
119
+ setDraggable: (value: boolean) => void;
120
+ onDragStart: (node: RowNode<unknown>, event: React.DragEvent) => void;
121
+ onDragEnd: () => void;
122
+ onDragOver: (node: RowNode<unknown>, event: React.DragEvent) => void;
123
+ onDrop: (node: RowNode<unknown>, event: React.DragEvent) => void;
124
+ }
125
+ interface UseRowDragResult {
126
+ /** Undefined when dragging is off, so `GridRow` renders no grip at all. */
127
+ handlers: RowDragHandlers | undefined;
128
+ /** Row currently under the pointer, and which edge the line is drawn on. */
129
+ dropAt: {
130
+ key: RowKey;
131
+ side: 'before' | 'after';
132
+ } | null;
133
+ /** Drop target for the empty space BELOW the rows — "move to the end". */
134
+ bodyHandlers: {
135
+ onDragOver: (event: React.DragEvent) => void;
136
+ onDrop: (event: React.DragEvent) => void;
137
+ onDragLeave: (event: React.DragEvent) => void;
138
+ } | undefined;
139
+ }
140
+ declare function useRowDrag<TRow = GridRowData>(controller: DataGridController<TRow>, config: RowDragConfig<TRow> | undefined): UseRowDragResult;
141
+
142
+ interface CellRenderContext<TRow = GridRowData> {
143
+ value: unknown;
144
+ row: TRow;
145
+ node: RowNode<TRow>;
146
+ column: GridColumnDef<TRow>;
147
+ }
148
+ interface HeaderRenderContext<TRow = GridRowData> {
149
+ column: GridColumnDef<TRow>;
150
+ }
151
+ /**
152
+ * What a custom editor is handed. `commit`/`cancel` are the SAME handles the
153
+ * built-in editor uses, so a replacement participates in Tab-walking, batch
154
+ * changes and undo rather than living beside them.
155
+ */
156
+ interface EditorRenderContext<TRow = GridRowData> {
157
+ /** Current (uncommitted) editor value. */
158
+ value: unknown;
159
+ /** The row with pending edits applied — what a cascading list should read. */
160
+ row: TRow;
161
+ columnId: string;
162
+ /** Validation message for `value`, or null. */
163
+ error: string | null;
164
+ setValue: (value: unknown) => void;
165
+ commit: () => void;
166
+ cancel: () => void;
167
+ }
168
+ interface ReactGridColumnDef<TRow = GridRowData> extends GridColumnDef<TRow> {
169
+ /** Expensive escape hatch — renders arbitrary React per cell. */
170
+ renderCell?: (ctx: CellRenderContext<TRow>) => ReactNode;
171
+ renderHeader?: (ctx: HeaderRenderContext<TRow>) => ReactNode;
172
+ /**
173
+ * Replace the built-in editor for this column (Enterprise). Use for controls
174
+ * the grid does not ship — a colour picker, a masked input, an autocomplete
175
+ * against your own API.
176
+ */
177
+ renderEditor?: (ctx: EditorRenderContext<TRow>) => ReactNode;
178
+ /**
179
+ * Extra class(es) on every cell of this column — a monospace SKU column, a
180
+ * status tint. A function form allows per-VALUE classes without paying for a
181
+ * full `renderCell`, which would replace the cell's content and lose the
182
+ * built-in truncation and alignment.
183
+ */
184
+ cellClassName?: string | ((ctx: CellRenderContext<TRow>) => string | undefined);
185
+ /**
186
+ * Let this column's text WRAP, and let the row grow to fit it.
187
+ *
188
+ * The row is measured after it renders and the measurement is fed back to the
189
+ * window engine, so the scrollbar and every row offset account for the real
190
+ * height. Two consequences worth knowing before turning it on:
191
+ *
192
+ * • only RENDERED rows can be measured. Rows below the viewport are assumed
193
+ * to be the standard height until they scroll into it, so the scrollbar
194
+ * firms up as the user travels. Every virtualized grid that measures has
195
+ * this property; the alternative is laying out every row up front, which
196
+ * at a million rows is not an alternative.
197
+ * • measurements are dropped when a column is resized, because the width is
198
+ * what decided where the text wrapped.
199
+ *
200
+ * When the height is known WITHOUT measuring, `getRowHeight` on the config is
201
+ * both cheaper and exact.
202
+ */
203
+ autoHeight?: boolean;
204
+ /**
205
+ * Tooltip text for this column's cells (P2.1).
206
+ *
207
+ * Without it, a cell whose text does not fit still gets one — the grid falls
208
+ * back to showing the clipped text in full. Set this when the tooltip should
209
+ * say something the cell does NOT: a code's description, an abbreviation
210
+ * spelled out, why a value is flagged.
211
+ *
212
+ * The function form runs only for the cell under the pointer, so it may be as
213
+ * expensive as it needs to be. Return null (or an empty string) to suppress
214
+ * the tooltip for that cell — including the truncation fallback.
215
+ */
216
+ tooltip?: string | ((ctx: CellRenderContext<TRow>) => string | null | undefined);
217
+ /**
218
+ * Read the tooltip from another FIELD of the row — the common case where the
219
+ * long form already travels with the data (`status` / `statusDetail`).
220
+ *
221
+ * `tooltip` wins if both are given.
222
+ */
223
+ tooltipField?: string;
224
+ /** Tooltip for this column's HEADER cell. */
225
+ headerTooltip?: string;
226
+ }
227
+ /** Which cell a note belongs to, and the row behind it. */
228
+ interface NoteCellContext<TRow = GridRowData> {
229
+ rowKey: RowKey;
230
+ columnId: string;
231
+ row: TRow;
232
+ }
233
+ /**
234
+ * Which of the grid's own entries the cell context menu offers.
235
+ *
236
+ * Every flag defaults to ON, so `cellMenu` is a way to REMOVE things rather
237
+ * than a list to opt into — a grid that ships without saying anything gets the
238
+ * clipboard and export entries a user expects from a spreadsheet.
239
+ *
240
+ * Entries the grid cannot honour are not rendered rather than rendered
241
+ * disabled: an Export submenu on a grid whose optional export peers are absent
242
+ * would be a menu of dead ends. The exception is a permission the user could
243
+ * satisfy by acting — "Paste" stays visible while a cell is read-only, because
244
+ * the way out is to focus a different cell.
245
+ */
246
+ interface CellMenuOptions {
247
+ /** Copy the selected cells, or the selected rows when there is no range. */
248
+ copy?: boolean;
249
+ /** Copy with a column-header line above the values. */
250
+ copyWithHeaders?: boolean;
251
+ /**
252
+ * Copy with the BAND caption rows above the header line. Hidden on a grid
253
+ * with no bands, where it would produce exactly `copyWithHeaders`.
254
+ */
255
+ copyWithGroupHeaders?: boolean;
256
+ /**
257
+ * Export submenu. `true` offers every format the grid can write; an array
258
+ * picks which, in the order given.
259
+ *
260
+ * Excel and PDF ride on OPTIONAL peers (`exceljs`, `jspdf`), so the default
261
+ * is CSV only — the one format that needs nothing installed. An app that
262
+ * installed the peers opts into them by naming them.
263
+ */
264
+ export?: boolean | ExportKind[];
265
+ }
266
+ /**
267
+ * Cell notes — prose a USER attached to a cell, Excel-style (gap analysis P3.3).
268
+ *
269
+ * Not a tooltip and not a value. A tooltip describes what the cell already
270
+ * says; a note is something a person added that is not in the data, so it
271
+ * carries a visible marker — you can see which cells have one without hovering
272
+ * every cell to find out.
273
+ *
274
+ * Storage is the application's. The grid renders notes, reveals them and (with
275
+ * `onChange`) collects edits; it does not decide where they live, because a
276
+ * note that survives a reload has to go somewhere only the app knows about.
277
+ */
278
+ interface GridNotesConfig<TRow = GridRowData> {
279
+ /**
280
+ * This cell's note, or null.
281
+ *
282
+ * Called for every RENDERED cell, so make it a lookup — a Map keyed by
283
+ * `${rowKey}:${columnId}` is the shape that fits. Anything that scans per
284
+ * call turns a viewport into quadratic work.
285
+ */
286
+ get: (ctx: NoteCellContext<TRow>) => string | null | undefined;
287
+ /**
288
+ * The user changed a note; `text` is null when they cleared it.
289
+ *
290
+ * Its PRESENCE is what makes notes editable: without it the grid shows notes
291
+ * and offers no way to write one, which is the right behaviour for a grid
292
+ * displaying notes that came from somewhere else.
293
+ */
294
+ onChange?: (ctx: NoteCellContext<TRow> & {
295
+ text: string | null;
296
+ }) => void;
297
+ /**
298
+ * Append add/edit/delete items to the row context menu. Default true when
299
+ * `onChange` is given.
300
+ *
301
+ * Turn it off to place the items yourself — the grid's own menu entries are a
302
+ * convenience, not the only way in.
303
+ */
304
+ menu?: boolean;
305
+ }
306
+ /** Grid-wide tooltip behaviour (P2.1). */
307
+ interface GridTooltipConfig {
308
+ /** Off entirely, truncation fallback included. Default true. */
309
+ enabled?: boolean;
310
+ /**
311
+ * Milliseconds of hover before it appears. Default 400 — long enough that
312
+ * sweeping the pointer across a grid does not strobe, short enough to feel
313
+ * like an answer rather than a wait.
314
+ */
315
+ delay?: number;
316
+ /**
317
+ * Show the full text when a cell's content is clipped. Default true; this is
318
+ * the behaviour the column API exists to override, not the other way round.
319
+ */
320
+ showOnTruncation?: boolean;
321
+ }
322
+ type ReactColumnRuntime<TRow = GridRowData> = Omit<ColumnRuntime<TRow>, 'def'> & {
323
+ def: ReactGridColumnDef<TRow>;
324
+ };
325
+ /** Vertical rhythm preset. `rowHeight` overrides whatever this resolves to. */
326
+ type GridDensity = 'compact' | 'normal' | 'comfortable';
327
+ /** Row height each density resolves to — the value the virtualizer runs on. */
328
+ declare const DENSITY_ROW_HEIGHT: Record<GridDensity, number>;
329
+ /**
330
+ * A packaged look. Purely visual: no theme changes what the grid does, which
331
+ * columns it shows, or how any of it behaves.
332
+ *
333
+ * The palettes, metrics and line work live in DataGridThemes.css and are ours
334
+ * — nothing is imported or copied from a third-party grid's stylesheet. The
335
+ * names are the shared vocabulary for a grid's chrome and density, in the same
336
+ * way a typeface is described as "grotesque".
337
+ */
338
+ type GridTheme =
339
+ /** Minimal chrome: no column rules, hairline rows, roomy radii. */
340
+ 'quartz'
341
+ /** Dense and fully ruled — small type, tight padding, square corners. */
342
+ | 'balham'
343
+ /** Tall rhythm, rows-only rules, medium-weight tracked header, real elevation. */
344
+ | 'material'
345
+ /** Airy but fully framed, with a doubled rule anchoring the header band. */
346
+ | 'alpine'
347
+ /** KanuniLabs house theme: ink header, brand gold accent, warm near-white body. */
348
+ | 'kanuni'
349
+ /** The house theme in dark. Applies without `dark`. */
350
+ | 'kanuni-midnight';
351
+ /** Every packaged theme, in the order the docs and the playground list them. */
352
+ declare const GRID_THEMES: readonly GridTheme[];
353
+ /**
354
+ * Row height per theme and density.
355
+ *
356
+ * This table exists in JS and not in CSS for the same reason `DENSITY_ROW_HEIGHT`
357
+ * does: the row height is a CORE value — the virtualizer computes every row
358
+ * offset and the scroll extent from it — so it has to reach the engine as a
359
+ * number. Everything else a theme changes about the rhythm (header height, cell
360
+ * padding, type scale, control sizes) is CSS's, and lives only there, so the
361
+ * two can never hold different opinions about the same measurement.
362
+ *
363
+ * Keep in sync with the `--dg-header-height` values in DataGridThemes.css.
364
+ */
365
+ declare const THEME_ROW_HEIGHT: Record<GridTheme, Record<GridDensity, number>>;
366
+ /**
367
+ * A colour scheme, independent of the theme.
368
+ *
369
+ * The theme decides the STRUCTURE — where the rules are drawn, how tall a row
370
+ * is, how round a control is, how much a popup lifts. A palette decides only
371
+ * the colours. They compose: `theme="balham" palette="nord"` is Balham's dense
372
+ * ruled grid in Nord's arctic blues; `theme="material" palette="nord"` is
373
+ * Material's tall airy one in the same blues.
374
+ *
375
+ * Each ships a light and a dark set, so `dark` keeps working across all of
376
+ * them. Every one clears WCAG AA on the surfaces that carry text — measured,
377
+ * and pinned by a test.
378
+ *
379
+ * The names are the colour schemes people already know from their editors. The
380
+ * hues are theirs; the mapping onto the grid's tokens, and the contrast work
381
+ * that mapping needs, are ours. No third-party stylesheet is involved.
382
+ */
383
+ type GridPalette =
384
+ /** Arctic, desaturated blue-grey. The calmest of the six. */
385
+ 'nord'
386
+ /** Near-black violet with high-chroma purple and pink. */
387
+ | 'dracula'
388
+ /** The fixed-lightness classic, designed as a matched light/dark pair. */
389
+ | 'solarized'
390
+ /** Warm retro, high contrast — the only genuinely warm light mode here. */
391
+ | 'gruvbox'
392
+ /** Pastel, with very close surface tones. Easy for a long reading session. */
393
+ | 'catppuccin'
394
+ /** Deep navy with violet and cyan; the most saturated dark. */
395
+ | 'tokyo-night';
396
+ /** Every packaged palette, in the order the docs and the playground list them. */
397
+ declare const GRID_PALETTES: readonly GridPalette[];
398
+ /**
399
+ * The modifier classes a theme contributes, as one string.
400
+ *
401
+ * Composed ONCE per grid and handed to the popup surfaces that render outside
402
+ * the grid's DOM (portals, dialogs, the row editor). Those cannot inherit the
403
+ * class through the cascade, and the tokens are declared on the theme class —
404
+ * so without this they render in the packaged palette while the grid behind
405
+ * them is in another one. It is the same reason `dark` is forwarded explicitly.
406
+ *
407
+ * A plain string, deliberately: nothing here is per-row or per-cell work.
408
+ */
409
+ declare function themeClassName(theme?: GridTheme, density?: GridDensity, palette?: GridPalette): string;
410
+ interface GridMenuItem<TContext = unknown> {
411
+ /** Stable key; falls back to the index when omitted. */
412
+ id?: string;
413
+ label?: string;
414
+ icon?: ReactNode;
415
+ /** Right-aligned hint, e.g. "Ctrl+C". Purely visual — no binding is created. */
416
+ shortcut?: string;
417
+ disabled?: boolean;
418
+ /** Renders a divider instead of an item. */
419
+ separator?: boolean;
420
+ /** One level of nesting. */
421
+ items?: GridMenuItem<TContext>[];
422
+ onSelect?: (context: TContext) => void;
423
+ }
424
+ /** What a row template is handed. */
425
+ interface RowTemplateContext<TRow = GridRowData> {
426
+ row: TRow;
427
+ node: RowNode<TRow>;
428
+ /** Position in the current view. */
429
+ rowIndex: number;
430
+ selected: boolean;
431
+ /** True when this is the focused row (`focusedRow` is on). */
432
+ focused: boolean;
433
+ }
434
+ /** What a header menu is opened against. */
435
+ interface HeaderMenuContext {
436
+ columnId: string;
437
+ /** Header text as rendered, for labels and announcements. */
438
+ headerName: string;
439
+ }
440
+ /** What a row menu is opened against. */
441
+ interface RowMenuContext<TRow = GridRowData> {
442
+ row: TRow;
443
+ node: RowNode<TRow>;
444
+ /** Index in the current view. */
445
+ rowIndex: number;
446
+ /** Column under the pointer, when the menu was opened from a cell. */
447
+ columnId: string | null;
448
+ /** Selected row keys at the moment the menu opened. */
449
+ selectedKeys: RowKey[];
450
+ }
451
+ interface DataGridProps<TRow = GridRowData> extends Omit<DataGridCoreConfig<TRow>, 'columns'> {
452
+ /**
453
+ * Optional: with an ARRAY data source and no columns, one column per field
454
+ * is derived from the data (see `autoColumns`). A remote `DataSource` must
455
+ * declare them — there is nothing to sample before the first fetch.
456
+ */
457
+ columns?: ReactGridColumnDef<TRow>[];
458
+ /**
459
+ * Tunes the columns derived from the data when `columns` is omitted:
460
+ * `sampleSize`, `include`, `exclude`, `humanizeHeaders`, `defaults`.
461
+ * `false` turns the derivation off and leaves the grid with no columns.
462
+ */
463
+ autoColumns?: boolean | InferColumnsOptions;
464
+ /**
465
+ * Drag rows to reorder them, and — with a shared `group` — between two
466
+ * grids. A grip appears at the start of each row on hover.
467
+ *
468
+ * Reordering the grid's own array only works on an UNSORTED view: with a
469
+ * sort active the sort decides the order and the drop would be invisible.
470
+ * Handle `onReorder` yourself (updating an order field, or the server) to
471
+ * support a sorted grid.
472
+ */
473
+ rowDrag?: RowDragConfig<TRow>;
474
+ className?: string;
475
+ style?: React.CSSProperties;
476
+ /** Applies the packaged dark palette. */
477
+ dark?: boolean;
478
+ /**
479
+ * A packaged look — `'quartz'`, `'balham'`, `'material'`, `'alpine'`,
480
+ * `'kanuni'` or `'kanuni-midnight'`. Omit for the default palette, which is
481
+ * unchanged and always will be.
482
+ *
483
+ * Switchable at runtime: it resolves to one class on the grid root, so
484
+ * changing it repaints without rebuilding anything. Combines with `dark`
485
+ * (each theme ships both palettes) and with `density`. `'kanuni-midnight'`
486
+ * is already dark and ignores the flag.
487
+ *
488
+ * Every colour, metric and border the stylesheet draws with is a `--dg-*`
489
+ * custom property, so a theme is a token override and nothing more — override
490
+ * the same properties yourself under your own class to go further.
491
+ */
492
+ theme?: GridTheme;
493
+ /**
494
+ * A colour scheme, on top of whatever `theme` decides about structure —
495
+ * `'nord'`, `'dracula'`, `'solarized'`, `'gruvbox'`, `'catppuccin'` or
496
+ * `'tokyo-night'`.
497
+ *
498
+ * Independent of `theme`, so the two multiply: Balham's dense ruled grid in
499
+ * Nord's blues, or Material's tall one in the same blues. Works with `dark`
500
+ * (each palette ships both), and without a `theme` it recolours the packaged
501
+ * default. Omit it and the theme's own colours apply.
502
+ */
503
+ palette?: GridPalette;
504
+ /** Alternating row background. */
505
+ striped?: boolean;
506
+ /** Shown when the pipeline yields no rows. */
507
+ emptyText?: string;
508
+ /** Header click sorting (multi-sort follows `sorting.multi` + Shift). */
509
+ sortable?: boolean;
510
+ /** Text-input row under the header, one box per column. */
511
+ filterRow?: boolean;
512
+ /**
513
+ * Milliseconds to wait after the last keystroke before applying a filter-row
514
+ * value. `0` applies on every keystroke.
515
+ *
516
+ * Defaults to **0 for client data** (filtering is local and instant — adding a
517
+ * delay to something already immediate would be a regression in feel) and
518
+ * **200 for a remote `DataSource`**, where each keystroke is a network round
519
+ * trip: typing "Marmara" used to open seven queries, six of them already
520
+ * superseded by the time they answered.
521
+ *
522
+ * Set it explicitly on a very large CLIENT grid, where one pass is expensive
523
+ * enough that coalescing keystrokes is worth a short delay.
524
+ *
525
+ * Enter, or moving focus out of the box, always applies immediately.
526
+ */
527
+ filterDebounce?: number;
528
+ /**
529
+ * A bar under the footer stating the active filter in words, with a checkbox
530
+ * that suspends it and an `×` that removes it — the DevExpress cxGrid filter
531
+ * panel.
532
+ *
533
+ * `true` shows it only while something is filtered; `'always'` keeps it in
534
+ * place. It describes the FULL filter — the builder's conditions and every
535
+ * column funnel and filter-row entry, combined — which is what makes it the
536
+ * one place to read back why the grid is showing 12 of 100.000 rows.
537
+ */
538
+ filterPanel?: boolean | 'always';
539
+ /** Wording for the filter panel's sentence — `and` / `or` / `is` / `blank`… */
540
+ filterPanelLabels?: DescribeFilterOptions;
541
+ /**
542
+ * Opens the app's filter builder from the panel's chevron. Without it the
543
+ * chevron is not rendered, because a control that opens nothing is worse than
544
+ * no control. `EnterpriseDataGrid` wires this to its own builder.
545
+ */
546
+ onOpenFilterBuilder?: () => void;
547
+ /** Funnel button per header opening the distinct-value checklist popup. */
548
+ headerFilter?: boolean;
549
+ /**
550
+ * When the funnel is visible. `'hover'` (default) keeps the header clean and
551
+ * reveals it on hover or keyboard focus; `'always'` pins it. A column that is
552
+ * actually filtered always shows its funnel, in either mode.
553
+ */
554
+ filterIconMode?: 'hover' | 'always';
555
+ /** Drag a header onto another header to change the column order. Default on. */
556
+ columnReorder?: boolean;
557
+ /** Width drag handle on each header's trailing edge; double-click best-fits. Default on. */
558
+ columnResize?: boolean;
559
+ /**
560
+ * Overlay shown while the grid is busy, with a percentage where one exists.
561
+ * Default on. A number sets how many milliseconds of work must pass before it
562
+ * appears (default 300) — short operations finish without ever showing it.
563
+ */
564
+ loadingOverlay?: boolean | number;
565
+ /** Fired on Enter / double-click over the focused row. */
566
+ onRowActivate?: (node: RowNode<TRow>) => void;
567
+ /** Announcements for screen readers (sort/filter/selection). Defaults on. */
568
+ announcements?: boolean;
569
+ /** Drop area above the grid for grouping by a column. */
570
+ groupPanel?: boolean;
571
+ /**
572
+ * Vertical rhythm preset. Resolves to a row height, header height, font size
573
+ * and cell padding. An explicit `rowHeight` still wins — density is a
574
+ * shorthand, not an override.
575
+ */
576
+ density?: GridDensity;
577
+ /**
578
+ * Right-click (and Shift+F10 / ContextMenu key) menu for data rows. A
579
+ * function receives the row that was clicked, so the items can depend on it.
580
+ * Returning an empty list suppresses the menu for that row.
581
+ */
582
+ rowMenu?: GridMenuItem<RowMenuContext<TRow>>[] | ((context: RowMenuContext<TRow>) => GridMenuItem<RowMenuContext<TRow>>[]);
583
+ /**
584
+ * The grid's OWN cell-menu entries — clipboard and export — appended below
585
+ * whatever `rowMenu` supplies. See {@link CellMenuOptions}.
586
+ *
587
+ * On by default: a right-click that offers no way to copy the cell is the
588
+ * gap this closes, and an app that wants the menu bare passes `false`.
589
+ * `rowMenu` is untouched either way — an app's items keep the order it wrote
590
+ * them in, and the built-ins read as an addition after a separator.
591
+ */
592
+ cellMenu?: boolean | CellMenuOptions;
593
+ /**
594
+ * Right-click menu on COLUMN HEADERS: sort, group, footers, hide, field
595
+ * chooser, best fit. `true` (default) uses the built-in list; a function
596
+ * receives it and returns the list to show, so an app can drop items or
597
+ * append its own. `false` turns it off and lets the browser menu through.
598
+ */
599
+ headerMenu?: boolean | ((context: HeaderMenuContext, defaults: GridMenuItem<HeaderMenuContext>[]) => GridMenuItem<HeaderMenuContext>[]);
600
+ /**
601
+ * Highlight the row holding focus. Distinct from selection: focus is where
602
+ * the keyboard is, selection is what an action applies to — a grid that
603
+ * conflates them cannot express "look at this row without selecting it".
604
+ */
605
+ focusedRow?: boolean;
606
+ /** Focus this row on mount / when the prop changes (controlled focus). */
607
+ focusedRowKey?: RowKey | null;
608
+ /** Fired when the focused ROW changes — not on every cell move within it. */
609
+ onFocusedRowChanged?: (event: {
610
+ rowKey: RowKey | null;
611
+ row?: TRow;
612
+ }) => void;
613
+ /**
614
+ * @deprecated Retired 2026-08-15 and now a no-op; the prop is kept so
615
+ * existing call sites do not break, and will be dropped in a later major.
616
+ *
617
+ * It rendered a strip under the grid whose only remaining content was the row
618
+ * count. That strip once carried the per-column totals too, but punch item
619
+ * #18 moved those into the column-aligned footer ROW, where each number sits
620
+ * under the column it belongs to — leaving a whole bar to say one number.
621
+ * With the Enterprise status bar switched on, that number was then printed
622
+ * twice, one line above itself.
623
+ *
624
+ * Where the count lives now: the **status bar** (`statusBar`, Enterprise —
625
+ * it reports total, filtered and selected), or the **pager**, which shows the
626
+ * total whenever pagination is on.
627
+ */
628
+ showRowCount?: boolean;
629
+ /**
630
+ * Column-aligned totals row at the bottom of the grid. Defaults to on when
631
+ * `summaries` is non-empty. Turning it on with no summaries shows an empty
632
+ * row, which is how the user reaches the per-column aggregation menu.
633
+ */
634
+ footer?: boolean;
635
+ /**
636
+ * Right-click (and click) on a footer cell opens the aggregation picker —
637
+ * Count / Sum / Average / Min / Max, filtered to what the column's data type
638
+ * supports. `false` makes the footer read-only.
639
+ */
640
+ footerMenu?: boolean;
641
+ /** Toolbar with search, column chooser and export. */
642
+ toolbar?: boolean;
643
+ /**
644
+ * The toolbar's Columns button — the popup listing HIDDEN columns. Default
645
+ * true. The Enterprise wrapper turns it off when the side bar's Columns
646
+ * panel is showing: two surfaces answering "where did my column go" is one
647
+ * more than anyone needs, and the panel is the fuller of the two.
648
+ */
649
+ toolbarColumnChooser?: boolean;
650
+ /** Add an Excel entry to the toolbar's export menu (needs the optional `exceljs` peer). */
651
+ toolbarExcel?: boolean;
652
+ /**
653
+ * Add a PDF entry to the toolbar's export menu (needs the optional `jspdf`
654
+ * and `jspdf-autotable` peers).
655
+ *
656
+ * Off by default for the same reason as Excel: an entry that is always there
657
+ * would fail on a grid that never installed the libraries, and a menu item
658
+ * that throws is worse than one that is absent.
659
+ */
660
+ toolbarPdf?: boolean;
661
+ /** Extra toolbar controls, before / after the built-in ones. */
662
+ toolbarItemsBefore?: ReactNode;
663
+ toolbarItemsAfter?: ReactNode;
664
+ /**
665
+ * Replace each DATA row's content. Group headers and detail panels keep
666
+ * their own rendering — a template that had to handle those too would be a
667
+ * template of the whole grid.
668
+ *
669
+ * The row ELEMENT stays: it carries the height, the aria indices and the
670
+ * click handlers the virtualizer and selection depend on. What the template
671
+ * replaces are the cells — so in-cell editing, range selection and the fill
672
+ * handle have nothing to attach to while it is on. For per-cell
673
+ * customization that keeps the column layout, use a column's `renderCell`.
674
+ */
675
+ renderRow?: (ctx: RowTemplateContext<TRow>) => ReactNode;
676
+ /**
677
+ * Remember the user's columns, sort, filters and grouping between visits.
678
+ * Off by default: persisting a layout the app never asked to persist is a
679
+ * surprise, and the storage key is shared ground the app may own.
680
+ */
681
+ stateStoring?: StateStoringConfig;
682
+ /**
683
+ * Cell and header tooltips (P2.1). On by default — with no configuration at
684
+ * all, a cell whose text is clipped shows it in full on hover, which is the
685
+ * complaint this exists to answer. Pass `{ enabled: false }` to opt out, or
686
+ * see a column's `tooltip` / `tooltipField` to say something else.
687
+ */
688
+ tooltip?: GridTooltipConfig;
689
+ /**
690
+ * Rows fixed above the scrolling body (P2.3) — a target line, last year's
691
+ * figures, a row the user pinned for comparison.
692
+ *
693
+ * They are NOT data: never sorted, filtered, paged, selected or counted, and
694
+ * not included in an export. Everything visual is shared with a real row
695
+ * (columns, widths, pinned offsets, `renderCell`), so the two stay aligned.
696
+ * For totals over the grid's own rows, use `summaries` — that is what the
697
+ * footer row is.
698
+ */
699
+ pinnedTopRows?: readonly TRow[];
700
+ /** Rows fixed below the scrolling body. See `pinnedTopRows`. */
701
+ pinnedBottomRows?: readonly TRow[];
702
+ /**
703
+ * Which edge the grand total row sticks to (P2.8). Default `'bottom'`.
704
+ *
705
+ * Same row either way — the column-aligned totals over the filtered result,
706
+ * with the same aggregation menu. `'top'` is for a grid read top-down, where
707
+ * the totals are the headline; the rows below are then the evidence for it.
708
+ */
709
+ footerPosition?: 'top' | 'bottom';
710
+ /**
711
+ * What to do with the column widths once the grid knows how much room it has
712
+ * (gap analysis P2.5). Default `'none'` — the configured widths are used as
713
+ * written, which is what every existing grid already does.
714
+ *
715
+ * - `'fitGrid'` spreads the columns across the width so they fill it exactly,
716
+ * proportionally to their configured widths, re-applied when the grid is
717
+ * resized. For a grid that should never show empty canvas or a horizontal
718
+ * scrollbar.
719
+ * - `'fitCells'` measures the rendered content and sizes each column to it —
720
+ * the "best fit" the header menu offers, applied once to every column after
721
+ * the first rows paint. It can overflow the grid; that is the point.
722
+ *
723
+ * A user resize is not undone by either: `'fitGrid'` re-runs on a GRID resize
724
+ * and takes the current widths as its proportions, so a column the user made
725
+ * wider stays proportionally wider.
726
+ */
727
+ autoSizeStrategy?: 'none' | 'fitGrid' | 'fitCells';
728
+ /**
729
+ * Mark the search term inside cell text (gap analysis P2.4). Default true.
730
+ *
731
+ * The search already decides which rows survive; without the marks the user
732
+ * is left scanning fifteen columns for the reason a row is on screen. Only
733
+ * the grid's own rendering is marked up — a column with a `renderCell` owns
734
+ * its output and is left alone.
735
+ */
736
+ highlightSearch?: boolean;
737
+ /**
738
+ * A leading column numbering the rows (gap analysis P3.5). Default false.
739
+ *
740
+ * The number is the row's position in WHAT IS SHOWN — it renumbers after a
741
+ * sort or a filter rather than carrying a record's identity around. A stable
742
+ * per-record number is a column of your data; this is a reading aid, the one
743
+ * users mean when they say "the third row".
744
+ *
745
+ * With pagination on it continues across pages: page 2 of 50 starts at 51,
746
+ * because a second page starting at 1 again says nothing about position.
747
+ * Group headers and detail panels get an empty cell — a number on a group
748
+ * header would claim it is a record.
749
+ */
750
+ rowNumbers?: boolean;
751
+ /**
752
+ * Print the whole grid instead of the screenful that happened to be visible
753
+ * (gap analysis P3.4). Off by default.
754
+ *
755
+ * Virtualization is what makes a printed grid useless: the browser gets the
756
+ * twenty rows inside a clipped, fixed-height box and paginates those. With
757
+ * this on, `beforeprint` switches the grid to a mode that renders every row
758
+ * and lets the box grow, and `afterprint` puts it back — the user's scroll
759
+ * position and selection survive, because only the rendering changed.
760
+ *
761
+ * `{ always: true }` stays in that mode permanently, for rendering a grid
762
+ * into a report page where there is no print dialog to react to.
763
+ *
764
+ * `{ maxRows }` (default 5.000) is where it refuses. Print layout puts every
765
+ * row in the DOM; at a hundred thousand that is a frozen tab, which is worse
766
+ * than a short printout. Past the cap the grid prints what it always did and
767
+ * says so under `debugMode` — `exportPdf()` is the tool at that size, and it
768
+ * streams.
769
+ */
770
+ printLayout?: boolean | PrintLayoutOptions;
771
+ /**
772
+ * Keep this grid's COLUMN LAYOUT and horizontal scroll in step with every
773
+ * other grid given the same name (gap analysis P3.2).
774
+ *
775
+ * For two grids that have to read as one table — a scrolling body with a
776
+ * fixed totals strip under it, or a master and a detail over the same
777
+ * columns. Resize, move, hide or pin a column in one and the others follow;
778
+ * scroll one sideways and the others keep pace.
779
+ *
780
+ * Sort, filter, selection and VERTICAL scroll deliberately do not travel.
781
+ * Aligned grids share a shape, not a result — a summary strip that re-sorted
782
+ * itself because the grid above it did would be answering a different
783
+ * question. Columns the peer does not have are ignored, so a three-column
784
+ * strip can align under a twelve-column grid.
785
+ */
786
+ alignedGrids?: string;
787
+ /**
788
+ * Cell notes (gap analysis P3.3) — user-written prose attached to a cell,
789
+ * marked with a corner triangle and revealed on hover. See
790
+ * {@link GridNotesConfig}.
791
+ */
792
+ notes?: GridNotesConfig<TRow>;
793
+ onRowClick?: (node: RowNode<TRow>) => void;
794
+ /**
795
+ * Imperative access to the controller (§5.3) — search, filters, layout,
796
+ * export. Called once per controller instance; keep the callback stable
797
+ * (useCallback) or it will fire on every render.
798
+ */
799
+ onReady?: (controller: DataGridController<TRow>) => void;
800
+ /** Replace a cell's content (e.g. with an editor). Return undefined for default. */
801
+ renderCellContent?: (ctx: CellSlotContext<TRow>) => ReactNode | undefined;
802
+ /** Extra class names per cell (dirty marker, range highlight, invalid). */
803
+ getCellClassName?: (ctx: CellSlotContext<TRow>) => string | undefined;
804
+ /** Absolutely-positioned layer over the viewport (fill handle, watermark). */
805
+ renderOverlay?: () => ReactNode;
806
+ /** Pointer down on a cell, with the view coordinates the range engine needs. */
807
+ onCellPointerDown?: (ctx: CellSlotContext<TRow>, event: React.MouseEvent) => void;
808
+ onCellPointerEnter?: (ctx: CellSlotContext<TRow>, event: React.MouseEvent) => void;
809
+ onCellDoubleClick?: (ctx: CellSlotContext<TRow>) => void;
810
+ /**
811
+ * Unlocks the Enterprise edition. **Set by `EnterpriseDataGrid`** — an app
812
+ * rendering `DataGrid` directly leaves it off.
813
+ *
814
+ * The engine keeps every capability either way: the pipeline (filter → sort →
815
+ * group → aggregate) is one piece, and an edition boundary cutting through it
816
+ * would be a fork rather than a gate. What this flag decides is what the
817
+ * COMPONENT agrees to switch on. See `gateEdition` for the removals and
818
+ * `DataGridController.setEnterpriseEdition` for the imperative half.
819
+ *
820
+ * Off, the following are declined (each says why under `debugMode`):
821
+ * multi-level grouping (capped at one level), `grouping.groupSummaries`,
822
+ * `masterDetail`, `tree`, `summaries` (footer column totals), column
823
+ * `spanRows`, `notes`, Excel and PDF export. A server-side `DataSource`
824
+ * THROWS rather than being stripped — an empty grid sends the developer
825
+ * hunting in their backend.
826
+ *
827
+ * Community keeps: virtual scrolling at a million rows, worker offload,
828
+ * sorting, the column family, filtering, selection, pagination, single-level
829
+ * grouping, CSV export, themes, 12 locales and the whole event/imperative
830
+ * API. Boundary set with the user on 2026-08-14 against AG Grid's own line.
831
+ */
832
+ enterpriseEdition?: boolean;
833
+ /**
834
+ * Body of a row's detail panel. Required for the expander column to appear —
835
+ * together with `masterDetail` on the config.
836
+ */
837
+ renderDetail?: (row: TRow) => ReactNode;
838
+ /**
839
+ * Body of the in-grid edit form (`mode: 'form'`), rendered in the row flow
840
+ * under the record it edits. Set by `EnterpriseDataGrid`; the form's position
841
+ * and height come from `controller.setEditFormRow`.
842
+ */
843
+ renderEditForm?: (rowKey: RowKey, row: TRow | undefined) => ReactNode;
844
+ }
845
+ interface CellSlotContext<TRow = GridRowData> {
846
+ node: RowNode<TRow>;
847
+ columnId: string;
848
+ /** Row position in the current view. */
849
+ rowIndex: number;
850
+ /** Column position in visual order (pinned-left → center → right). */
851
+ columnIndex: number;
852
+ value: unknown;
853
+ }
854
+
855
+ declare function DataGrid<TRow = GridRowData>(props: DataGridProps<TRow>): react.JSX.Element;
856
+
857
+ interface GridHeaderProps<TRow = GridRowData> {
858
+ layout: ColumnLayout<TRow>;
859
+ /**
860
+ * Height of the band rows above, in px. The header sticks BELOW them rather
861
+ * than at 0 — otherwise scrolling down slides the header up over the bands
862
+ * it belongs to.
863
+ */
864
+ bandOffset?: number;
865
+ centerColumns: ReactColumnRuntime<TRow>[];
866
+ /** Inset per STICKY center column, by id. See RenderGeometry.stickyInsets. */
867
+ stickyInsets?: ReadonlyMap<string, number>;
868
+ leadingSpacer: number;
869
+ trailingSpacer: number;
870
+ sort: SortDescriptor[];
871
+ sortable: boolean;
872
+ onSort: (columnId: string, additive: boolean) => void;
873
+ /** Renders the funnel button that opens the distinct-value popup. */
874
+ headerFilter?: boolean;
875
+ /** Leading select-all checkbox column. */
876
+ checkboxColumn?: boolean;
877
+ /** Leading expander column for master-detail (Enterprise). */
878
+ detailColumn?: boolean;
879
+ /** Leading row-number column (gap analysis P3.5). */
880
+ rowNumberColumn?: boolean;
881
+ /** What the row-number column's header shows. Defaults to `#`. */
882
+ rowNumberHeader?: React.ReactNode;
883
+ /** 'all' | 'some' | 'none' — drives the tri-state select-all box. */
884
+ selectAllState?: 'all' | 'some' | 'none';
885
+ onToggleSelectAll?: () => void;
886
+ /** Make headers draggable onto the group panel. */
887
+ draggable?: boolean;
888
+ /** Column ids that currently have a filter (funnel shown as active). */
889
+ filteredColumns?: string[];
890
+ onOpenFilter?: (columnId: string, anchor: HTMLElement) => void;
891
+ /**
892
+ * When the funnel is visible. `'hover'` (default) keeps the header clean and
893
+ * reveals it on hover/keyboard focus; `'always'` pins it. A column that IS
894
+ * filtered shows its funnel in both modes — hiding the only indicator that a
895
+ * filter is active would make rows disappear for no visible reason.
896
+ */
897
+ filterIconMode?: 'hover' | 'always';
898
+ /** Drag a header onto another to change the column order. */
899
+ reorderable?: boolean;
900
+ /** `beforeId === undefined` means "move to the end". */
901
+ onReorder?: (columnId: string, beforeId: string | undefined) => void;
902
+ /** Drag handle on the trailing edge of each header. */
903
+ resizable?: boolean;
904
+ /** `finished` is false while dragging, true once on release. Absent = one-shot. */
905
+ onResize?: (columnId: string, width: number, finished?: boolean) => void;
906
+ /** Double-click on the resize handle, and the context-menu "best fit" item. */
907
+ onAutoFit?: (columnId: string) => void;
908
+ /** Right-click on a header cell (Enterprise header menu). */
909
+ onHeaderContextMenu?: (columnId: string, event: React.MouseEvent) => void;
910
+ }
911
+ declare function GridHeaderImpl<TRow = GridRowData>({ bandOffset, stickyInsets, layout, centerColumns, leadingSpacer, trailingSpacer, sort, sortable, onSort, headerFilter, filteredColumns, onOpenFilter, filterIconMode, checkboxColumn, detailColumn, rowNumberColumn, rowNumberHeader, selectAllState, onToggleSelectAll, draggable, reorderable, onReorder, resizable, onResize, onAutoFit, onHeaderContextMenu, }: GridHeaderProps<TRow>): react.JSX.Element;
912
+ declare const GridHeader: typeof GridHeaderImpl;
913
+
914
+ /**
915
+ * Height of one band row. Shorter than a column header on purpose: a band is a
916
+ * grouping caption, and giving it the same weight as the headers underneath
917
+ * makes the real headers harder to scan.
918
+ */
919
+ declare const BAND_ROW_HEIGHT = 26;
920
+ interface GridHeaderBandsProps<TRow = GridRowData> {
921
+ layout: ColumnLayout<TRow>;
922
+ /** The virtualized slice of the centre block, as the header renders it. */
923
+ centerColumns: ReactColumnRuntime<TRow>[];
924
+ leadingSpacer: number;
925
+ trailingSpacer: number;
926
+ /** Number of band rows. Nothing renders at 0. */
927
+ depth: number;
928
+ checkboxColumn?: boolean;
929
+ detailColumn?: boolean;
930
+ /** Leading row-number column (gap analysis P3.5). */
931
+ rowNumberColumn?: boolean;
932
+ /** Height of one band row, in px. Defaults to `BAND_ROW_HEIGHT`. */
933
+ rowHeight?: number;
934
+ /**
935
+ * Folding (gap analysis P2.6). Absent leaves bands display-only, exactly as
936
+ * they were — a grid whose columns declare no `bandShow` never sees a toggle.
937
+ */
938
+ bandFold?: {
939
+ /** Band path of a column, outermost first. */
940
+ pathOf: (columnId: string) => string[];
941
+ collapsible: (path: readonly string[]) => boolean;
942
+ collapsed: (path: readonly string[]) => boolean;
943
+ onToggle: (path: string[]) => void;
944
+ labels: {
945
+ expand: string;
946
+ collapse: string;
947
+ };
948
+ };
949
+ }
950
+ declare function GridHeaderBandsImpl<TRow = GridRowData>({ layout, centerColumns, leadingSpacer, trailingSpacer, depth, checkboxColumn, detailColumn, rowNumberColumn, rowHeight, bandFold, }: GridHeaderBandsProps<TRow>): react.JSX.Element | null;
951
+ declare const GridHeaderBands: typeof GridHeaderBandsImpl;
952
+
953
+ interface GridRowProps<TRow = GridRowData> {
954
+ /** Used to build stable cell ids for aria-activedescendant. */
955
+ gridId: string;
956
+ node: RowNode<TRow>;
957
+ /** Row's ordinal in the view (drives striping). */
958
+ index: number;
959
+ layout: ColumnLayout<TRow>;
960
+ columnModel: ColumnModel<TRow>;
961
+ /** Center columns inside the horizontal window. */
962
+ centerColumns: ReactColumnRuntime<TRow>[];
963
+ /**
964
+ * True index in `layout.center` for each rendered center column.
965
+ *
966
+ * A column's position in `centerColumns` is NOT its position in the grid —
967
+ * the block is a window slice with scrolled-past sticky columns spliced onto
968
+ * the front. Deriving the index from the loop counter made every center
969
+ * column report a smaller `aria-colindex` the further right the grid was
970
+ * scrolled, and put the range anchor and fill handle on the wrong column.
971
+ */
972
+ centerIndices?: readonly number[];
973
+ /** Inset per STICKY center column, by id. See RenderGeometry.stickyInsets. */
974
+ stickyInsets?: ReadonlyMap<string, number>;
975
+ /** Width of the center columns skipped to the left of the window. */
976
+ leadingSpacer: number;
977
+ /** Width of the center columns skipped to the right of the window. */
978
+ trailingSpacer: number;
979
+ striped: boolean;
980
+ selected: boolean;
981
+ /** Column id holding keyboard focus in this row, or null. */
982
+ focusedColumnId: string | null;
983
+ /** Highlight this row as the focused one (`focusedRow` is on). */
984
+ rowFocused?: boolean;
985
+ /**
986
+ * Replace the row's CONTENT with arbitrary markup. The row element itself
987
+ * stays — it carries the height, the aria indices and the click handlers the
988
+ * virtualizer and selection depend on, so a template that owned the whole
989
+ * element would have to reproduce all of it correctly to work at all.
990
+ */
991
+ renderRow?: (ctx: RowTemplateContext<TRow>) => React.ReactNode;
992
+ /**
993
+ * Row drag & drop (gap G7). A sticky GRIP is rendered at the row's inline
994
+ * start rather than a lead column: a third lead column would have to be
995
+ * threaded through `leadColumnWidth`, every sticky offset and every
996
+ * aria-colindex in six components, and this file already warns that those
997
+ * drifting apart is what desynchronizes header and body.
998
+ */
999
+ rowDrag?: RowDragHandlers;
1000
+ /** Which edge to draw the drop line on, or null. */
1001
+ dropSide?: 'before' | 'after' | null;
1002
+ /** Renders the leading checkbox cell. */
1003
+ checkboxColumn: boolean;
1004
+ /** Renders the leading row-number cell (gap analysis P3.5). */
1005
+ rowNumberColumn?: boolean;
1006
+ /**
1007
+ * This cell's note, or null (gap analysis P3.3). Only the MARKER is decided
1008
+ * here — the text itself is revealed by the tooltip layer, which reads the
1009
+ * same source at hover time.
1010
+ *
1011
+ * Called once per rendered cell, so the config's `get` has to be a lookup.
1012
+ */
1013
+ getNote?: (rowKey: RowKey, columnId: string, row: TRow) => string | null | undefined;
1014
+ /**
1015
+ * How this row takes part in a merged block for a column (gap analysis P3.1),
1016
+ * or null when the column does not merge here.
1017
+ *
1018
+ * Resolved by the caller: deciding it needs the run list AND the window's
1019
+ * bounds, and the row knows neither. `height` is already in pixels because
1020
+ * only the caller can ask the engine what the covered rows really measure —
1021
+ * an `autoHeight` row in the middle of a block is not `rowHeight` tall.
1022
+ *
1023
+ * Takes the row index rather than arriving pre-bound to it. A closure per row
1024
+ * defeats this component's memo, and with an `autoHeight` column that turns
1025
+ * into a measure/re-render loop React ends with "Maximum update depth
1026
+ * exceeded".
1027
+ */
1028
+ getSpan?: (rowIndex: number, columnId: string) => {
1029
+ kind: 'draw';
1030
+ height: number;
1031
+ offset: number;
1032
+ } | {
1033
+ kind: 'covered';
1034
+ } | null;
1035
+ /**
1036
+ * The number to show, already resolved (see `rowNumberAt` in geometry).
1037
+ *
1038
+ * Resolved by the caller rather than derived from `index` here, because the
1039
+ * ordinal is NOT the number: group banners occupy view positions, and
1040
+ * pagination offsets the count. A row that computed it itself would have to
1041
+ * know about both.
1042
+ *
1043
+ * `null` means this row is not part of the numbering and gets an empty cell.
1044
+ * Pinned rows are the case: they sit outside the result rather than at a
1045
+ * position in it, and numbering them from their own index made a pinned row
1046
+ * and the first data row both claim to be row 1.
1047
+ */
1048
+ rowNumber?: number | null;
1049
+ /** Number of non-data rows above (header, and the filter row when shown). */
1050
+ headerRowCount: number;
1051
+ /**
1052
+ * The extra arguments are OPTIONAL on purpose, and ordered to match
1053
+ * `onRowContextMenu` below. The grid passes them so the adapter can resolve
1054
+ * WHICH CELL was hit (`closest('[data-column-id]')`) and publish
1055
+ * `cellClicked` / `rowClicked`; a caller that only wants the row keeps
1056
+ * passing a one-argument function, which stays assignable.
1057
+ */
1058
+ onRowClick?: (node: RowNode<TRow>, rowIndex?: number, event?: React.MouseEvent) => void;
1059
+ onRowMouseDown?: (node: RowNode<TRow>, event: React.MouseEvent) => void;
1060
+ onRowDoubleClick?: (node: RowNode<TRow>, rowIndex?: number, event?: React.MouseEvent) => void;
1061
+ onToggleSelect?: (node: RowNode<TRow>) => void;
1062
+ onCellMouseDown?: (node: RowNode<TRow>, columnId: string) => void;
1063
+ /** Right-click on a data row (row menu). */
1064
+ onRowContextMenu?: (node: RowNode<TRow>, rowIndex: number, event: React.MouseEvent) => void;
1065
+ onToggleGroup?: (groupKey: string) => void;
1066
+ /** Tree data (P1.4): expand/collapse from the first column's toggle. */
1067
+ onToggleTreeRow?: (rowKey: RowKey) => void;
1068
+ /**
1069
+ * Bumped by `controller.refreshCells()` (P1.7). Read by nothing — its only
1070
+ * job is to differ, so the row memo lets a re-render through when the DATA
1071
+ * behind a `valueGetter` changed without the node identity changing.
1072
+ */
1073
+ revision?: number;
1074
+ /**
1075
+ * Lower-cased search term to mark up inside cell text (gap analysis P2.4).
1076
+ *
1077
+ * A PROP rather than a read of the controller, so it takes part in the row
1078
+ * memo: when the search changes, the rows that need new markup re-render, and
1079
+ * when it does not, they do not.
1080
+ */
1081
+ searchNeedle?: string;
1082
+ /** Aggregate definitions rendered inside group headers (Enterprise). */
1083
+ summaryDefs?: readonly SummaryDef<TRow>[];
1084
+ /** Render a leading expander cell for master-detail (Enterprise). */
1085
+ detailColumn?: boolean;
1086
+ /** Whether THIS row's detail panel is open. */
1087
+ detailExpanded?: boolean;
1088
+ onToggleDetail?: (rowKey: RowKey) => void;
1089
+ /** Panel body for a detail row (Enterprise slot). */
1090
+ renderDetail?: (row: TRow) => React.ReactNode;
1091
+ /**
1092
+ * Body for an in-grid edit form (Enterprise slot, `mode: 'form'`).
1093
+ *
1094
+ * `row` is undefined for a record that does not exist yet — the form is
1095
+ * still opened, because the alternative is pressing "New row" and seeing
1096
+ * nothing happen.
1097
+ */
1098
+ renderEditForm?: (rowKey: RowKey, row: TRow | undefined) => React.ReactNode;
1099
+ /**
1100
+ * This row's own height, when it is NOT the standard one — a measured
1101
+ * `autoHeight` row, or one `getRowHeight` sized.
1102
+ *
1103
+ * Undefined means standard, and undefined is the case worth optimizing: the
1104
+ * element gets no inline style at all and inherits `--dg-row-height`, so a
1105
+ * grid with uniform rows renders exactly the markup it did before any of this
1106
+ * existed. Panels do not use this — they carry their height on the node.
1107
+ */
1108
+ height?: number;
1109
+ renderCellContent?: (ctx: CellSlotContext<TRow>) => React.ReactNode | undefined;
1110
+ getCellClassName?: (ctx: CellSlotContext<TRow>) => string | undefined;
1111
+ onCellPointerDown?: (ctx: CellSlotContext<TRow>, event: React.MouseEvent) => void;
1112
+ onCellPointerEnter?: (ctx: CellSlotContext<TRow>, event: React.MouseEvent) => void;
1113
+ onCellDoubleClick?: (ctx: CellSlotContext<TRow>) => void;
1114
+ }
1115
+ declare function GridRowImpl<TRow = GridRowData>({ gridId, node, index, layout, columnModel, centerColumns, centerIndices, stickyInsets, leadingSpacer, trailingSpacer, striped, selected, focusedColumnId, rowFocused, renderRow, checkboxColumn, rowNumberColumn, rowNumber, getNote, getSpan, headerRowCount, onRowClick, onRowMouseDown, onRowDoubleClick, onToggleSelect, onCellMouseDown, onRowContextMenu, searchNeedle, onToggleGroup, summaryDefs, detailColumn, detailExpanded, onToggleDetail, onToggleTreeRow, renderDetail, renderEditForm, renderCellContent, getCellClassName, onCellPointerDown, onCellPointerEnter, onCellDoubleClick, rowDrag, dropSide, height, }: GridRowProps<TRow>): react.JSX.Element | null;
1116
+ declare const GridRow: typeof GridRowImpl;
1117
+
1118
+ interface GridFilterRowProps<TRow = GridRowData> {
1119
+ /**
1120
+ * Total height of the band rows above the header, in pixels.
1121
+ *
1122
+ * Published as a custom property and added to the header height IN CSS,
1123
+ * because the header height is density-dependent (28/32/40) and lives in a
1124
+ * variable — computing the sum here would fork that mapping into JS. The
1125
+ * stylesheet used to stick this row at the header height alone, ignoring the
1126
+ * bands, so a banded grid parked the filter row inside the header and the two
1127
+ * drew over each other as soon as the user scrolled.
1128
+ */
1129
+ bandOffset?: number;
1130
+ layout: ColumnLayout<TRow>;
1131
+ centerColumns: ReactColumnRuntime<TRow>[];
1132
+ /** Inset per STICKY center column, by id. See RenderGeometry.stickyInsets. */
1133
+ stickyInsets?: ReadonlyMap<string, number>;
1134
+ leadingSpacer: number;
1135
+ trailingSpacer: number;
1136
+ /** Raw text per column id (controlled by the grid). */
1137
+ values: Record<string, string>;
1138
+ /** Leading non-data columns, so the row lines up with the header. */
1139
+ checkboxColumn?: boolean;
1140
+ detailColumn?: boolean;
1141
+ /** Leading row-number column (gap analysis P3.5). */
1142
+ rowNumberColumn?: boolean;
1143
+ placeholder?: string;
1144
+ onChange: (columnId: string, raw: string) => void;
1145
+ /**
1146
+ * The user has finished typing (Enter, or focus left the box). Applies the
1147
+ * filter without waiting out the debounce.
1148
+ */
1149
+ onCommit?: (columnId: string, raw: string) => void;
1150
+ }
1151
+ /**
1152
+ * Translate a raw input string into a filter node for one column.
1153
+ * Exported because it is pure and worth testing without a DOM.
1154
+ */
1155
+ declare function parseFilterInput(columnId: string, raw: string, dataType: GridDataType | undefined): FilterNode | null;
1156
+ /**
1157
+ * The text that would produce `node` if it were typed into this column's box,
1158
+ * or null when no text would.
1159
+ *
1160
+ * Used to show a restored filter in the box that owns it. The candidate is
1161
+ * checked by parsing it back rather than trusted: a `contains` whose value
1162
+ * happens to start with `>=` would parse as an operator and quietly become a
1163
+ * different filter, and a box whose text does not mean what the grid is
1164
+ * filtering by is worse than an empty one.
1165
+ */
1166
+ declare function filterInputFromNode(columnId: string, node: FilterNode | null, dataType: GridDataType | undefined): string | null;
1167
+ /** What the reconciler needs to know about one column. Kept tiny so it is testable. */
1168
+ interface FilterInputSyncProbe {
1169
+ /** The text is still on its way to the controller behind a debounce. */
1170
+ isPending(columnId: string): boolean;
1171
+ /**
1172
+ * The text this column's box should show for the filter-row part the
1173
+ * controller holds — `''` when it holds none, and null when the part cannot
1174
+ * be written as text (set by code in a shape no box can express).
1175
+ */
1176
+ textFor(columnId: string): string | null;
1177
+ }
1178
+ /**
1179
+ * Bring the boxes back in line with what the controller actually holds.
1180
+ *
1181
+ * The filter row's text is React state, not a view of the controller, so the
1182
+ * two drift in both directions: a `clearColumnFilters()` from the filter panel
1183
+ * left a box reading `Ankara` with nothing filtered, and a restored saved view
1184
+ * filtered rows with every box blank. Since P3.7 the header popup keeps its own
1185
+ * part on the same column, so a lying box is not a lone oddity any more — it is
1186
+ * one of two visible surfaces contradicting the other.
1187
+ *
1188
+ * Two columns are deliberately left alone:
1189
+ * - one with a pending debounce — its text has not reached the controller yet,
1190
+ * so "no filter" means "not yet", not "cleared";
1191
+ * - one whose part cannot be expressed as text (`textFor` returns null), where
1192
+ * writing anything would misrepresent the filter.
1193
+ *
1194
+ * Text the user is in the middle of typing is protected by the caller, which
1195
+ * does not treat its own commits as an external change — otherwise the `>` on
1196
+ * the way to `> 100` would be deleted mid-keystroke.
1197
+ *
1198
+ * Returns `prev` unchanged when nothing needs to move, so React can skip the
1199
+ * re-render entirely.
1200
+ */
1201
+ declare function reconcileFilterInputs(prev: Record<string, string>, probe: FilterInputSyncProbe, columnIds?: readonly string[]): Record<string, string>;
1202
+ declare function GridFilterRowImpl<TRow = GridRowData>({ bandOffset, stickyInsets, layout, centerColumns, leadingSpacer, trailingSpacer, values, checkboxColumn, detailColumn, rowNumberColumn, placeholder, onChange, onCommit, }: GridFilterRowProps<TRow>): react.JSX.Element;
1203
+ declare const GridFilterRow: typeof GridFilterRowImpl;
1204
+
1205
+ interface DistinctEntry {
1206
+ value: unknown;
1207
+ label: string;
1208
+ }
1209
+ interface GridHeaderFilterPopupProps {
1210
+ /** Scopes the popup's remembered scroll/size so two grids do not share it. */
1211
+ gridId?: string;
1212
+ columnId: string;
1213
+ headerLabel: string;
1214
+ entries: DistinctEntry[];
1215
+ /** Labels currently selected; empty means "no filter" (all shown). */
1216
+ initialSelected: string[] | null;
1217
+ anchor: {
1218
+ left: number;
1219
+ top: number;
1220
+ };
1221
+ /**
1222
+ * The values are still being fetched (remote grids). The popup opens straight
1223
+ * away — the user has already said where they want it — and fills in when they
1224
+ * arrive. Apply is disabled meanwhile: applying an EMPTY list would mean
1225
+ * "nothing selected", which on this grid means a filter, not a no-op.
1226
+ */
1227
+ loading?: boolean;
1228
+ dark?: boolean;
1229
+ /**
1230
+ * The chosen filter, or null for "no constraint".
1231
+ *
1232
+ * Only the AST — the ticked labels are deliberately NOT reported. The caller
1233
+ * used to store them and hand them back as `initialSelected`, which made the
1234
+ * ticks a second source of truth that drifted from the filter itself (visibly,
1235
+ * after a page reload). They are now derived from the filter via
1236
+ * `selectedLabelsFromFilter`.
1237
+ */
1238
+ onApply: (filter: FilterNode | null) => void;
1239
+ onClose: () => void;
1240
+ /**
1241
+ * `entries` is only the first page of a longer list.
1242
+ *
1243
+ * The list is capped so a million-value column does not build a million
1244
+ * checkboxes. What was missing is saying so: a capped list looked exactly
1245
+ * like a complete one, so a column of a million ids appeared to hold a
1246
+ * thousand. When this is set the popup shows a notice and routes SEARCH
1247
+ * through `onSearch` instead of filtering what it already has.
1248
+ */
1249
+ truncated?: boolean;
1250
+ /**
1251
+ * Look a needle up across the WHOLE dataset. Without it, searching a capped
1252
+ * list can only find what was already loaded — typing an id past the cap
1253
+ * matched nothing even though the value exists.
1254
+ */
1255
+ onSearch?: (needle: string) => void;
1256
+ labels?: Partial<Record<'search' | 'selectAll' | 'clear' | 'apply' | 'cancel' | 'blank' | 'noMatch' | 'loading' | 'truncated', string>>;
1257
+ }
1258
+ declare function GridHeaderFilterPopup({ gridId, columnId, headerLabel, entries, initialSelected, anchor, loading, dark, onApply, onClose, truncated, onSearch, labels, }: GridHeaderFilterPopupProps): react.JSX.Element;
1259
+
1260
+ interface GridContextMenuProps<TContext> {
1261
+ x: number;
1262
+ y: number;
1263
+ items: GridMenuItem<TContext>[];
1264
+ context: TContext;
1265
+ onClose: () => void;
1266
+ dark?: boolean;
1267
+ /** Aria label for the menu itself. */
1268
+ label?: string;
1269
+ }
1270
+ declare function GridContextMenu<TContext>({ x, y, items, context, onClose, dark, label, }: GridContextMenuProps<TContext>): react.JSX.Element;
1271
+
1272
+ interface GridPortalProps {
1273
+ children: ReactNode;
1274
+ dark?: boolean;
1275
+ }
1276
+ declare function GridPortal({ children, dark }: GridPortalProps): react.ReactPortal | null;
1277
+
1278
+ interface GridGroupPanelProps<TRow = GridRowData> {
1279
+ /** Columns offered for grouping (groupable and visible). */
1280
+ columns: ColumnRuntime<TRow>[];
1281
+ /** Active grouping levels, outermost first. */
1282
+ levels: readonly string[];
1283
+ onSetLevels: (levels: string[]) => void;
1284
+ /** Maximum number of levels the current edition allows. Default 1. */
1285
+ maxLevels?: number;
1286
+ onExpandAll?: () => void;
1287
+ onCollapseAll?: () => void;
1288
+ emptyText?: string;
1289
+ }
1290
+ declare function GridGroupPanelImpl<TRow = GridRowData>({ columns, levels, onSetLevels, maxLevels, onExpandAll, onCollapseAll, emptyText, }: GridGroupPanelProps<TRow>): react.JSX.Element;
1291
+ declare const GridGroupPanel: typeof GridGroupPanelImpl;
1292
+
1293
+ interface GridSummaryBarProps<TRow = GridRowData> {
1294
+ defs: readonly SummaryDef<TRow>[];
1295
+ values: Record<string, number | null>;
1296
+ columnModel: ColumnModel<TRow>;
1297
+ /** Row count shown alongside the totals. */
1298
+ rowCount?: number;
1299
+ rowCountLabel?: string;
1300
+ }
1301
+ declare function GridSummaryBarImpl<TRow = GridRowData>({ defs, values, columnModel, rowCount, rowCountLabel, }: GridSummaryBarProps<TRow>): react.JSX.Element | null;
1302
+ declare const GridSummaryBar: typeof GridSummaryBarImpl;
1303
+
1304
+ interface GridFooterRowProps<TRow = GridRowData> {
1305
+ layout: ColumnLayout<TRow>;
1306
+ centerColumns: ReactColumnRuntime<TRow>[];
1307
+ leadingSpacer: number;
1308
+ trailingSpacer: number;
1309
+ /** Every definition currently active, keyed to columns by `columnId`. */
1310
+ defs: readonly SummaryDef<TRow>[];
1311
+ values: Record<string, number | null>;
1312
+ checkboxColumn?: boolean;
1313
+ detailColumn?: boolean;
1314
+ /** Leading row-number column (gap analysis P3.5). */
1315
+ rowNumberColumn?: boolean;
1316
+ /** Opens the aggregation menu for a column. */
1317
+ onPickSummary?: (columnId: string, event: React.MouseEvent) => void;
1318
+ /** Shown in a cell with no aggregation, as the affordance for the menu. */
1319
+ emptyHint?: string;
1320
+ /**
1321
+ * Which edge the totals stick to (P2.8). Bottom is the default and the
1322
+ * spreadsheet convention; top is for a grid read top-down, where the totals
1323
+ * are the headline and the rows are the evidence.
1324
+ */
1325
+ position?: 'top' | 'bottom';
1326
+ /** Pixels of BAND rows above — only meaningful at the top. */
1327
+ bandOffset?: number;
1328
+ /** Header-height rows between this row and its edge. See GridPinnedRows. */
1329
+ headerRowsBetween?: number;
1330
+ }
1331
+ declare function GridFooterRowImpl<TRow = GridRowData>({ layout, centerColumns, leadingSpacer, trailingSpacer, defs, values, checkboxColumn, detailColumn, rowNumberColumn, onPickSummary, emptyHint, position, bandOffset, headerRowsBetween, }: GridFooterRowProps<TRow>): react.JSX.Element;
1332
+ declare const GridFooterRow: typeof GridFooterRowImpl;
1333
+
1334
+ interface GridFilterPanelProps<TRow = GridRowData> {
1335
+ controller: DataGridController<TRow>;
1336
+ /**
1337
+ * Opens whatever built the filter. Omitted when the grid has no builder — a
1338
+ * chevron that opens nothing is worse than no chevron.
1339
+ */
1340
+ onOpenBuilder?: () => void;
1341
+ /**
1342
+ * Keep the bar visible with "no filter" when nothing is filtered.
1343
+ * Default false: an empty bar is a permanent strip of chrome explaining that
1344
+ * nothing is happening.
1345
+ */
1346
+ alwaysVisible?: boolean;
1347
+ /** Wording for the conditions themselves — `and` / `or` / `is` / `blank`… */
1348
+ describeOptions?: DescribeFilterOptions;
1349
+ labels?: Partial<Record<'clear' | 'toggle' | 'builder' | 'empty', string>>;
1350
+ }
1351
+ declare function GridFilterPanelImpl<TRow = GridRowData>({ controller, onOpenBuilder, alwaysVisible, describeOptions, labels, }: GridFilterPanelProps<TRow>): react.JSX.Element | null;
1352
+ declare const GridFilterPanel: typeof GridFilterPanelImpl;
1353
+
1354
+ /** English captions, kept as the exported constant this package already had. */
1355
+ declare const SUMMARY_TYPE_LABEL: Record<SummaryType, string>;
1356
+ /** The same map in the grid's own language. */
1357
+ declare function summaryTypeLabels(t: GridDictionary): Record<SummaryType, string>;
1358
+ /**
1359
+ * What the footer prints beside the value. A custom reducer's own `label` wins
1360
+ * — "Custom" beside a number tells the reader nothing about what it measures.
1361
+ *
1362
+ * `labels` is the resolved dictionary's map; omitted, it falls back to English
1363
+ * so the helper stays usable outside a grid (it is exported).
1364
+ */
1365
+ declare function summaryLabel<TRow = unknown>(def: SummaryDef<TRow>, labels?: Record<SummaryType, string>): string;
1366
+ declare function formatSummaryValue<TRow>(def: SummaryDef<TRow>, value: number | null | undefined, column: GridColumnDef<TRow> | undefined): string;
1367
+ /**
1368
+ * Aggregations that make sense for a column's data type.
1369
+ *
1370
+ * `sum`/`avg` over text would coerce every cell through `Number()` and total a
1371
+ * column of names as 0 — a number that looks authoritative and means nothing.
1372
+ * min/max stay available for dates because ordering is defined there.
1373
+ */
1374
+ declare function allowedSummaryTypes(dataType: GridColumnDef['dataType']): readonly SummaryType[];
1375
+
1376
+ interface GridToolbarProps<TRow = GridRowData> {
1377
+ controller: DataGridController<TRow>;
1378
+ search?: boolean;
1379
+ exportCsv?: boolean;
1380
+ exportExcel?: boolean;
1381
+ /**
1382
+ * Offer PDF. Off by default for the same reason Excel is: `jspdf` and
1383
+ * `jspdf-autotable` are OPTIONAL peers, so a menu entry that is always there
1384
+ * would fail on a grid that never installed them.
1385
+ */
1386
+ exportPdf?: boolean;
1387
+ columnChooser?: boolean;
1388
+ dark?: boolean;
1389
+ /**
1390
+ * Opens the field chooser anchored under the button. The panel itself is
1391
+ * owned by the grid, because the header menu can open it in a grid that
1392
+ * renders no toolbar.
1393
+ */
1394
+ onOpenColumnChooser?: (anchor: {
1395
+ left: number;
1396
+ top: number;
1397
+ }) => void;
1398
+ /**
1399
+ * Extra controls. `before` sits ahead of the built-ins, `after` behind them —
1400
+ * the spacer between search and the buttons keeps the two groups apart.
1401
+ *
1402
+ * Nodes rather than a descriptor object: a toolbar item is arbitrary UI (a
1403
+ * date range, a saved-view picker, a refresh button wired to the app's own
1404
+ * store), and every descriptor schema ends up chasing the next widget.
1405
+ */
1406
+ itemsBefore?: React.ReactNode;
1407
+ itemsAfter?: React.ReactNode;
1408
+ /** Milliseconds to wait after the last keystroke. Default 200. */
1409
+ searchDebounce?: number;
1410
+ labels?: Partial<Record<'search' | 'csv' | 'excel' | 'excelSelected' | 'pdf' | 'columns' | 'exporting' | 'exportMenu', string>>;
1411
+ }
1412
+ declare function GridToolbarImpl<TRow = GridRowData>({ controller, search, exportCsv, exportExcel, exportPdf, columnChooser, dark, onOpenColumnChooser, itemsBefore, itemsAfter, searchDebounce, labels, }: GridToolbarProps<TRow>): react.JSX.Element;
1413
+ declare const GridToolbar: typeof GridToolbarImpl;
1414
+
1415
+ interface GridColumnChooserProps<TRow = GridRowData> {
1416
+ controller: DataGridController<TRow>;
1417
+ /** Passed in so the panel re-renders when visibility changes. */
1418
+ columns: ColumnState[];
1419
+ anchor: {
1420
+ left: number;
1421
+ top: number;
1422
+ };
1423
+ onClose: () => void;
1424
+ dark?: boolean;
1425
+ labels?: Partial<Record<'title' | 'empty' | 'showAll' | 'ok', string>>;
1426
+ }
1427
+ declare function GridColumnChooser<TRow = GridRowData>({ controller, columns, anchor, onClose, dark, labels, }: GridColumnChooserProps<TRow>): react.JSX.Element;
1428
+
1429
+ interface GridLoadingOverlayProps<TRow> {
1430
+ controller: DataGridController<TRow>;
1431
+ /** Milliseconds of continuous work before the overlay appears. Default 300. */
1432
+ delay?: number;
1433
+ labels?: Partial<Record<BusyPhase | 'busy' | 'cancel', string>>;
1434
+ /**
1435
+ * Offer a Cancel button for phases that can actually stop. Default true.
1436
+ *
1437
+ * Only shown for the off-thread phases — the ones that check between chunks.
1438
+ * A button that does nothing is worse than no button.
1439
+ */
1440
+ cancellable?: boolean;
1441
+ /**
1442
+ * Show it regardless of what the grid is doing, and without the delay
1443
+ * (gap analysis P2.11) — `controller.showLoadingOverlay()` sets this.
1444
+ *
1445
+ * The delay exists to stop the overlay strobing over work that finishes in a
1446
+ * few milliseconds. An explicit call is a decision, not a flicker to absorb,
1447
+ * and the application making it usually has nothing else to show yet.
1448
+ */
1449
+ forced?: boolean;
1450
+ }
1451
+ declare function GridLoadingOverlay<TRow>({ controller, delay, labels, cancellable, forced, }: GridLoadingOverlayProps<TRow>): react.JSX.Element | null;
1452
+
1453
+ interface GridPopupPanelProps {
1454
+ anchor: {
1455
+ left: number;
1456
+ top: number;
1457
+ };
1458
+ onClose: () => void;
1459
+ children: ReactNode;
1460
+ dark?: boolean;
1461
+ className?: string;
1462
+ label: string;
1463
+ role?: 'dialog' | 'menu';
1464
+ }
1465
+ declare function GridPopupPanel({ anchor, onClose, children, dark, className, label, role, }: GridPopupPanelProps): react.JSX.Element;
1466
+
1467
+ interface GridNoteEditorProps {
1468
+ anchor: {
1469
+ left: number;
1470
+ top: number;
1471
+ };
1472
+ /** Text already on the cell, or '' for a new note. */
1473
+ value: string;
1474
+ dark?: boolean;
1475
+ /** `null` means the user cleared it — delete the note. */
1476
+ onCommit: (text: string | null) => void;
1477
+ onCancel: () => void;
1478
+ }
1479
+ declare function GridNoteEditor({ anchor, value, dark, onCommit, onCancel }: GridNoteEditorProps): react.JSX.Element;
1480
+
1481
+ interface GridPagerProps {
1482
+ page: number;
1483
+ pageCount: number;
1484
+ pageSize: number;
1485
+ pageSizes?: number[];
1486
+ /** Rows after filtering (the thing being paged through). */
1487
+ totalRows: number;
1488
+ /** Absolute jump (first/last). */
1489
+ onPage: (page: number) => void;
1490
+ /**
1491
+ * RELATIVE moves go through their own callbacks so they read the controller's
1492
+ * current page instead of this component's prop: two clicks landing in the
1493
+ * same React commit would otherwise both compute `page + 1` from the same
1494
+ * stale value and advance only one page.
1495
+ */
1496
+ onNext: () => void;
1497
+ onPrevious: () => void;
1498
+ onPageSize: (size: number) => void;
1499
+ labels?: Partial<Record<'first' | 'previous' | 'next' | 'last' | 'page' | 'of' | 'rowsPerPage', string>>;
1500
+ }
1501
+ declare function GridPagerImpl({ page, pageCount, pageSize, pageSizes, totalRows, onPage, onNext, onPrevious, onPageSize, labels, }: GridPagerProps): react.JSX.Element;
1502
+ declare const GridPager: react.MemoExoticComponent<typeof GridPagerImpl>;
1503
+
1504
+ /** Labels, so the menu can be localized without rebuilding the item list. */
1505
+ interface HeaderMenuLabels {
1506
+ sortAsc: string;
1507
+ sortDesc: string;
1508
+ clearSort: string;
1509
+ groupBy: string;
1510
+ ungroup: string;
1511
+ showGroupPanel: string;
1512
+ hideGroupPanel: string;
1513
+ showFooter: string;
1514
+ hideFooter: string;
1515
+ showGroupFooters: string;
1516
+ hideGroupFooters: string;
1517
+ hideColumn: string;
1518
+ fieldChooser: string;
1519
+ bestFit: string;
1520
+ bestFitAll: string;
1521
+ fixedPosition: string;
1522
+ pinLeft: string;
1523
+ pinRight: string;
1524
+ pinSticky: string;
1525
+ unpin: string;
1526
+ moveLeft: string;
1527
+ moveRight: string;
1528
+ }
1529
+
1530
+ /** The menu's captions in one language. */
1531
+ declare function headerMenuLabels(t: GridDictionary): HeaderMenuLabels;
1532
+ /** English, kept as the constant this package already exported. */
1533
+ declare const DEFAULT_HEADER_MENU_LABELS: HeaderMenuLabels;
1534
+ interface HeaderMenuDeps<TRow = GridRowData> {
1535
+ controller: DataGridController<TRow>;
1536
+ /** Current UI switches the menu toggles operate on. */
1537
+ groupPanelVisible: boolean;
1538
+ footerVisible: boolean;
1539
+ /** False in Community — per-group aggregates are Enterprise (decision #4). */
1540
+ groupFootersAvailable: boolean;
1541
+ groupFootersVisible: boolean;
1542
+ onToggleGroupPanel: () => void;
1543
+ onToggleFooter: () => void;
1544
+ onToggleGroupFooters: () => void;
1545
+ onOpenFieldChooser: () => void;
1546
+ onBestFit: (columnId: string) => void;
1547
+ onBestFitAll: () => void;
1548
+ /** Grouping levels allowed — 1 in Community, unbounded in Enterprise. */
1549
+ maxGroupLevels: number;
1550
+ labels?: Partial<HeaderMenuLabels>;
1551
+ }
1552
+ declare function buildHeaderMenu<TRow = GridRowData>(context: HeaderMenuContext, deps: HeaderMenuDeps<TRow>): GridMenuItem<HeaderMenuContext>[];
1553
+
1554
+ /**
1555
+ * Width that fits the header and every rendered cell of `columnId`.
1556
+ * Returns null when nothing could be measured (no such column on screen, or no
1557
+ * canvas — jsdom, SSR), so the caller can leave the width alone.
1558
+ */
1559
+ declare function measureBestFitWidth(root: HTMLElement, columnId: string): number | null;
1560
+
1561
+ interface AnchoredPopupOptions {
1562
+ /** Where the popup wants to appear, in viewport coordinates. */
1563
+ anchor: {
1564
+ left: number;
1565
+ top: number;
1566
+ };
1567
+ /** Called on outside click, Escape, or a scroll under the popup. */
1568
+ onClose: () => void;
1569
+ /** Gap kept from the viewport edges. Default 8. */
1570
+ margin?: number;
1571
+ /** Focus the popup when it attaches. Default false. */
1572
+ autoFocus?: boolean;
1573
+ }
1574
+ interface AnchoredPopupResult<T extends HTMLElement> {
1575
+ /** Spread onto the popup's style. */
1576
+ position: {
1577
+ left: number;
1578
+ top: number;
1579
+ };
1580
+ /** Attach to the popup element's `ref`. */
1581
+ ref: (el: T | null) => void;
1582
+ /** The live element, for callers that need it. */
1583
+ elementRef: React.MutableRefObject<T | null>;
1584
+ }
1585
+ declare function useAnchoredPopup<T extends HTMLElement>({ anchor, onClose, margin, autoFocus, }: AnchoredPopupOptions): AnchoredPopupResult<T>;
1586
+
1587
+ interface AlignedGridsApi {
1588
+ /** Called from the grid's scroll handler with the PHYSICAL scrollLeft. */
1589
+ syncScroll: (scrollLeft: number) => void;
1590
+ }
1591
+ /**
1592
+ * Keep this grid's column layout and horizontal scroll in step with the others
1593
+ * in `group`.
1594
+ *
1595
+ * A group of one is the ordinary case and costs a Map lookup: the hook
1596
+ * registers, finds no peers, and every propagation returns immediately.
1597
+ */
1598
+ declare function useAlignedGrids<TRow = GridRowData>(controller: DataGridController<TRow>, group: string | undefined, scrollerRef: React.RefObject<HTMLElement | null>): AlignedGridsApi;
1599
+
1600
+ /** What the tooltip should say and where it should point. */
1601
+ interface TooltipState {
1602
+ text: string;
1603
+ /** Viewport rectangle of the element being described. */
1604
+ anchor: {
1605
+ top: number;
1606
+ left: number;
1607
+ width: number;
1608
+ height: number;
1609
+ };
1610
+ }
1611
+ interface CellTooltipOptions<TRow = GridRowData> {
1612
+ rootRef: RefObject<HTMLElement | null>;
1613
+ columnModel: ColumnModel<TRow>;
1614
+ /**
1615
+ * This cell's note (P3.3), read at hover time. A function rather than a map
1616
+ * so the hook never holds a snapshot that the app has since edited.
1617
+ */
1618
+ getNote?: (rowKey: RowKey, columnId: string, row: TRow) => string | null | undefined;
1619
+ /**
1620
+ * Resolves a row node from the RAW `data-row-key` attribute — a string, even
1621
+ * when the grid's keys are numbers. Coercing is the caller's job because only
1622
+ * it knows which type the keys really are.
1623
+ */
1624
+ getRowNode: (rawKey: string) => DataRowNode<TRow> | undefined;
1625
+ config?: GridTooltipConfig;
1626
+ }
1627
+ declare function useCellTooltip<TRow = GridRowData>({ rootRef, columnModel, getRowNode, getNote, config, }: CellTooltipOptions<TRow>): TooltipState | null;
1628
+
1629
+ interface GridTooltipProps {
1630
+ state: TooltipState;
1631
+ dark?: boolean;
1632
+ }
1633
+ declare function GridTooltip({ state, dark }: GridTooltipProps): react.JSX.Element;
1634
+
1635
+ interface GridPinnedRowsProps<TRow = GridRowData> {
1636
+ gridId: string;
1637
+ /** Which edge — decides the sticky side and the border. */
1638
+ side: 'top' | 'bottom';
1639
+ rows: readonly TRow[];
1640
+ layout: ColumnLayout<TRow>;
1641
+ columnModel: ColumnModel<TRow>;
1642
+ centerColumns: ReactColumnRuntime<TRow>[];
1643
+ centerIndices?: readonly number[];
1644
+ stickyInsets?: ReadonlyMap<string, number>;
1645
+ leadingSpacer: number;
1646
+ trailingSpacer: number;
1647
+ checkboxColumn?: boolean;
1648
+ detailColumn?: boolean;
1649
+ /** Leading row-number column (gap analysis P3.5). */
1650
+ rowNumberColumn?: boolean;
1651
+ rowHeight: number;
1652
+ /** Pixels of BAND rows above. Zero on a grid without column bands. */
1653
+ bandOffset?: number;
1654
+ /**
1655
+ * How many header-height rows sit between this block and its sticky edge:
1656
+ * above, the header (1) plus the filter row when shown (2); below, the footer
1657
+ * row (1) or nothing (0).
1658
+ *
1659
+ * A count rather than a pixel offset because the header's height is a THEME
1660
+ * token — `--dg-header-height` changes with the theme and the density, and a
1661
+ * number measured in JS at render time would be the previous theme's.
1662
+ */
1663
+ headerRowsBetween: number;
1664
+ /**
1665
+ * Rows that precede this block in the ARIA row numbering: the header rows for
1666
+ * a top block, plus the pinned-top rows and every data row for a bottom one.
1667
+ *
1668
+ * A pinned row IS a row to a screen reader even though it is not one to the
1669
+ * row model, so it has to take a number in the same sequence — and the data
1670
+ * rows have to move down to make room. `DataGrid` does both from one count.
1671
+ */
1672
+ ariaRowBase: number;
1673
+ }
1674
+ declare function GridPinnedRowsImpl<TRow = GridRowData>({ gridId, side, rows, layout, columnModel, centerColumns, centerIndices, stickyInsets, leadingSpacer, trailingSpacer, checkboxColumn, detailColumn, rowNumberColumn, rowHeight, bandOffset, headerRowsBetween, ariaRowBase, }: GridPinnedRowsProps<TRow>): react.JSX.Element | null;
1675
+ declare const GridPinnedRows: typeof GridPinnedRowsImpl;
1676
+
1677
+ type UseDataGridOptions<TRow = GridRowData> = DataGridCoreConfig<TRow>;
1678
+ interface UseDataGridResult<TRow = GridRowData> {
1679
+ controller: DataGridController<TRow>;
1680
+ rows: RowNode<TRow>[];
1681
+ window: VirtualWindow;
1682
+ layout: ColumnLayout<TRow>;
1683
+ sort: SortDescriptor[];
1684
+ rowHeight: number;
1685
+ /** Rows passing filter/search. */
1686
+ visibleCount: number;
1687
+ /** Rows in the source data. */
1688
+ totalCount: number;
1689
+ }
1690
+ declare function useDataGrid<TRow = GridRowData>(options: UseDataGridOptions<TRow>): UseDataGridResult<TRow>;
1691
+
1692
+ declare function useGridChartData<TRow = GridRowData>(controller: DataGridController<TRow> | null,
1693
+ /** Memoize this — a new reference recomputes. Null renders no chart. */
1694
+ spec: ChartSpec<TRow> | null): ChartModel | null;
1695
+
1696
+ declare const SHORTCUT_COPY: string;
1697
+ declare const SHORTCUT_CUT: string;
1698
+ declare const SHORTCUT_PASTE: string;
1699
+ /**
1700
+ * Write the grid's current clipboard text and publish the gesture.
1701
+ *
1702
+ * The single copy implementation, shared by Ctrl+C and by every Copy entry in
1703
+ * the menu. `performed` is false when there was nothing to copy or the
1704
+ * platform withholds the async clipboard — the bracket still closes, because a
1705
+ * listener asked to be told about the gesture, not only about its successes.
1706
+ */
1707
+ declare function copyToClipboard<TRow>(controller: DataGridController<TRow>, options?: {
1708
+ includeHeaders?: boolean;
1709
+ includeGroupHeaders?: boolean;
1710
+ }): void;
1711
+ /**
1712
+ * Write to the system clipboard, swallowing the platform's refusal.
1713
+ *
1714
+ * `writeText` REJECTS when the document is not focused or permission is
1715
+ * withheld, and an un-awaited promise then surfaces as an uncaught rejection
1716
+ * in the console — noise that looks like a grid bug and is not one. Swallowed
1717
+ * rather than reported because the caller has already published the gesture
1718
+ * through `notifyClipboardGesture`, which is the hook an application uses to
1719
+ * observe (or replace) copying.
1720
+ */
1721
+ declare function writeClipboardText(text: string): void;
1722
+ /**
1723
+ * Build the built-in entries for one opened menu.
1724
+ *
1725
+ * `needsSeparator` is the caller telling us whether anything sits above us —
1726
+ * the divider belongs to the boundary, not to this list, so an app menu that
1727
+ * turned out to be empty does not leave a rule floating at the top.
1728
+ */
1729
+ declare function buildCellMenuItems<TRow = GridRowData>(controller: DataGridController<TRow>, text: GridDictionary, option: boolean | CellMenuOptions | undefined, needsSeparator: boolean, onExportError?: (message: string) => void): GridMenuItem<RowMenuContext<TRow>>[];
1730
+
1731
+ interface GridSurfaceProviderProps {
1732
+ theme?: GridTheme;
1733
+ density?: GridDensity;
1734
+ palette?: GridPalette;
1735
+ children: ReactNode;
1736
+ }
1737
+ declare function GridSurfaceProvider({ theme, density, palette, children, }: GridSurfaceProviderProps): react.JSX.Element;
1738
+ /**
1739
+ * The theme + density modifier classes for a surface rendered outside the grid.
1740
+ * `''` when the grid is unthemed, so appending it is always safe.
1741
+ */
1742
+ declare function useGridSurfaceClass(): string;
1743
+
1744
+ interface GridTextProviderProps {
1745
+ locale?: string;
1746
+ messages?: Partial<GridDictionary>;
1747
+ children: ReactNode;
1748
+ }
1749
+ declare function GridTextProvider({ locale, messages, children }: GridTextProviderProps): react.JSX.Element;
1750
+ /** The resolved dictionary. Stable between language changes. */
1751
+ declare function useGridText(): GridDictionary;
1752
+ /**
1753
+ * Substitute `{name}` placeholders.
1754
+ *
1755
+ * Deliberately not a template engine: the dictionary's placeholders are a fixed
1756
+ * contract checked by `locales.test.ts`, so anything cleverer would be a
1757
+ * feature nobody asked for on a hot path.
1758
+ */
1759
+ declare function fillText(template: string, values: Record<string, string | number>): string;
1760
+
1761
+ export { type AlignedGridsApi, BAND_ROW_HEIGHT, type CellMenuOptions, type CellRenderContext, type CellSlotContext, type CellTooltipOptions, DEFAULT_HEADER_MENU_LABELS, DENSITY_ROW_HEIGHT, DataGrid, type DataGridProps, type DistinctEntry, type EditorRenderContext, type ExportOutcome, GRID_PALETTES, GRID_THEMES, GridColumnChooser, type GridColumnChooserProps, GridContextMenu, type GridContextMenuProps, type GridDensity, GridFilterPanel, type GridFilterPanelProps, GridFilterRow, type GridFilterRowProps, GridFooterRow, type GridFooterRowProps, GridGroupPanel, type GridGroupPanelProps, GridHeader, GridHeaderBands, type GridHeaderBandsProps, GridHeaderFilterPopup, type GridHeaderFilterPopupProps, type GridHeaderProps, GridLoadingOverlay, type GridLoadingOverlayProps, type GridMenuItem, GridNoteEditor, type GridNotesConfig, GridPager, type GridPagerProps, type GridPalette, GridPinnedRows, type GridPinnedRowsProps, GridPopupPanel, type GridPopupPanelProps, GridPortal, type GridPortalProps, GridRow, type GridRowProps, GridSummaryBar, type GridSummaryBarProps, GridSurfaceProvider, type GridSurfaceProviderProps, GridTextProvider, type GridTextProviderProps, type GridTheme, GridToolbar, type GridToolbarProps, GridTooltip, type GridTooltipConfig, type GridTooltipProps, type HeaderMenuContext, type HeaderMenuDeps, type HeaderMenuLabels, type HeaderRenderContext, type NoteCellContext, PRINT_MAX_ROWS, type PrintLayoutOptions, type PrintLayoutState, ROW_DRAG_MIME, type ReactColumnRuntime, type ReactGridColumnDef, type RowDragConfig, type RowDragHandlers, type RowDragPayload, type RowDropEvent, type RowMenuContext, type RowReorderEvent, type RowTemplateContext, SHORTCUT_COPY, SHORTCUT_CUT, SHORTCUT_PASTE, SUMMARY_TYPE_LABEL, THEME_ROW_HEIGHT, type TooltipState, type UseDataGridOptions, type UseDataGridResult, allowedSummaryTypes, buildCellMenuItems, buildHeaderMenu, copyToClipboard, fillText, filterInputFromNode, formatSummaryValue, headerMenuLabels, measureBestFitWidth, parseFilterInput, reconcileFilterInputs, runGridExport, summaryLabel, summaryTypeLabels, themeClassName, useAlignedGrids, useAnchoredPopup, useCellTooltip, useDataGrid, useGridChartData, useGridSurfaceClass, useGridText, usePrintLayout, useRowDrag, useStateStoring, writeClipboardText };