@svgrid/grid 1.0.2 → 1.1.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.
Files changed (143) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +137 -39
  3. package/dist/GridFooter.svelte +164 -0
  4. package/dist/GridFooter.svelte.d.ts +29 -0
  5. package/dist/GridMenus.svelte +570 -0
  6. package/dist/GridMenus.svelte.d.ts +31 -0
  7. package/dist/SvGrid.controller.svelte.d.ts +429 -0
  8. package/dist/SvGrid.controller.svelte.js +1732 -0
  9. package/dist/SvGrid.css +1709 -0
  10. package/dist/SvGrid.helpers.d.ts +35 -0
  11. package/dist/SvGrid.helpers.js +160 -0
  12. package/dist/SvGrid.svelte +344 -7043
  13. package/dist/SvGrid.svelte.d.ts +4 -357
  14. package/dist/SvGrid.types.d.ts +436 -0
  15. package/dist/SvGrid.types.js +1 -0
  16. package/dist/SvGridChart.svelte +1060 -23
  17. package/dist/SvGridChart.svelte.d.ts +17 -0
  18. package/dist/build-api.d.ts +5 -0
  19. package/dist/build-api.js +527 -0
  20. package/dist/cell-render.d.ts +15 -0
  21. package/dist/cell-render.js +246 -0
  22. package/dist/cell-values.d.ts +28 -0
  23. package/dist/cell-values.js +89 -0
  24. package/dist/chart.d.ts +370 -3
  25. package/dist/chart.js +1135 -42
  26. package/dist/clipboard.d.ts +15 -0
  27. package/dist/clipboard.js +356 -0
  28. package/dist/columns.d.ts +30 -0
  29. package/dist/columns.js +277 -0
  30. package/dist/core.d.ts +1 -1
  31. package/dist/css.d.ts +3 -0
  32. package/dist/editing.d.ts +24 -0
  33. package/dist/editing.js +343 -0
  34. package/dist/editors/cell-editors.d.ts +1 -1
  35. package/dist/facet-buckets.d.ts +13 -0
  36. package/dist/facet-buckets.js +54 -0
  37. package/dist/features.d.ts +5 -0
  38. package/dist/features.js +30 -0
  39. package/dist/filter-operators.d.ts +16 -0
  40. package/dist/filter-operators.js +69 -0
  41. package/dist/hyperformula-adapter.d.ts +82 -0
  42. package/dist/hyperformula-adapter.js +73 -0
  43. package/dist/index.d.ts +5 -2
  44. package/dist/index.js +5 -2
  45. package/dist/keyboard-handlers.d.ts +7 -0
  46. package/dist/keyboard-handlers.js +197 -0
  47. package/dist/menus.d.ts +40 -0
  48. package/dist/menus.js +389 -0
  49. package/dist/named-views.d.ts +27 -0
  50. package/dist/named-views.js +39 -0
  51. package/dist/row-resize.d.ts +43 -0
  52. package/dist/row-resize.js +158 -0
  53. package/dist/scroll-sync.d.ts +9 -0
  54. package/dist/scroll-sync.js +86 -0
  55. package/dist/selection.d.ts +26 -0
  56. package/dist/selection.js +387 -0
  57. package/dist/spreadsheet.d.ts +80 -0
  58. package/dist/spreadsheet.js +194 -0
  59. package/dist/summaries.d.ts +12 -0
  60. package/dist/summaries.js +65 -0
  61. package/dist/svgrid-wrapper.types.d.ts +12 -1
  62. package/dist/svgrid.comments-autocomplete.test.d.ts +1 -0
  63. package/dist/svgrid.comments-autocomplete.test.js +96 -0
  64. package/dist/svgrid.context-menu.test.d.ts +1 -0
  65. package/dist/svgrid.context-menu.test.js +102 -0
  66. package/dist/svgrid.new-features.wrapper.test.js +30 -4
  67. package/dist/svgrid.wrapper.test.js +27 -1
  68. package/dist/virtualization/column-virtualizer.d.ts +2 -0
  69. package/dist/virtualization/scroll-scaling.d.ts +28 -0
  70. package/dist/virtualization/scroll-scaling.js +64 -0
  71. package/dist/virtualization/scroll-scaling.test.d.ts +1 -0
  72. package/dist/virtualization/scroll-scaling.test.js +86 -0
  73. package/dist/virtualization/svelte-virtualizer.svelte.d.ts +2 -0
  74. package/dist/virtualization/svelte-virtualizer.svelte.js +2 -0
  75. package/dist/virtualization/virtualizer.d.ts +7 -0
  76. package/dist/virtualization/virtualizer.js +30 -0
  77. package/package.json +1 -1
  78. package/src/GridFooter.svelte +164 -0
  79. package/src/GridMenus.svelte +570 -0
  80. package/src/SvGrid.controller.svelte.ts +2195 -0
  81. package/src/SvGrid.css +1747 -0
  82. package/src/SvGrid.helpers.test.ts +415 -0
  83. package/src/SvGrid.helpers.ts +185 -0
  84. package/src/SvGrid.svelte +348 -7043
  85. package/src/SvGrid.types.ts +456 -0
  86. package/src/SvGridChart.svelte +1060 -23
  87. package/src/build-api.coverage.test.ts +532 -0
  88. package/src/build-api.ts +663 -0
  89. package/src/cell-render.test.ts +451 -0
  90. package/src/cell-render.ts +426 -0
  91. package/src/cell-values.ts +114 -0
  92. package/src/chart-export.test.ts +370 -0
  93. package/src/chart.coverage.test.ts +814 -0
  94. package/src/chart.ts +1352 -47
  95. package/src/clipboard.test.ts +731 -0
  96. package/src/clipboard.ts +524 -0
  97. package/src/collaboration.coverage.test.ts +220 -0
  98. package/src/columns.test.ts +702 -0
  99. package/src/columns.ts +419 -0
  100. package/src/core.ts +8 -0
  101. package/src/css.d.ts +3 -0
  102. package/src/editing.test.ts +837 -0
  103. package/src/editing.ts +513 -0
  104. package/src/editors/cell-editors.coverage.test.ts +156 -0
  105. package/src/editors/cell-editors.ts +1 -0
  106. package/src/facet-buckets.test.ts +353 -0
  107. package/src/facet-buckets.ts +67 -0
  108. package/src/features.ts +128 -0
  109. package/src/filter-operators.test.ts +174 -0
  110. package/src/filter-operators.ts +87 -0
  111. package/src/hyperformula-adapter.test.ts +256 -0
  112. package/src/hyperformula-adapter.ts +124 -0
  113. package/src/index.ts +37 -0
  114. package/src/keyboard-handlers.coverage.test.ts +560 -0
  115. package/src/keyboard-handlers.ts +353 -0
  116. package/src/keyboard.ts +97 -97
  117. package/src/menus.test.ts +620 -0
  118. package/src/menus.ts +554 -0
  119. package/src/named-views.coverage.test.ts +210 -0
  120. package/src/named-views.ts +48 -0
  121. package/src/row-resize.test.ts +369 -0
  122. package/src/row-resize.ts +171 -0
  123. package/src/scroll-sync.test.ts +330 -0
  124. package/src/scroll-sync.ts +216 -0
  125. package/src/selection.test.ts +722 -0
  126. package/src/selection.ts +545 -0
  127. package/src/server-data-source.coverage.test.ts +180 -0
  128. package/src/spreadsheet.test.ts +445 -0
  129. package/src/spreadsheet.ts +246 -0
  130. package/src/summaries.ts +204 -0
  131. package/src/sv-grid-scrollbar.ts +13 -1
  132. package/src/svgrid-wrapper.types.ts +12 -1
  133. package/src/svgrid.behavior.test.ts +22 -0
  134. package/src/svgrid.comments-autocomplete.test.ts +112 -0
  135. package/src/svgrid.context-menu.test.ts +126 -0
  136. package/src/svgrid.interaction.test.ts +30 -0
  137. package/src/svgrid.new-features.wrapper.test.ts +65 -4
  138. package/src/svgrid.wrapper.test.ts +27 -1
  139. package/src/test-setup.ts +9 -6
  140. package/src/virtualization/scroll-scaling.test.ts +148 -0
  141. package/src/virtualization/scroll-scaling.ts +121 -0
  142. package/src/virtualization/svelte-virtualizer.svelte.ts +2 -0
  143. package/src/virtualization/virtualizer.ts +26 -0
