@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.
- package/LICENSE +55 -0
- package/README.md +246 -0
- package/dist/index.cjs +2 -0
- package/dist/index.d.cts +1761 -0
- package/dist/index.d.ts +1761 -0
- package/dist/index.js +2 -0
- package/dist/styles.css +3497 -0
- package/package.json +68 -0
package/dist/index.d.ts
ADDED
|
@@ -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 };
|