@kanunilabs/datagrid 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,501 @@
1
+ import { GridRowData, DataGridController, GridDictionary, RowNode, GridColumnDef, DataGridCoreConfig, GridTheme, GridPalette, GridDensity, DescribeFilterOptions, RowKey, StateStoringConfig } from '@kanunilabs/datagrid-core';
2
+ export { DENSITY_ROW_HEIGHT, DataGridController, GridColumnDef, GridDensity, GridRowData, GridTheme, RowKey, RowNode, SortDescriptor, THEME_ROW_HEIGHT, presetRowHeight } from '@kanunilabs/datagrid-core';
3
+
4
+ /** What a header menu is opened against — the React shape, verbatim. */
5
+ interface HeaderMenuContext {
6
+ columnId: string;
7
+ /** Header text as rendered, for labels and announcements. */
8
+ headerName: string;
9
+ }
10
+ interface HeaderMenuDeps<TRow = GridRowData> {
11
+ controller: DataGridController<TRow>;
12
+ t: GridDictionary;
13
+ /** Current UI switches the menu toggles operate on. */
14
+ groupPanelVisible: boolean;
15
+ footerVisible: boolean;
16
+ onToggleGroupPanel: () => void;
17
+ onToggleFooter: () => void;
18
+ onOpenFieldChooser: () => void;
19
+ onBestFit: (columnId: string) => void;
20
+ onBestFitAll: () => void;
21
+ /** Grouping levels allowed — 1 in Community, the React rule. */
22
+ maxGroupLevels: number;
23
+ }
24
+ declare function buildHeaderMenu<TRow = GridRowData>(context: HeaderMenuContext, deps: HeaderMenuDeps<TRow>): GridMenuItem<HeaderMenuContext>[];
25
+
26
+ /** Same value as the React renderer's lead column. */
27
+ declare const ROW_NUMBER_COLUMN_WIDTH = 60;
28
+ /** Same value as the React renderer's CHECKBOX_COLUMN_WIDTH. */
29
+ declare const CHECKBOX_COLUMN_WIDTH = 36;
30
+ /** Same value as the React renderer's master-detail expander column. */
31
+ declare const DETAIL_COLUMN_WIDTH = 28;
32
+ /** MIME type carrying a dragged row. The SAME string the React renderer uses,
33
+ * so a row can cross between a React grid and a vanilla grid on one page. */
34
+ declare const ROW_DRAG_MIME = "application/x-kanuni-row";
35
+ /** What travels in `dataTransfer` when a row leaves its grid. */
36
+ interface RowDragPayload {
37
+ /** Grid the row started in — a same-grid drop is a REORDER, not a move. */
38
+ gridId: string;
39
+ /** Only grids sharing a group exchange rows. */
40
+ group: string | null;
41
+ rowKey: RowKey;
42
+ /** The row itself, so the receiving grid can insert it without a round trip. */
43
+ row: unknown;
44
+ }
45
+ interface RowReorderEvent<TRow = GridRowData> {
46
+ dragKey: RowKey;
47
+ /** Row it was dropped onto, or null for the empty space past the last row. */
48
+ targetKey: RowKey | null;
49
+ position: 'before' | 'after';
50
+ row: TRow | undefined;
51
+ }
52
+ interface RowDropEvent<TRow = GridRowData> {
53
+ /** Row that crossed over, as the source grid had it. */
54
+ row: TRow;
55
+ rowKey: RowKey;
56
+ /** Grid it came from / went to. */
57
+ otherGridId: string;
58
+ targetKey: RowKey | null;
59
+ position: 'before' | 'after';
60
+ }
61
+ /** The React `rowDrag` prop's shape, DOM-flavoured. */
62
+ interface RowDragConfig<TRow = GridRowData> {
63
+ enabled?: boolean;
64
+ /** Grids sharing a group can exchange rows. */
65
+ group?: string;
66
+ /** Reordering within this grid. Default true. */
67
+ allowReordering?: boolean;
68
+ /** Accept rows from another grid in the group. Default true when `group` is set. */
69
+ allowDropInsert?: boolean;
70
+ /** A row was moved within this grid. Without a handler the grid reorders its
71
+ * own source array; with one, YOU own the order. */
72
+ onReorder?: (event: RowReorderEvent<TRow>) => void;
73
+ /** A row arrived from another grid. */
74
+ onAdd?: (event: RowDropEvent<TRow>) => void;
75
+ }
76
+ /** Default row cap. Report-sized results print; result sets do not. */
77
+ declare const PRINT_MAX_ROWS = 5000;
78
+ interface PrintLayoutOptions {
79
+ /** Stay in print layout permanently — for a grid rendered INTO a report page. */
80
+ always?: boolean;
81
+ /** Above this many rows the grid does NOT switch (default 5.000): print
82
+ * layout puts every row in the DOM, and past report sizes that is a frozen
83
+ * tab behind a dialog the user cannot cancel. Use exportPdf() there. */
84
+ maxRows?: number;
85
+ }
86
+ /** What a row template is handed — the React `RowTemplateContext`. */
87
+ interface RowTemplateContext<TRow> {
88
+ row: TRow;
89
+ node: RowNode<TRow>;
90
+ /** Position in the current view, not in the source array. */
91
+ rowIndex: number;
92
+ selected: boolean;
93
+ /** True when this is the focused row (`focusedRow` is on). */
94
+ focused: boolean;
95
+ }
96
+ /** The React `GridMenuItem`, DOM-flavoured: `icon` is a string or a Node. */
97
+ interface GridMenuItem<TContext = unknown> {
98
+ /** Stable key; falls back to the index when omitted. */
99
+ id?: string;
100
+ label?: string;
101
+ icon?: string | Node;
102
+ /** Right-aligned hint, e.g. "Ctrl+C". Purely visual — no binding is created. */
103
+ shortcut?: string;
104
+ disabled?: boolean;
105
+ /** Renders a divider instead of an item. */
106
+ separator?: boolean;
107
+ /** One level of nesting. */
108
+ items?: GridMenuItem<TContext>[];
109
+ onSelect?: (context: TContext) => void;
110
+ }
111
+ /** What a row menu is opened against — the React shape, verbatim. */
112
+ interface RowMenuContext<TRow = GridRowData> {
113
+ row: TRow;
114
+ node: RowNode<TRow>;
115
+ /** Index in the current view. */
116
+ rowIndex: number;
117
+ /** Column under the pointer, when the menu was opened from a cell. */
118
+ columnId: string | null;
119
+ /** Selected row keys at the moment the menu opened. */
120
+ selectedKeys: RowKey[];
121
+ }
122
+ /**
123
+ * What the Enterprise cell slots are handed — the React `CellSlotContext`,
124
+ * verbatim. `rowIndex`/`columnIndex` are VIEW coordinates (a range is a
125
+ * rectangle over what the user sees).
126
+ */
127
+ interface CellSlotContext<TRow = GridRowData> {
128
+ node: RowNode<TRow>;
129
+ columnId: string;
130
+ rowIndex: number;
131
+ columnIndex: number;
132
+ value: unknown;
133
+ }
134
+ /** Which cell a note belongs to — the React `NoteCellContext`, verbatim. */
135
+ interface NoteCellContext<TRow = GridRowData> {
136
+ rowKey: RowKey;
137
+ columnId: string;
138
+ row: TRow;
139
+ }
140
+ /**
141
+ * Cell notes (P3.3) — the React `GridNotesConfig`, verbatim. The grid SHOWS
142
+ * notes (marker + tooltip) and, with `onChange`, collects edits; it does not
143
+ * decide where they live, because a note that survives a reload has to go
144
+ * somewhere only the app knows about.
145
+ */
146
+ interface GridNotesConfig<TRow = GridRowData> {
147
+ /** This cell's note, or null. Called for every RENDERED cell — make it a lookup. */
148
+ get: (ctx: NoteCellContext<TRow>) => string | null | undefined;
149
+ /** The user changed a note; `text` is null when they cleared it. Its PRESENCE makes notes editable. */
150
+ onChange?: (ctx: NoteCellContext<TRow> & {
151
+ text: string | null;
152
+ }) => void;
153
+ /** Append add/edit/delete items to the row context menu. Default true when `onChange` is given. */
154
+ menu?: boolean;
155
+ }
156
+ /** What a custom cell renderer is handed — the React `CellRenderContext`, DOM-flavoured. */
157
+ interface CellRenderContext<TRow = GridRowData> {
158
+ value: unknown;
159
+ row: TRow;
160
+ node: RowNode<TRow>;
161
+ column: GridColumnDef<TRow>;
162
+ }
163
+ /**
164
+ * Column definition, plus the vanilla escape hatch.
165
+ *
166
+ * `renderCell` returns a string (set as textContent — safe by construction, no
167
+ * HTML parsing) or a Node (adopted as the cell's content). This is the honest
168
+ * translation of React's ReactNode contract: there is no vanilla equivalent of
169
+ * an element tree, and pretending otherwise would mean shipping an HTML-string
170
+ * API and its injection surface.
171
+ */
172
+ type VanillaColumnDef<TRow = GridRowData> = GridColumnDef<TRow> & {
173
+ renderCell?: (ctx: CellRenderContext<TRow>) => string | Node;
174
+ /** Replace the header cell's LABEL — the React `renderHeader`, DOM-typed.
175
+ * Sorting, funnel, reorder and resize chrome behave as the def's own flags
176
+ * say; a chrome column (the command column) turns them all off itself. */
177
+ renderHeader?: (ctx: {
178
+ column: VanillaColumnDef<TRow>;
179
+ }) => string | Node;
180
+ /**
181
+ * Tooltip for this column's cells — the React column extension, DOM-typed.
182
+ * A string is static; a function runs only for the cell under the pointer,
183
+ * so it may be as expensive as it needs to be. Return null to suppress the
184
+ * tooltip INCLUDING the truncation fallback.
185
+ */
186
+ tooltip?: string | ((ctx: CellRenderContext<TRow>) => string | null | undefined);
187
+ /** Read the tooltip from another field of the row. `tooltip` wins. */
188
+ tooltipField?: string;
189
+ /**
190
+ * Let this column's text WRAP, and let the row grow to fit it. Only
191
+ * RENDERED rows can be measured, so the scrollbar firms up as the user
192
+ * travels — every virtualized grid that measures has this property.
193
+ */
194
+ autoHeight?: boolean;
195
+ };
196
+ interface CreateGridConfig<TRow = GridRowData> extends Omit<DataGridCoreConfig<TRow>, 'columns'> {
197
+ columns: VanillaColumnDef<TRow>[];
198
+ theme?: GridTheme;
199
+ palette?: GridPalette;
200
+ density?: GridDensity;
201
+ dark?: boolean;
202
+ /** Zebra rows. Default TRUE — the React renderer's default. */
203
+ striped?: boolean;
204
+ /** Leading 1-based row-number column. Adapter chrome; core never sees it. */
205
+ rowNumbers?: boolean;
206
+ /** Text of the empty-state overlay. Defaults to the locale dictionary. */
207
+ emptyText?: string;
208
+ /** Label of the busy overlay. Defaults to the locale dictionary. */
209
+ loadingText?: string;
210
+ /**
211
+ * The busy overlay. `true` (default) shows it after 300 ms of continuous
212
+ * FOREGROUND work; a number sets that delay; `false` turns it off entirely.
213
+ *
214
+ * The delay is not decoration. Every filter keystroke re-runs the pipeline,
215
+ * and a pass that finishes in 20 ms would raise and drop the overlay faster
216
+ * than anyone can read it — a strobe. Background passes (a streamed
217
+ * transaction batch, a block fetch caused by scrolling) never raise it at
218
+ * all: the user did not ask for them and is not waiting on them.
219
+ *
220
+ * `controller.showLoadingOverlay()` is an explicit decision rather than a
221
+ * flicker to absorb, so it appears at once, delay skipped.
222
+ */
223
+ loadingOverlay?: boolean | number;
224
+ /** Dictionary locale (core's twelve). Default 'en'. */
225
+ locale?: string;
226
+ /** Totals row pinned to the bottom edge. Values come from `summaries`. */
227
+ footer?: boolean;
228
+ /**
229
+ * Wording for the filter panel's sentence — operator captions, the list
230
+ * separator, and `maxListValues` (how long an `in` list gets before it
231
+ * collapses to "N values").
232
+ *
233
+ * Defaults come from the grid's LOCALE; what you pass overrides key by key,
234
+ * so an app can rename one operator without restating the other thirteen.
235
+ */
236
+ filterPanelLabels?: DescribeFilterOptions;
237
+ /**
238
+ * Replace each DATA row's content. Group headers and detail panels keep
239
+ * their own rendering — a template that had to handle those too would be a
240
+ * template of the whole grid.
241
+ *
242
+ * The row ELEMENT stays: it carries the height, the aria indices, the zebra
243
+ * and selection classes and the handlers the virtualizer depends on. What
244
+ * the template replaces are the CELLS — including the lead ones, so the row
245
+ * number, the detail expander and the selection checkbox are not drawn while
246
+ * it is on, and in-cell editing and range selection have nothing to attach
247
+ * to. (The drag grip survives: it is not a cell.) React draws exactly the
248
+ * same line.
249
+ *
250
+ * For per-cell customization that keeps the column layout, use a column's
251
+ * `renderCell`.
252
+ *
253
+ * Returning the SAME node you returned last time is how a template says
254
+ * "nothing changed" — the DOM is then left alone, so a focused input inside
255
+ * it keeps its caret.
256
+ */
257
+ renderRow?: (context: RowTemplateContext<TRow>) => string | Node | null | undefined;
258
+ /**
259
+ * A polite live region reporting row count, sort and selection to screen
260
+ * readers. Default true.
261
+ *
262
+ * Sorting and filtering are otherwise visual-only events: the rows change
263
+ * under a screen-reader user with nothing said. The text is built from the
264
+ * resolved dictionary, so it is announced in the grid's language.
265
+ */
266
+ announcements?: boolean;
267
+ /**
268
+ * Clicking a footer cell opens the aggregation picker — No total / Count /
269
+ * Sum / Average / Min / Max, filtered to what the column's data type
270
+ * supports, so a text column is never offered a Sum (`Number()` over names
271
+ * totals 0, and a 0 in a footer is indistinguishable from a real zero).
272
+ * Default true; `false` makes the footer read-only.
273
+ *
274
+ * With it on, a cell holding no total shows a faint dot — otherwise the
275
+ * only affordance is an empty rectangle nobody thinks to click.
276
+ */
277
+ footerMenu?: boolean;
278
+ /** Rows fixed above the scrolling body. Adapter chrome, like React's. */
279
+ pinnedTopRows?: readonly TRow[];
280
+ /** Rows fixed below the scrolling body. */
281
+ pinnedBottomRows?: readonly TRow[];
282
+ /** Drag the trailing edge of a header to resize. Default true. */
283
+ resizable?: boolean;
284
+ /** Drag a header onto another to reorder. Default true. */
285
+ reorderable?: boolean;
286
+ /** Per-column text inputs under the header. */
287
+ filterRow?: boolean;
288
+ /** ms before typed filter text reaches the engine. Same default as React:
289
+ * 0 for local data (the pipeline is the debounce), 200 for remote. */
290
+ filterDebounce?: number;
291
+ /** Funnel buttons opening the distinct-value checkbox popup. */
292
+ headerFilter?: boolean;
293
+ /**
294
+ * When the header's funnel icon is visible: `'hover'` (default) reveals it
295
+ * on hover or keyboard focus, `'always'` pins it. An ACTIVE filter stays
296
+ * visible in both modes — it is the only sign that rows are being withheld.
297
+ */
298
+ filterIconMode?: 'hover' | 'always';
299
+ /** The active filter, in words, with one-click clear. */
300
+ /**
301
+ * The strip under the grid that says, in words, what is being filtered —
302
+ * with an × to drop it and a checkbox to suspend it without losing it.
303
+ *
304
+ * `'always'` keeps the strip on screen with no filter applied, so the
305
+ * control does not appear and disappear under the pointer.
306
+ */
307
+ filterPanel?: boolean | 'always';
308
+ /**
309
+ * Opens the app's filter builder from the panel's chevron. Without it the
310
+ * chevron is not drawn, because a control that opens nothing is worse than
311
+ * no control — React's rule, and the Enterprise wrapper wires it to its own
312
+ * builder.
313
+ */
314
+ onOpenFilterBuilder?: () => void;
315
+ /** Highlight the row that holds focus. Default false — the React default. */
316
+ focusedRow?: boolean;
317
+ /**
318
+ * Focus this row when the grid is built — the React `focusedRowKey` prop.
319
+ *
320
+ * React re-applies it whenever the prop changes; a vanilla config is read
321
+ * once, so this is the STARTING focus and later moves go through
322
+ * `grid.controller.focusRow(key)`. Said plainly because a silently
323
+ * one-shot prop is exactly the kind of thing that costs an afternoon.
324
+ */
325
+ focusedRowKey?: RowKey | null;
326
+ /** Fired when the focused ROW changes — not on every cell move within it. */
327
+ onFocusedRowChanged?: (event: {
328
+ rowKey: RowKey | null;
329
+ row?: TRow;
330
+ }) => void;
331
+ /** Fired on a click anywhere in a row, before the grid's own row event. */
332
+ onRowClick?: (node: RowNode<TRow>) => void;
333
+ /** Fired on Enter / double-click over the focused row. */
334
+ onRowActivate?: (node: RowNode<TRow>) => void;
335
+ /**
336
+ * Where the totals row sits: `'bottom'` (default) or `'top'`, directly
337
+ * under the header — the React `footerPosition` prop. Enterprise, like the
338
+ * totals themselves.
339
+ */
340
+ footerPosition?: 'top' | 'bottom';
341
+ /**
342
+ * Cell notes (P3.3): a marker on noted cells, the note as the hover
343
+ * tooltip, and — with `onChange` — add/edit/delete on the row menu with an
344
+ * anchored textarea. Enterprise: ignored (reported under `debugMode`)
345
+ * without `enterpriseEdition`, the React rule.
346
+ */
347
+ notes?: GridNotesConfig<TRow>;
348
+ /**
349
+ * Wrap search matches in `<mark>` inside the cell. Default true — the React
350
+ * `highlightSearch` prop. Applied to the FINISHED text (after the lookup,
351
+ * the formatter and the date format), because that is what the reader
352
+ * searched.
353
+ */
354
+ highlightSearch?: boolean;
355
+ /**
356
+ * Sorting for the whole grid. Default true; `false` turns every header into
357
+ * a plain label. A column can still opt out on its own with
358
+ * `sortable: false` on its definition — the React prop of the same name.
359
+ */
360
+ sortable?: boolean;
361
+ /**
362
+ * Column widths on startup. `'none'` (default) keeps the declared widths.
363
+ *
364
+ * `'fitGrid'` divides the grid's width among the columns and RE-RUNS on every
365
+ * grid resize, taking the current widths as its proportions — so a column the
366
+ * user dragged wider stays proportionally wider.
367
+ *
368
+ * `'fitCells'` measures what the first rows actually rendered and runs ONCE:
369
+ * before the first paint there is nothing to measure. It can overflow the
370
+ * grid; that is the point of asking for content-width columns.
371
+ */
372
+ autoSizeStrategy?: 'none' | 'fitGrid' | 'fitCells';
373
+ /**
374
+ * @deprecated Retired 2026-08-15 and inert in BOTH renderers; accepted so
375
+ * existing call sites keep working.
376
+ *
377
+ * It rendered a strip under the grid whose only content was the row count.
378
+ * The per-column totals it once carried moved into the footer ROW, where
379
+ * each number sits under its own column — leaving a whole bar to say one
380
+ * number, printed a second time whenever the Enterprise status bar was on.
381
+ *
382
+ * The count lives in the **status bar** (Enterprise: total, filtered,
383
+ * selected) or in the **pager**, which shows the total when pagination is on.
384
+ *
385
+ * Unlike React, this renderer says so once in the console when you set it —
386
+ * a vanilla caller has no editor surfacing the `@deprecated` tag.
387
+ */
388
+ showRowCount?: boolean;
389
+ /** Quick-search + CSV export bar above the grid. */
390
+ toolbar?: boolean;
391
+ /** "Columns" button in the toolbar opening the visibility chooser. */
392
+ toolbarColumnChooser?: boolean;
393
+ /**
394
+ * Extra toolbar buttons, appended after the grid's own — the React
395
+ * `toolbarItemsAfter` prop, DOM-flavoured. Ignored without `toolbar`.
396
+ */
397
+ toolbarItemsAfter?: HTMLElement[];
398
+ /** Extra toolbar buttons, BEFORE the grid's own — React's `toolbarItemsBefore`. */
399
+ toolbarItemsBefore?: HTMLElement[];
400
+ /**
401
+ * Excel entries (all rows / selected rows) on the toolbar's export menu.
402
+ * Enterprise: shown only under `enterpriseEdition`, hidden rather than
403
+ * shown-and-failing — the controller would throw, and a menu entry whose
404
+ * only outcome is an error is worse than no entry (the React rule).
405
+ */
406
+ toolbarExcel?: boolean;
407
+ /** PDF entry on the export menu — Enterprise, same gate as `toolbarExcel`. */
408
+ toolbarPdf?: boolean;
409
+ /** Right-click menu with Copy / Copy with headers. Default true. */
410
+ cellMenu?: boolean;
411
+ /**
412
+ * App-defined right-click menu for data rows — the React `rowMenu` prop,
413
+ * DOM-flavoured. A function receives the row that was clicked so the items
414
+ * can depend on it. The grid's own entries (Copy…) append BELOW after a
415
+ * separator, so an app's menu keeps the order it wrote.
416
+ */
417
+ rowMenu?: GridMenuItem<RowMenuContext<TRow>>[] | ((context: RowMenuContext<TRow>) => GridMenuItem<RowMenuContext<TRow>>[]);
418
+ /**
419
+ * Right-click menu on column headers — the React `headerMenu` prop,
420
+ * DOM-flavoured. `true`/omitted shows the default items (sort, group,
421
+ * pin, hide, best fit…); `false` turns it off; a function receives the
422
+ * context and the defaults and returns the finished list, so an app can
423
+ * reorder, drop or append without rebuilding what the grid provides.
424
+ */
425
+ headerMenu?: boolean | ((context: HeaderMenuContext, defaults: GridMenuItem<HeaderMenuContext>[]) => GridMenuItem<HeaderMenuContext>[]);
426
+ /**
427
+ * Drag rows to reorder them, and — with a shared `group` — between two
428
+ * grids. A grip appears at the start of each row on hover. Reordering the
429
+ * grid's own array only works on an UNSORTED view (the React caveat).
430
+ */
431
+ rowDrag?: RowDragConfig<TRow>;
432
+ /**
433
+ * Grids naming the same group keep their column layout and horizontal
434
+ * scroll in step — a scrolling body with a totals strip below it. Sort,
435
+ * filter, selection and vertical scroll deliberately do NOT travel.
436
+ * (Aligns with other VANILLA grids; the React registry is separate.)
437
+ */
438
+ alignedGrids?: string;
439
+ /**
440
+ * Render EVERY row while the browser prints, so the print engine paginates
441
+ * real content instead of one clipped screenful. `true` = only while the
442
+ * print dialog is open; an options object can pin it `always` (report
443
+ * pages) or move the `maxRows` refusal point.
444
+ */
445
+ printLayout?: boolean | PrintLayoutOptions;
446
+ /** Persist and restore the view (sort, filters, widths…). Core's service. */
447
+ stateStoring?: StateStoringConfig;
448
+ /** Hover tooltips. `false` disables, a number overrides the 400ms delay. */
449
+ tooltip?: boolean | {
450
+ delay?: number;
451
+ };
452
+ /** Drop strip above the grid: drag a header here to group by it. Reads
453
+ * `grouping.panel` exactly like the React prop shape. */
454
+ /** Search box debounce, ms. React's default. */
455
+ searchDebounce?: number;
456
+ /** Replace a body cell's content. `undefined` = the default painter runs. */
457
+ renderCellContent?: (ctx: CellSlotContext<TRow>) => Node | string | undefined;
458
+ /**
459
+ * Panel body for a master-detail row. With the engine's `masterDetail`
460
+ * config present, supplying this turns the leading expander column on —
461
+ * the React `renderDetail` prop, DOM-typed.
462
+ */
463
+ renderDetail?: (row: TRow) => Node | string;
464
+ /**
465
+ * Body for an in-grid edit form row (`mode: 'form'`). `row` is undefined
466
+ * for a record that does not exist yet — the form still opens, because the
467
+ * alternative is pressing "New row" and seeing nothing happen.
468
+ */
469
+ renderEditForm?: (rowKey: RowKey, row: TRow | undefined) => Node | string;
470
+ /**
471
+ * The edition flag, set by the Enterprise wrapper — the React prop of the
472
+ * same name. It gates what the CONTROLLER agrees to do (styled exports,
473
+ * master-detail…); without it the `masterDetail` config is stripped, the
474
+ * way the React Community renderer strips it.
475
+ */
476
+ enterpriseEdition?: boolean;
477
+ /** Extra classes for a body cell (pending edit, range, fill preview…). */
478
+ getCellClassName?: (ctx: CellSlotContext<TRow>) => string | undefined;
479
+ onCellPointerDown?: (ctx: CellSlotContext<TRow>, event: MouseEvent) => void;
480
+ onCellPointerEnter?: (ctx: CellSlotContext<TRow>, event: MouseEvent) => void;
481
+ onCellDoubleClick?: (ctx: CellSlotContext<TRow>) => void;
482
+ }
483
+ interface GridApi<TRow = GridRowData> {
484
+ /** The engine, whole. The same object React hands to `onReady`. */
485
+ controller: DataGridController<TRow>;
486
+ /** The `.kanuni-datagrid-root` element this grid built. */
487
+ element: HTMLElement;
488
+ setData(rows: TRow[]): void;
489
+ setColumns(columns: VanillaColumnDef<TRow>[]): void;
490
+ destroy(): void;
491
+ }
492
+ declare function createGrid<TRow = GridRowData>(container: HTMLElement, config: CreateGridConfig<TRow>): GridApi<TRow>;
493
+
494
+ /**
495
+ * Width that fits the header and every rendered cell of `columnId`.
496
+ * Returns null when nothing could be measured (no such column on screen, or no
497
+ * canvas — jsdom, SSR), so the caller can leave the width alone.
498
+ */
499
+ declare function measureBestFitWidth(root: HTMLElement, columnId: string): number | null;
500
+
501
+ export { CHECKBOX_COLUMN_WIDTH, type CellRenderContext, type CellSlotContext, type CreateGridConfig, DETAIL_COLUMN_WIDTH, type GridApi, type GridMenuItem, type GridNotesConfig, type HeaderMenuContext, type HeaderMenuDeps, type NoteCellContext, PRINT_MAX_ROWS, type PrintLayoutOptions, ROW_DRAG_MIME, ROW_NUMBER_COLUMN_WIDTH, type RowDragConfig, type RowDragPayload, type RowDropEvent, type RowMenuContext, type RowReorderEvent, type VanillaColumnDef, buildHeaderMenu, createGrid, measureBestFitWidth };