@@ -0,0 +1,456 @@
1
+ // Type definitions extracted from SvGrid.svelte. These are compile-time
2
+ // only - moving them out keeps the component's <script> focused on logic.
3
+ import type { Snippet } from "svelte";
4
+ import type {
5
+ CellEditorType,
6
+ ColumnDef,
7
+ RowData,
8
+ SvGridApi,
9
+ TableFeatures,
10
+ } from "./index";
11
+ import type { ConditionalFormat } from "./conditional-formatting";
12
+
13
+ /** The cell a context menu was opened on. Passed to every item's callbacks. */
14
+ export type ContextMenuTarget<TData extends RowData = RowData> = {
15
+ rowIndex: number;
16
+ colIndex: number;
17
+ columnId: string;
18
+ /** Stable row id (from getRowId / row model), for keying notes etc. */
19
+ rowId: string;
20
+ row: TData | null;
21
+ };
22
+
23
+ /**
24
+ * A context-menu entry. Either a built-in action key, the `"separator"`
25
+ * divider, or a custom item. Built-in keys: `"copy" | "cut" | "paste" |
26
+ * "clear" | "row_above" | "row_below" | "remove_row" | "remove_col"`.
27
+ */
28
+ export type ContextMenuItem<TData extends RowData = RowData> =
29
+ | string
30
+ | {
31
+ key: string;
32
+ label: string;
33
+ /** Hide the item entirely for this target. */
34
+ hidden?: (target: ContextMenuTarget<TData>) => boolean;
35
+ /** Render the item greyed-out and non-clickable for this target. */
36
+ disabled?: (target: ContextMenuTarget<TData>) => boolean;
37
+ /** Invoked on click. The menu closes afterwards. */
38
+ action: (target: ContextMenuTarget<TData>) => void;
39
+ };
40
+
41
+ export type Props<TFeatures extends TableFeatures = TableFeatures, TData extends RowData = RowData> = {
42
+ data: ReadonlyArray<TData>;
43
+ columns: Array<ColumnDef<TFeatures, TData>>;
44
+ /**
45
+ * Right-click context menu. `true` shows the default item set (copy, cut,
46
+ * paste, clear, insert row above/below, remove row, remove column). Pass an
47
+ * array to customize: strings are built-in keys, `"separator"` is a divider,
48
+ * and objects are custom items. Omitted/`false` disables the menu (native
49
+ * browser menu shows instead).
50
+ */
51
+ contextMenu?: boolean | ReadonlyArray<ContextMenuItem<TData>>;
52
+ /**
53
+ * The feature set built with `tableFeatures({ ... })`. Optional - the
54
+ * `sortable` / `filterable` / `groupable` shortcuts below inject the
55
+ * matching feature for you, so a grid can be configured entirely from
56
+ * the boolean shortcuts without importing the feature constants.
57
+ */
58
+ features?: TFeatures;
59
+ /**
60
+ * Convenience shortcuts to switch a whole capability on without wiring
61
+ * `features` or the finer-grained props by hand. Every capability is OFF
62
+ * by default - a bare grid is a plain read-only table - so set the
63
+ * shortcut `true` to opt in. (`false` / omitted both leave it off; the
64
+ * shortcut is mainly there to turn things ON.)
65
+ *
66
+ * `sortable` - column sorting (injects `rowSortingFeature`)
67
+ * `filterable` - column filtering (injects `columnFilteringFeature`)
68
+ * `editable` - inline cell editing (alias of `enableInlineEditing`)
69
+ * `groupable` - row grouping controls(alias of `showGroupingControls`,
70
+ * also injects `columnGroupingFeature`)
71
+ * `pageable` - pagination footer (alias of `showPagination`)
72
+ *
73
+ * Fine-grained props (`enableInlineEditing`, `showPagination`, ...) still
74
+ * work; the shortcut wins only when it is explicitly set.
75
+ */
76
+ sortable?: boolean;
77
+ filterable?: boolean;
78
+ editable?: boolean;
79
+ groupable?: boolean;
80
+ pageable?: boolean;
81
+ loading?: boolean;
82
+ /**
83
+ * Render `loading` as a non-blocking overlay instead of replacing the
84
+ * whole grid: the current rows stay visible (dimmed, with a top progress
85
+ * bar) during a refetch, and the first load shows shimmer skeleton rows.
86
+ * Ideal for server-paged grids so paging/sorting doesn't flash. Defaults
87
+ * to `false` (the classic full "Loading..." replacement).
88
+ */
89
+ loadingOverlay?: boolean;
90
+ /** Skeleton placeholder rows to show on first load. Defaults to 8. */
91
+ loadingSkeletonRows?: number;
92
+ error?: string | null;
93
+ emptyMessage?: string;
94
+ showGlobalFilter?: boolean;
95
+ showColumnFilters?: boolean;
96
+ /**
97
+ * Quick way to pick a single filtering UI. When set it controls which of
98
+ * the three filter surfaces appears (and is overridden per-surface by the
99
+ * `showGlobalFilter` / `showColumnFilters` / `showFilterRow` props).
100
+ * Defaults to `'menu'` (only the column menu's filter section is shown).
101
+ */
102
+ filterMode?: "row" | "menu" | "global" | "none";
103
+ showGroupingControls?: boolean;
104
+ showRowSelection?: boolean;
105
+ showPagination?: boolean;
106
+ /** Initial page size when pagination is enabled. Defaults to 10. */
107
+ pageSize?: number;
108
+ virtualization?: boolean;
109
+ /** Row height in pixels. Pass a function `(rowIndex) => px` for
110
+ * per-row variable heights (e.g. an interactive row-resize feature).
111
+ * Defaults to 30. */
112
+ rowHeight?: number | ((rowIndex: number) => number);
113
+ /**
114
+ * Height (px) of a single column-header level row. With multi-level
115
+ * (grouped) headers the total header height is `levels * headerHeight`,
116
+ * since each level renders as its own row. When omitted, header rows size
117
+ * to their content (the default). Does not affect the filter row.
118
+ */
119
+ headerHeight?: number;
120
+ overscan?: number;
121
+ /**
122
+ * Height of the grid's scrollable shell. A number is treated as pixels;
123
+ * a string is used as-is, so callers can pass `'100%'` or `'auto'` to
124
+ * make the grid fill its parent. Defaults to 520 px.
125
+ */
126
+ containerHeight?: number | string;
127
+ columnVirtualization?: boolean;
128
+ columnOverscan?: number;
129
+ columnWidth?: number;
130
+ /**
131
+ * Columns pinned to the left/right edge on mount. Each entry is a
132
+ * column id (matches `ColumnDef.field` when no explicit id is set).
133
+ * The internal pinning state is seeded once at mount; user-driven
134
+ * pinning via the column menu still works and overrides this default.
135
+ * Requires `columnVirtualization={false}` to be visible in the menu
136
+ * UI (sticky positioning can't co-exist with column virtualization
137
+ * since the virtualizer recycles DOM nodes).
138
+ */
139
+ initialColumnPinning?: {
140
+ left?: ReadonlyArray<string>;
141
+ right?: ReadonlyArray<string>;
142
+ };
143
+ /**
144
+ * When `true`, columns are scaled proportionally so their total width
145
+ * fills the viewport (no empty space on the right). Disabled by
146
+ * default - explicit `width` values are used as-is. User resizes still
147
+ * win once they happen.
148
+ */
149
+ fitColumns?: boolean;
150
+ showFilterMenu?: boolean;
151
+ showFilterRow?: boolean;
152
+ enableCellSelection?: boolean;
153
+ enableInlineEditing?: boolean;
154
+ enableRowSummaries?: boolean;
155
+ /**
156
+ * Excel-style status bar under the grid showing live aggregates of the
157
+ * selected cell range (count, numeric count, sum, average, min, max).
158
+ * `true` shows the default set; pass `{ aggregates: [...] }` to choose
159
+ * which. Requires `enableCellSelection`.
160
+ */
161
+ statusBar?:
162
+ | boolean
163
+ | {
164
+ aggregates?: ReadonlyArray<
165
+ "count" | "numericCount" | "sum" | "avg" | "min" | "max"
166
+ >;
167
+ };
168
+ /**
169
+ * Show a docked Columns tool panel - the enterprise sidebar for toggling
170
+ * column visibility, reordering, and grouping without a right-click. A
171
+ * toggle button appears at the grid's top-right; the panel itself docks
172
+ * on the right edge.
173
+ */
174
+ toolPanel?: boolean;
175
+ /**
176
+ * Quick way to pick which selection surfaces are active. `'row'` shows the
177
+ * selection checkbox column only, `'cell'` allows rectangle/range cell
178
+ * selection only, `'both'` (default) enables both, `'none'` disables both.
179
+ * Overridden per-surface by `showRowSelection` / `enableCellSelection`.
180
+ */
181
+ selectionMode?: "row" | "cell" | "both" | "none";
182
+ /**
183
+ * Render a leading row-number column (1-based) before any selection
184
+ * column. Useful as a permanent anchor when scrolling wide grids.
185
+ */
186
+ showRowNumbers?: boolean;
187
+ /**
188
+ * Paint alternating data rows with the `--sg-row-alt-bg` color (zebra
189
+ * striping). Only data rows stripe - pinned, group, detail, and summary
190
+ * rows keep their single background. Defaults to `false`.
191
+ */
192
+ zebraRows?: boolean;
193
+ /**
194
+ * Width (px) of the row-number column. Defaults to 56, which fits up
195
+ * to "99,999"; bump this when the dataset crosses six digits so the
196
+ * largest row number stays fully visible at the bottom of a scroll.
197
+ */
198
+ rowNumberWidth?: number;
199
+ /** Receives the imperative grid API once the component has mounted. */
200
+ onApiReady?: (api: SvGridApi<TFeatures, TData>) => void;
201
+ /**
202
+ * Fires whenever the row-selection state changes. The first argument
203
+ * is the new selection record `{ [rowId]: true }`; the second is the
204
+ * array of selected `TData` rows.
205
+ */
206
+ onRowSelectionChange?: (
207
+ selection: Record<string, boolean>,
208
+ rows: TData[],
209
+ ) => void;
210
+ /**
211
+ * Fires whenever the cell-selection rectangle changes (mouse, keyboard,
212
+ * or `api.selectCells()`). `ranges` matches `api.getSelected()` -
213
+ * `[rowStart, colStart, rowEnd, colEnd]` rectangles in grid coords.
214
+ * Empty array when the user clears the selection.
215
+ */
216
+ onCellSelectionChange?: (
217
+ ranges: Array<[number, number, number, number]>,
218
+ ) => void;
219
+ /**
220
+ * When `true`, the grid records sort state (and renders sort indicators
221
+ * + cycling on headers) but does NOT actually re-order the rows. The
222
+ * consumer is expected to sort `data` themselves - typically via
223
+ * `onSortingChange`. Use this for tree/hierarchical data, where a flat
224
+ * global sort would break parent-child adjacency. Defaults to `false`.
225
+ */
226
+ externalSort?: boolean;
227
+ /**
228
+ * Fires whenever the sort clauses change. Receives the new array of
229
+ * `{ id, desc }` entries. Pair with `externalSort={true}` when the
230
+ * consumer wants to own the row ordering.
231
+ */
232
+ onSortingChange?: (sorting: Array<{ id: string; desc: boolean }>) => void;
233
+ /**
234
+ * When `true`, the grid still records column-filter / global-filter /
235
+ * facet state (so the menu UI works and indicators light up) but does
236
+ * NOT actually filter the rows. The consumer is expected to fetch / sort
237
+ * / filter the data themselves - typically via `onFiltersChange`. Used
238
+ * by server-side data sources. Defaults to `false`.
239
+ */
240
+ externalFilter?: boolean;
241
+ /**
242
+ * Fires whenever any of the in-grid filter state changes - global
243
+ * search, per-column operator filters, or facet (value-checklist)
244
+ * filters. Pair with `externalFilter={true}` when the consumer wants to
245
+ * push the query to the server.
246
+ */
247
+ onFiltersChange?: (filters: {
248
+ global: string;
249
+ columns: Array<{
250
+ id: string;
251
+ operator: FilterOperator;
252
+ value: string;
253
+ /**
254
+ * Upper bound for the `between` operator. Only set when
255
+ * `operator === 'between'` AND the user has typed both values.
256
+ */
257
+ valueTo?: string;
258
+ selectedValues?: Array<string>;
259
+ }>;
260
+ }) => void;
261
+ /**
262
+ * Fires when an inline edit is committed (Enter / Tab / blur). Useful
263
+ * for cascade-recompute pipelines: a parent listens, then refreshes
264
+ * derived columns or aggregates. The wrapper has already written the
265
+ * parsed value back into the row by the time this fires.
266
+ */
267
+ /**
268
+ * Resolve a stable id per row. Drives selection, expansion, edit,
269
+ * and active-cell state. When omitted, the row's array index is
270
+ * used - fine for read-only views but the wrong choice if `data`
271
+ * gets reordered or filtered outside the grid (selection would
272
+ * follow positions, not actual rows). Use a database PK, a UUID,
273
+ * or any stable string.
274
+ */
275
+ getRowId?: (row: TData, index: number) => string;
276
+ /**
277
+ * Conditional class(es) added to every `<tr>` body row. Receives
278
+ * the row's `original` data + its data-array index. Return a
279
+ * string, an array of strings, or an object mapping class names to
280
+ * booleans. Useful for "highlight overdue rows", "tint cancelled
281
+ * orders", and similar row-level state mappings.
282
+ */
283
+ rowClass?: (ctx: {
284
+ row: TData;
285
+ rowIndex: number;
286
+ }) => string | ReadonlyArray<string> | Record<string, boolean> | undefined | null;
287
+ /**
288
+ * Per-cell notes - longer free-form comments shown as a corner
289
+ * indicator + tooltip on hover. Keyed by row id then column id;
290
+ * empty / missing entries mean "no note". The grid owns rendering;
291
+ * you own storage (write your own callbacks to add / edit notes).
292
+ */
293
+ notes?: Record<string, Record<string, string>>;
294
+ /**
295
+ * Allow editing per-cell notes/comments through the UI: the context menu
296
+ * gains an "Edit comment" item that opens a popover editor. Edits are
297
+ * applied to an internal overlay for immediate feedback and emitted via
298
+ * `onNoteChange` so you can persist them back into `notes`.
299
+ */
300
+ editableComments?: boolean;
301
+ /** Fires when a comment is saved or removed (removed = empty `note`). */
302
+ onNoteChange?: (event: { rowId: string; columnId: string; note: string }) => void;
303
+ /**
304
+ * Excel-style conditional formatting. A list of value-driven rules that
305
+ * color cells: `colorScale` (gradient across the column range),
306
+ * `dataBar` (in-cell proportional bar), `iconSet` (arrows / traffic /
307
+ * triangles by threshold), and `rule` (apply a style when a predicate
308
+ * matches). Scope a format to specific columns with `columns: [...]`,
309
+ * or omit it to apply to every column. Later entries win on conflict.
310
+ */
311
+ conditionalFormats?: ReadonlyArray<ConditionalFormat<TData>>;
312
+ onCellValueChange?: (event: {
313
+ rowIndex: number;
314
+ columnId: string;
315
+ oldValue: unknown;
316
+ newValue: unknown;
317
+ row: TData;
318
+ }) => void;
319
+ /**
320
+ * Fires whenever the active cell changes - click, keyboard move,
321
+ * tab, page-up/down, etc. Consumers (toolbars, ribbon UIs) use this
322
+ * to stay synced with the grid's selection without polling the DOM.
323
+ */
324
+ onActiveCellChange?: (cell: {
325
+ rowIndex: number;
326
+ colIndex: number;
327
+ columnId: string;
328
+ }) => void;
329
+ /**
330
+ * Fires when a data cell (and therefore a row) is single-clicked.
331
+ * Group-header rows are excluded. `value` is the displayed cell value.
332
+ */
333
+ onCellClick?: (event: {
334
+ rowIndex: number;
335
+ colIndex: number;
336
+ columnId: string;
337
+ value: unknown;
338
+ row: TData;
339
+ }) => void;
340
+ /** Fires when a data row is single-clicked (any cell). Group rows excluded. */
341
+ onRowClick?: (event: {
342
+ rowIndex: number;
343
+ columnId: string;
344
+ row: TData;
345
+ }) => void;
346
+ /**
347
+ * Fires when a data cell is double-clicked - independent of whether the
348
+ * cell is editable, so it fires even on read-only grids. Group rows excluded.
349
+ */
350
+ onCellDoubleClick?: (event: {
351
+ rowIndex: number;
352
+ colIndex: number;
353
+ columnId: string;
354
+ value: unknown;
355
+ row: TData;
356
+ }) => void;
357
+ /** Fires when a data row is double-clicked (any cell). Group rows excluded. */
358
+ onRowDoubleClick?: (event: {
359
+ rowIndex: number;
360
+ columnId: string;
361
+ row: TData;
362
+ }) => void;
363
+ /**
364
+ * Fires once each time the body is scrolled to (within ~32px of) the
365
+ * bottom. Re-arms after the user scrolls back up. The canonical hook for
366
+ * infinite / lazy loading - append more rows to `data` when it fires.
367
+ */
368
+ onScrollBottomReached?: (event: {
369
+ scrollTop: number;
370
+ scrollHeight: number;
371
+ clientHeight: number;
372
+ }) => void;
373
+ /**
374
+ * Marks a row as an expandable "detail row". When this returns true the
375
+ * grid renders that row as a SINGLE full-width cell (colspan across every
376
+ * column) using `renderDetailRow`, instead of the normal per-column cells
377
+ * - the canonical Stripe / GitHub "expand a rich panel beneath the row"
378
+ * pattern. Insert the detail rows into `data` yourself (typically right
379
+ * after the row they belong to) and toggle them with your own expanded
380
+ * state. Pair with `virtualization={false}` so the variable-height detail
381
+ * isn't clipped by the fixed-row-height virtualizer.
382
+ */
383
+ isDetailRow?: (row: TData, rowIndex: number) => boolean;
384
+ /**
385
+ * Snippet rendered inside the full-width detail cell for rows where
386
+ * `isDetailRow` is true. Receives the row's data and its index.
387
+ */
388
+ renderDetailRow?: Snippet<[{ row: TData; rowIndex: number }]>;
389
+ /**
390
+ * Rows to pin to the TOP of the grid - rendered above the regular
391
+ * rows and sticky-positioned so they stay visible while the user
392
+ * scrolls. Typical use: a "totals" or "headline" row that should
393
+ * always be in view. Rows are read-only (no inline editing, no
394
+ * row-selection checkbox). They share the column schema with the
395
+ * main grid; field/format/cell/cellClass all apply.
396
+ */
397
+ pinnedTopRows?: ReadonlyArray<TData>;
398
+ /**
399
+ * Rows to pin to the BOTTOM of the grid - rendered below the regular
400
+ * rows and sticky-positioned (sticks to the bottom of the viewport
401
+ * while the user scrolls). Typical use: a "page totals" or "grand
402
+ * total" row computed from `getDisplayedRows()`.
403
+ */
404
+ pinnedBottomRows?: ReadonlyArray<TData>;
405
+ /**
406
+ * Enables drag-to-reorder on the grid's column headers. When `true`,
407
+ * every header gets `draggable=true` and a drop indicator paints
408
+ * between headers during a drag. On drop the grid mutates its
409
+ * internal column order and fires `onColumnOrderChange` with the
410
+ * new order. Defaults to `false`.
411
+ */
412
+ enableColumnReorder?: boolean;
413
+ /**
414
+ * Initial column order, by `id` (falls back to `field`). When the
415
+ * user reorders columns, this is the starting state. After mount,
416
+ * the grid owns the order internally and emits `onColumnOrderChange`
417
+ * on every change - persist that to `localStorage` to restore.
418
+ */
419
+ columnOrder?: ReadonlyArray<string>;
420
+ /** Fires every time the column order changes (drag or `api.setColumnOrder`). */
421
+ onColumnOrderChange?: (order: ReadonlyArray<string>) => void;
422
+ /**
423
+ * BCP-47 locale tag (or array of fallbacks) used for accent- and
424
+ * case-insensitive text filtering / sorting / search. Powered by
425
+ * `Intl.Collator` with `sensitivity: 'base'`, so "cafe", "Café"
426
+ * and "CAFÉ" all match "cafe" without strain. Defaults to the
427
+ * browser's locale.
428
+ */
429
+ filterLocale?: string | ReadonlyArray<string>;
430
+ };
431
+
432
+ export type SelectionPoint = { rowIndex: number; colIndex: number };
433
+ export type SelectionRange = {
434
+ anchor: SelectionPoint | null;
435
+ focus: SelectionPoint | null;
436
+ };
437
+ export type CellEditState = {
438
+ rowId: string;
439
+ columnId: string;
440
+ editorType: CellEditorType;
441
+ value: unknown;
442
+ } | null;
443
+ export type FilterOperator =
444
+ | "contains"
445
+ | "equals"
446
+ | "startsWith"
447
+ | "greaterThan"
448
+ | "lessThan"
449
+ | "between"
450
+ | "isBlank";
451
+ export type FilterOption = {
452
+ value: FilterOperator;
453
+ label: string;
454
+ iconName: string;
455
+ };
456
+ export type MenuPosition = { x: number; y: number };