@toolbox-web/grid 1.6.2 → 1.8.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/README.md +51 -15
- package/all.js +267 -158
- package/all.js.map +1 -1
- package/index.js +866 -722
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +68 -1
- package/lib/core/grid.d.ts.map +1 -1
- package/lib/core/internal/header.d.ts.map +1 -1
- package/lib/core/plugin/base-plugin.d.ts +182 -1
- package/lib/core/plugin/base-plugin.d.ts.map +1 -1
- package/lib/core/plugin/index.d.ts +1 -1
- package/lib/core/plugin/index.d.ts.map +1 -1
- package/lib/core/plugin/plugin-manager.d.ts +56 -1
- package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
- package/lib/core/plugin/types.d.ts +36 -0
- package/lib/core/plugin/types.d.ts.map +1 -1
- package/lib/core/types.d.ts +1349 -31
- package/lib/core/types.d.ts.map +1 -1
- package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.js +140 -87
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js +64 -7
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
- package/lib/plugins/context-menu/index.js +123 -65
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/EditingPlugin.d.ts +6 -1
- package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
- package/lib/plugins/editing/index.js +95 -13
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/index.js +91 -34
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/FilteringPlugin.d.ts +6 -1
- package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
- package/lib/plugins/filtering/index.js +192 -123
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js +57 -0
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +7 -2
- package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
- package/lib/plugins/grouping-rows/index.js +142 -60
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +69 -12
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +70 -13
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +3 -3
- package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -1
- package/lib/plugins/pinned-columns/index.js +106 -36
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js +57 -0
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +57 -0
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/PrintPlugin.d.ts.map +1 -1
- package/lib/plugins/print/index.js +58 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
- package/lib/plugins/reorder/column-drag.d.ts +2 -2
- package/lib/plugins/reorder/index.js +68 -17
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/responsive/ResponsivePlugin.d.ts +6 -1
- package/lib/plugins/responsive/ResponsivePlugin.d.ts.map +1 -1
- package/lib/plugins/responsive/index.js +125 -54
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/row-reorder/index.js +169 -112
- package/lib/plugins/row-reorder/index.js.map +1 -1
- package/lib/plugins/selection/SelectionPlugin.d.ts +14 -2
- package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
- package/lib/plugins/selection/index.js +84 -7
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/index.js +79 -22
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/TreePlugin.d.ts +7 -1
- package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
- package/lib/plugins/tree/index.js +140 -58
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +6 -1
- package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
- package/lib/plugins/undo-redo/index.js +79 -10
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js +57 -0
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/public.d.ts +80 -2
- package/public.d.ts.map +1 -1
- package/umd/grid.all.umd.js +25 -25
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +15 -15
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js +5 -5
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/context-menu.umd.js +1 -1
- package/umd/plugins/context-menu.umd.js.map +1 -1
- package/umd/plugins/editing.umd.js +1 -1
- package/umd/plugins/editing.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +1 -1
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +2 -2
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/pinned-columns.umd.js +1 -1
- package/umd/plugins/pinned-columns.umd.js.map +1 -1
- package/umd/plugins/print.umd.js +1 -1
- package/umd/plugins/print.umd.js.map +1 -1
- package/umd/plugins/reorder.umd.js +1 -1
- package/umd/plugins/reorder.umd.js.map +1 -1
- package/umd/plugins/responsive.umd.js +1 -1
- package/umd/plugins/responsive.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +2 -2
- package/umd/plugins/selection.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
- package/umd/plugins/undo-redo.umd.js +1 -1
- package/umd/plugins/undo-redo.umd.js.map +1 -1
package/lib/core/types.d.ts
CHANGED
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
import { PluginQuery } from './plugin/base-plugin';
|
|
2
2
|
import { AfterCellRenderContext, AfterRowRenderContext, CellMouseEvent } from './plugin/types';
|
|
3
3
|
/**
|
|
4
|
-
* The compiled
|
|
4
|
+
* The compiled web component interface for DataGrid.
|
|
5
|
+
*
|
|
6
|
+
* This interface represents the `<tbw-grid>` custom element, combining
|
|
7
|
+
* the public grid API with standard HTMLElement functionality.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Query existing grid
|
|
12
|
+
* const grid = document.querySelector('tbw-grid') as DataGridElement<Employee>;
|
|
13
|
+
* grid.rows = employees;
|
|
14
|
+
* grid.addEventListener('cell-click', (e) => console.log(e.detail));
|
|
15
|
+
*
|
|
16
|
+
* // Create grid programmatically
|
|
17
|
+
* import { createGrid } from '@toolbox-web/grid';
|
|
18
|
+
* const grid = createGrid<Employee>({
|
|
19
|
+
* columns: [{ field: 'name' }, { field: 'email' }],
|
|
20
|
+
* });
|
|
21
|
+
* document.body.appendChild(grid);
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @see {@link PublicGrid} for the public API methods and properties
|
|
25
|
+
* @see {@link createGrid} for typed grid creation
|
|
26
|
+
* @see {@link queryGrid} for typed grid querying
|
|
5
27
|
*/
|
|
6
28
|
export interface DataGridElement extends PublicGrid, HTMLElement {
|
|
7
29
|
}
|
|
@@ -100,6 +122,74 @@ export interface PublicGrid<T = any> {
|
|
|
100
122
|
* Unregister a previously registered tool panel.
|
|
101
123
|
*/
|
|
102
124
|
unregisterToolPanel?(panelId: string): void;
|
|
125
|
+
/**
|
|
126
|
+
* Whether the grid is currently in a loading state.
|
|
127
|
+
* When true, displays a loading overlay with spinner.
|
|
128
|
+
*
|
|
129
|
+
* Can also be set via the `loading` HTML attribute.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* // Show loading overlay
|
|
134
|
+
* grid.loading = true;
|
|
135
|
+
* const data = await fetchData();
|
|
136
|
+
* grid.rows = data;
|
|
137
|
+
* grid.loading = false;
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
loading?: boolean;
|
|
141
|
+
/**
|
|
142
|
+
* Set loading state for a specific row.
|
|
143
|
+
* Displays a small spinner indicator on the row.
|
|
144
|
+
*
|
|
145
|
+
* Use when persisting row data or performing row-level async operations.
|
|
146
|
+
*
|
|
147
|
+
* @param rowId - The row's unique identifier (from getRowId)
|
|
148
|
+
* @param loading - Whether the row is loading
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* ```typescript
|
|
152
|
+
* // Show loading while saving row
|
|
153
|
+
* grid.setRowLoading('emp-123', true);
|
|
154
|
+
* await saveRow(row);
|
|
155
|
+
* grid.setRowLoading('emp-123', false);
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
setRowLoading?(rowId: string, loading: boolean): void;
|
|
159
|
+
/**
|
|
160
|
+
* Set loading state for a specific cell.
|
|
161
|
+
* Displays a small spinner indicator on the cell.
|
|
162
|
+
*
|
|
163
|
+
* Use when performing cell-level async operations (e.g., validation, lookup).
|
|
164
|
+
*
|
|
165
|
+
* @param rowId - The row's unique identifier (from getRowId)
|
|
166
|
+
* @param field - The column field
|
|
167
|
+
* @param loading - Whether the cell is loading
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```typescript
|
|
171
|
+
* // Show loading while validating cell
|
|
172
|
+
* grid.setCellLoading('emp-123', 'email', true);
|
|
173
|
+
* const isValid = await validateEmail(email);
|
|
174
|
+
* grid.setCellLoading('emp-123', 'email', false);
|
|
175
|
+
* ```
|
|
176
|
+
*/
|
|
177
|
+
setCellLoading?(rowId: string, field: string, loading: boolean): void;
|
|
178
|
+
/**
|
|
179
|
+
* Check if a row is currently in loading state.
|
|
180
|
+
* @param rowId - The row's unique identifier
|
|
181
|
+
*/
|
|
182
|
+
isRowLoading?(rowId: string): boolean;
|
|
183
|
+
/**
|
|
184
|
+
* Check if a cell is currently in loading state.
|
|
185
|
+
* @param rowId - The row's unique identifier
|
|
186
|
+
* @param field - The column field
|
|
187
|
+
*/
|
|
188
|
+
isCellLoading?(rowId: string, field: string): boolean;
|
|
189
|
+
/**
|
|
190
|
+
* Clear all row and cell loading states.
|
|
191
|
+
*/
|
|
192
|
+
clearAllLoading?(): void;
|
|
103
193
|
}
|
|
104
194
|
/**
|
|
105
195
|
* Internal-only augmented interface for DataGrid component.
|
|
@@ -212,10 +302,64 @@ export interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {
|
|
|
212
302
|
/** Request emission of column-state-change event (debounced) */
|
|
213
303
|
requestStateChange?: () => void;
|
|
214
304
|
}
|
|
305
|
+
/**
|
|
306
|
+
* Built-in primitive column types with automatic formatting and editing support.
|
|
307
|
+
*
|
|
308
|
+
* - `'string'` - Text content, default text input editor
|
|
309
|
+
* - `'number'` - Numeric content, right-aligned, number input editor
|
|
310
|
+
* - `'date'` - Date content, formatted display, date picker editor
|
|
311
|
+
* - `'boolean'` - True/false, rendered as checkbox
|
|
312
|
+
* - `'select'` - Dropdown selection from `options` array
|
|
313
|
+
*
|
|
314
|
+
* @example
|
|
315
|
+
* ```typescript
|
|
316
|
+
* columns: [
|
|
317
|
+
* { field: 'name', type: 'string' },
|
|
318
|
+
* { field: 'age', type: 'number' },
|
|
319
|
+
* { field: 'hireDate', type: 'date' },
|
|
320
|
+
* { field: 'active', type: 'boolean' },
|
|
321
|
+
* { field: 'department', type: 'select', options: [
|
|
322
|
+
* { label: 'Engineering', value: 'eng' },
|
|
323
|
+
* { label: 'Sales', value: 'sales' },
|
|
324
|
+
* ]},
|
|
325
|
+
* ]
|
|
326
|
+
* ```
|
|
327
|
+
*
|
|
328
|
+
* @see {@link ColumnType} for custom type support
|
|
329
|
+
* @see {@link TypeDefault} for type-level defaults
|
|
330
|
+
*/
|
|
215
331
|
export type PrimitiveColumnType = 'number' | 'string' | 'date' | 'boolean' | 'select';
|
|
216
332
|
/**
|
|
217
333
|
* Column type - built-in primitives or custom type strings.
|
|
218
|
-
*
|
|
334
|
+
*
|
|
335
|
+
* Use built-in types for automatic formatting, or define custom types
|
|
336
|
+
* (e.g., 'currency', 'country') with type-level defaults via `typeDefaults`.
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* ```typescript
|
|
340
|
+
* // Built-in types
|
|
341
|
+
* { field: 'name', type: 'string' }
|
|
342
|
+
* { field: 'salary', type: 'number' }
|
|
343
|
+
*
|
|
344
|
+
* // Custom types with defaults
|
|
345
|
+
* grid.gridConfig = {
|
|
346
|
+
* columns: [
|
|
347
|
+
* { field: 'salary', type: 'currency' },
|
|
348
|
+
* { field: 'birthCountry', type: 'country' },
|
|
349
|
+
* ],
|
|
350
|
+
* typeDefaults: {
|
|
351
|
+
* currency: {
|
|
352
|
+
* format: (v) => `$${Number(v).toFixed(2)}`,
|
|
353
|
+
* },
|
|
354
|
+
* country: {
|
|
355
|
+
* renderer: (ctx) => `🌍 ${ctx.value}`,
|
|
356
|
+
* },
|
|
357
|
+
* },
|
|
358
|
+
* };
|
|
359
|
+
* ```
|
|
360
|
+
*
|
|
361
|
+
* @see {@link PrimitiveColumnType} for built-in types
|
|
362
|
+
* @see {@link TypeDefault} for defining custom type defaults
|
|
219
363
|
*/
|
|
220
364
|
export type ColumnType = PrimitiveColumnType | (string & {});
|
|
221
365
|
/**
|
|
@@ -243,6 +387,10 @@ export type ColumnType = PrimitiveColumnType | (string & {});
|
|
|
243
387
|
* }
|
|
244
388
|
* }
|
|
245
389
|
* ```
|
|
390
|
+
*
|
|
391
|
+
* @see {@link ColumnViewRenderer} for custom renderer function signature
|
|
392
|
+
* @see {@link ColumnType} for type strings that can have defaults
|
|
393
|
+
* @see {@link GridConfig.typeDefaults} for registering type defaults
|
|
246
394
|
*/
|
|
247
395
|
export interface TypeDefault<TRow = unknown> {
|
|
248
396
|
/**
|
|
@@ -302,8 +450,41 @@ export interface TypeDefault<TRow = unknown> {
|
|
|
302
450
|
renderer?: ColumnViewRenderer<TRow, unknown>;
|
|
303
451
|
}
|
|
304
452
|
/**
|
|
305
|
-
* Base contract for a column
|
|
306
|
-
*
|
|
453
|
+
* Base contract for a column configuration.
|
|
454
|
+
*
|
|
455
|
+
* Defines the fundamental properties all columns share. Extended by {@link ColumnConfig}
|
|
456
|
+
* with additional features like custom renderers and grouping.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* // Basic column with common properties
|
|
461
|
+
* const columns: BaseColumnConfig<Employee>[] = [
|
|
462
|
+
* {
|
|
463
|
+
* field: 'name',
|
|
464
|
+
* header: 'Full Name',
|
|
465
|
+
* sortable: true,
|
|
466
|
+
* resizable: true,
|
|
467
|
+
* },
|
|
468
|
+
* {
|
|
469
|
+
* field: 'salary',
|
|
470
|
+
* type: 'number',
|
|
471
|
+
* width: 120,
|
|
472
|
+
* format: (value) => `$${value.toLocaleString()}`,
|
|
473
|
+
* sortComparator: (a, b) => a - b,
|
|
474
|
+
* },
|
|
475
|
+
* {
|
|
476
|
+
* field: 'department',
|
|
477
|
+
* type: 'select',
|
|
478
|
+
* options: [
|
|
479
|
+
* { label: 'Engineering', value: 'eng' },
|
|
480
|
+
* { label: 'Sales', value: 'sales' },
|
|
481
|
+
* ],
|
|
482
|
+
* },
|
|
483
|
+
* ];
|
|
484
|
+
* ```
|
|
485
|
+
*
|
|
486
|
+
* @see {@link ColumnConfig} for full column configuration with renderers
|
|
487
|
+
* @see {@link ColumnType} for type options
|
|
307
488
|
*/
|
|
308
489
|
export interface BaseColumnConfig<TRow = any, TValue = any> {
|
|
309
490
|
/** Unique field key referencing property in row objects */
|
|
@@ -347,7 +528,54 @@ export interface BaseColumnConfig<TRow = any, TValue = any> {
|
|
|
347
528
|
meta?: Record<string, unknown>;
|
|
348
529
|
}
|
|
349
530
|
/**
|
|
350
|
-
* Full column configuration including
|
|
531
|
+
* Full column configuration including custom renderers, editors, and grouping metadata.
|
|
532
|
+
*
|
|
533
|
+
* Extends {@link BaseColumnConfig} with additional features for customizing
|
|
534
|
+
* how cells are displayed and edited.
|
|
535
|
+
*
|
|
536
|
+
* @example
|
|
537
|
+
* ```typescript
|
|
538
|
+
* const columns: ColumnConfig<Employee>[] = [
|
|
539
|
+
* // Basic sortable column
|
|
540
|
+
* { field: 'id', header: 'ID', width: 60, sortable: true },
|
|
541
|
+
*
|
|
542
|
+
* // Column with custom renderer
|
|
543
|
+
* {
|
|
544
|
+
* field: 'name',
|
|
545
|
+
* header: 'Employee',
|
|
546
|
+
* renderer: (ctx) => {
|
|
547
|
+
* const div = document.createElement('div');
|
|
548
|
+
* div.innerHTML = `<img src="${ctx.row.avatar}" /><span>${ctx.value}</span>`;
|
|
549
|
+
* return div;
|
|
550
|
+
* },
|
|
551
|
+
* },
|
|
552
|
+
*
|
|
553
|
+
* // Column with custom header
|
|
554
|
+
* {
|
|
555
|
+
* field: 'email',
|
|
556
|
+
* headerLabelRenderer: (ctx) => `${ctx.value} 📧`,
|
|
557
|
+
* },
|
|
558
|
+
*
|
|
559
|
+
* // Editable column (requires EditingPlugin)
|
|
560
|
+
* {
|
|
561
|
+
* field: 'status',
|
|
562
|
+
* editable: true,
|
|
563
|
+
* editor: (ctx) => {
|
|
564
|
+
* const select = document.createElement('select');
|
|
565
|
+
* // ... editor implementation
|
|
566
|
+
* return select;
|
|
567
|
+
* },
|
|
568
|
+
* },
|
|
569
|
+
*
|
|
570
|
+
* // Hidden column (can be shown via VisibilityPlugin)
|
|
571
|
+
* { field: 'internalNotes', hidden: true },
|
|
572
|
+
* ];
|
|
573
|
+
* ```
|
|
574
|
+
*
|
|
575
|
+
* @see {@link BaseColumnConfig} for basic column properties
|
|
576
|
+
* @see {@link ColumnViewRenderer} for custom cell renderers
|
|
577
|
+
* @see {@link ColumnEditorSpec} for custom cell editors
|
|
578
|
+
* @see {@link HeaderRenderer} for custom header renderers
|
|
351
579
|
*/
|
|
352
580
|
export interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {
|
|
353
581
|
/**
|
|
@@ -422,12 +650,11 @@ export interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {
|
|
|
422
650
|
headerLabelRenderer?: HeaderLabelRenderer<TRow>;
|
|
423
651
|
/**
|
|
424
652
|
* Custom header cell renderer. Complete control over the entire header cell.
|
|
425
|
-
*
|
|
653
|
+
* Resize handles are added automatically for resizable columns.
|
|
426
654
|
*
|
|
427
|
-
* The context provides helper functions to include standard elements
|
|
655
|
+
* The context provides helper functions to include standard elements:
|
|
428
656
|
* - `renderSortIcon()` - Returns sort indicator element (null if not sortable)
|
|
429
657
|
* - `renderFilterButton()` - Returns filter button (null if not filterable)
|
|
430
|
-
* - `renderResizeHandle()` - Returns resize handle element
|
|
431
658
|
*
|
|
432
659
|
* **Precedence**: `headerRenderer` > `headerLabelRenderer` > `header` > `field`
|
|
433
660
|
*
|
|
@@ -445,8 +672,75 @@ export interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {
|
|
|
445
672
|
*/
|
|
446
673
|
headerRenderer?: HeaderRenderer<TRow>;
|
|
447
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Array of column configurations.
|
|
677
|
+
* Convenience type alias for `ColumnConfig<TRow>[]`.
|
|
678
|
+
*
|
|
679
|
+
* @example
|
|
680
|
+
* ```typescript
|
|
681
|
+
* const columns: ColumnConfigMap<Employee> = [
|
|
682
|
+
* { field: 'name', header: 'Full Name', sortable: true },
|
|
683
|
+
* { field: 'email', header: 'Email Address' },
|
|
684
|
+
* { field: 'department', type: 'select', options: deptOptions },
|
|
685
|
+
* ];
|
|
686
|
+
*
|
|
687
|
+
* grid.columns = columns;
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* @see {@link ColumnConfig} for individual column options
|
|
691
|
+
* @see {@link GridConfig.columns} for setting columns on the grid
|
|
692
|
+
*/
|
|
448
693
|
export type ColumnConfigMap<TRow = any> = ColumnConfig<TRow>[];
|
|
449
|
-
/**
|
|
694
|
+
/**
|
|
695
|
+
* Editor specification for inline cell editing.
|
|
696
|
+
* Supports multiple formats for maximum flexibility.
|
|
697
|
+
*
|
|
698
|
+
* **Format Options:**
|
|
699
|
+
* - `string` - Custom element tag name (e.g., 'my-date-picker')
|
|
700
|
+
* - `function` - Factory function returning an editor element
|
|
701
|
+
* - `object` - External component spec for framework integration
|
|
702
|
+
*
|
|
703
|
+
* @example
|
|
704
|
+
* ```typescript
|
|
705
|
+
* // 1. Custom element tag name
|
|
706
|
+
* columns: [
|
|
707
|
+
* { field: 'date', editor: 'my-date-picker' }
|
|
708
|
+
* ]
|
|
709
|
+
*
|
|
710
|
+
* // 2. Factory function (full control)
|
|
711
|
+
* columns: [
|
|
712
|
+
* {
|
|
713
|
+
* field: 'status',
|
|
714
|
+
* editor: (ctx) => {
|
|
715
|
+
* const select = document.createElement('select');
|
|
716
|
+
* select.innerHTML = `
|
|
717
|
+
* <option value="active">Active</option>
|
|
718
|
+
* <option value="inactive">Inactive</option>
|
|
719
|
+
* `;
|
|
720
|
+
* select.value = ctx.value;
|
|
721
|
+
* select.onchange = () => ctx.commit(select.value);
|
|
722
|
+
* select.onkeydown = (e) => {
|
|
723
|
+
* if (e.key === 'Escape') ctx.cancel();
|
|
724
|
+
* };
|
|
725
|
+
* return select;
|
|
726
|
+
* }
|
|
727
|
+
* }
|
|
728
|
+
* ]
|
|
729
|
+
*
|
|
730
|
+
* // 3. External component (React, Angular, Vue)
|
|
731
|
+
* columns: [
|
|
732
|
+
* {
|
|
733
|
+
* field: 'country',
|
|
734
|
+
* editor: {
|
|
735
|
+
* component: CountrySelect,
|
|
736
|
+
* props: { showFlags: true }
|
|
737
|
+
* }
|
|
738
|
+
* }
|
|
739
|
+
* ]
|
|
740
|
+
* ```
|
|
741
|
+
*
|
|
742
|
+
* @see {@link ColumnEditorContext} for the context passed to factory functions
|
|
743
|
+
*/
|
|
450
744
|
export type ColumnEditorSpec<TRow = unknown, TValue = unknown> = string | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string) | {
|
|
451
745
|
/** Arbitrary component reference (class, function, token) */
|
|
452
746
|
component: unknown;
|
|
@@ -463,6 +757,37 @@ export type ColumnEditorSpec<TRow = unknown, TValue = unknown> = string | ((cont
|
|
|
463
757
|
};
|
|
464
758
|
/**
|
|
465
759
|
* Context object provided to editor factories allowing mutation (commit/cancel) of a cell value.
|
|
760
|
+
*
|
|
761
|
+
* The `commit` and `cancel` functions control the editing lifecycle:
|
|
762
|
+
* - Call `commit(newValue)` to save changes and exit edit mode
|
|
763
|
+
* - Call `cancel()` to discard changes and exit edit mode
|
|
764
|
+
*
|
|
765
|
+
* @example
|
|
766
|
+
* ```typescript
|
|
767
|
+
* const myEditor: ColumnEditorSpec = (ctx: ColumnEditorContext) => {
|
|
768
|
+
* const input = document.createElement('input');
|
|
769
|
+
* input.value = ctx.value;
|
|
770
|
+
* input.className = 'my-editor';
|
|
771
|
+
*
|
|
772
|
+
* // Save on Enter, cancel on Escape
|
|
773
|
+
* input.onkeydown = (e) => {
|
|
774
|
+
* if (e.key === 'Enter') {
|
|
775
|
+
* ctx.commit(input.value);
|
|
776
|
+
* } else if (e.key === 'Escape') {
|
|
777
|
+
* ctx.cancel();
|
|
778
|
+
* }
|
|
779
|
+
* };
|
|
780
|
+
*
|
|
781
|
+
* // Access row data for validation
|
|
782
|
+
* if (ctx.row.locked) {
|
|
783
|
+
* input.disabled = true;
|
|
784
|
+
* }
|
|
785
|
+
*
|
|
786
|
+
* return input;
|
|
787
|
+
* };
|
|
788
|
+
* ```
|
|
789
|
+
*
|
|
790
|
+
* @see {@link ColumnEditorSpec} for editor specification options
|
|
466
791
|
*/
|
|
467
792
|
export interface ColumnEditorContext<TRow = any, TValue = any> {
|
|
468
793
|
/** Underlying full row object for the active edit. */
|
|
@@ -480,6 +805,36 @@ export interface ColumnEditorContext<TRow = any, TValue = any> {
|
|
|
480
805
|
}
|
|
481
806
|
/**
|
|
482
807
|
* Context passed to custom view renderers (pure display – no commit helpers).
|
|
808
|
+
*
|
|
809
|
+
* Used by `viewRenderer` and `renderer` column properties to create
|
|
810
|
+
* custom cell content. Return a DOM node or HTML string.
|
|
811
|
+
*
|
|
812
|
+
* @example
|
|
813
|
+
* ```typescript
|
|
814
|
+
* // Status badge renderer
|
|
815
|
+
* const statusRenderer: ColumnViewRenderer = (ctx: CellRenderContext) => {
|
|
816
|
+
* const badge = document.createElement('span');
|
|
817
|
+
* badge.className = `badge badge-${ctx.value}`;
|
|
818
|
+
* badge.textContent = ctx.value;
|
|
819
|
+
* return badge;
|
|
820
|
+
* };
|
|
821
|
+
*
|
|
822
|
+
* // Progress bar using row data
|
|
823
|
+
* const progressRenderer: ColumnViewRenderer = (ctx) => {
|
|
824
|
+
* const bar = document.createElement('div');
|
|
825
|
+
* bar.className = 'progress-bar';
|
|
826
|
+
* bar.style.width = `${ctx.value}%`;
|
|
827
|
+
* bar.title = `${ctx.row.taskName}: ${ctx.value}%`;
|
|
828
|
+
* return bar;
|
|
829
|
+
* };
|
|
830
|
+
*
|
|
831
|
+
* // Return HTML string (simpler, less performant)
|
|
832
|
+
* const htmlRenderer: ColumnViewRenderer = (ctx) => {
|
|
833
|
+
* return `<strong>${ctx.value}</strong>`;
|
|
834
|
+
* };
|
|
835
|
+
* ```
|
|
836
|
+
*
|
|
837
|
+
* @see {@link ColumnViewRenderer} for the renderer function signature
|
|
483
838
|
*/
|
|
484
839
|
export interface CellRenderContext<TRow = any, TValue = any> {
|
|
485
840
|
/** Row object for the cell being rendered. */
|
|
@@ -497,6 +852,39 @@ export interface CellRenderContext<TRow = any, TValue = any> {
|
|
|
497
852
|
*/
|
|
498
853
|
cellEl?: HTMLElement;
|
|
499
854
|
}
|
|
855
|
+
/**
|
|
856
|
+
* Custom view renderer function for cell content.
|
|
857
|
+
*
|
|
858
|
+
* Returns one of:
|
|
859
|
+
* - `Node` - DOM element to display in the cell
|
|
860
|
+
* - `string` - HTML string (parsed and inserted)
|
|
861
|
+
* - `void | null` - Use default text rendering
|
|
862
|
+
*
|
|
863
|
+
* @example
|
|
864
|
+
* ```typescript
|
|
865
|
+
* // DOM element (recommended for interactivity)
|
|
866
|
+
* const avatarRenderer: ColumnViewRenderer<Employee> = (ctx) => {
|
|
867
|
+
* const img = document.createElement('img');
|
|
868
|
+
* img.src = ctx.row.avatarUrl;
|
|
869
|
+
* img.alt = ctx.row.name;
|
|
870
|
+
* img.className = 'avatar';
|
|
871
|
+
* return img;
|
|
872
|
+
* };
|
|
873
|
+
*
|
|
874
|
+
* // HTML string (simpler, good for static content)
|
|
875
|
+
* const emailRenderer: ColumnViewRenderer = (ctx) => {
|
|
876
|
+
* return `<a href="mailto:${ctx.value}">${ctx.value}</a>`;
|
|
877
|
+
* };
|
|
878
|
+
*
|
|
879
|
+
* // Conditional rendering
|
|
880
|
+
* const conditionalRenderer: ColumnViewRenderer = (ctx) => {
|
|
881
|
+
* if (!ctx.value) return null; // Use default
|
|
882
|
+
* return `<em>${ctx.value}</em>`;
|
|
883
|
+
* };
|
|
884
|
+
* ```
|
|
885
|
+
*
|
|
886
|
+
* @see {@link CellRenderContext} for available context properties
|
|
887
|
+
*/
|
|
500
888
|
export type ColumnViewRenderer<TRow = unknown, TValue = unknown> = (ctx: CellRenderContext<TRow, TValue>) => Node | string | void | null;
|
|
501
889
|
/**
|
|
502
890
|
* Context passed to `headerLabelRenderer` for customizing header label content.
|
|
@@ -519,7 +907,8 @@ export interface HeaderLabelContext<TRow = unknown> {
|
|
|
519
907
|
}
|
|
520
908
|
/**
|
|
521
909
|
* Context passed to `headerRenderer` for complete control over header cell content.
|
|
522
|
-
* When using this, the
|
|
910
|
+
* When using this, you control the header content. Resize handles are added automatically
|
|
911
|
+
* for resizable columns.
|
|
523
912
|
*
|
|
524
913
|
* @example
|
|
525
914
|
* ```typescript
|
|
@@ -527,10 +916,9 @@ export interface HeaderLabelContext<TRow = unknown> {
|
|
|
527
916
|
* const div = document.createElement('div');
|
|
528
917
|
* div.className = 'custom-header';
|
|
529
918
|
* div.innerHTML = `<span>${ctx.value}</span>`;
|
|
530
|
-
* // Optionally include
|
|
919
|
+
* // Optionally include sort icon
|
|
531
920
|
* const sortIcon = ctx.renderSortIcon();
|
|
532
921
|
* if (sortIcon) div.appendChild(sortIcon);
|
|
533
|
-
* div.appendChild(ctx.renderResizeHandle());
|
|
534
922
|
* return div;
|
|
535
923
|
* }
|
|
536
924
|
* ```
|
|
@@ -557,20 +945,79 @@ export interface HeaderCellContext<TRow = unknown> {
|
|
|
557
945
|
* Note: The actual button is added by FilteringPlugin's afterRender hook.
|
|
558
946
|
*/
|
|
559
947
|
renderFilterButton: () => HTMLElement | null;
|
|
560
|
-
/**
|
|
561
|
-
* Render the standard resize handle.
|
|
562
|
-
* Returns a handle element that will be managed by the grid's resize controller.
|
|
563
|
-
*/
|
|
564
|
-
renderResizeHandle: () => HTMLElement;
|
|
565
948
|
}
|
|
566
949
|
/**
|
|
567
950
|
* Header label renderer function type.
|
|
568
951
|
* Customize the label while framework handles sort icons, filter buttons, resize handles.
|
|
952
|
+
*
|
|
953
|
+
* Use this for simple label customizations without taking over the entire header.
|
|
954
|
+
* The grid automatically appends sort icons, filter buttons, and resize handles.
|
|
955
|
+
*
|
|
956
|
+
* @example
|
|
957
|
+
* ```typescript
|
|
958
|
+
* // Add required indicator
|
|
959
|
+
* const requiredHeader: HeaderLabelRenderer = (ctx) => {
|
|
960
|
+
* return `${ctx.value} <span style="color: red;">*</span>`;
|
|
961
|
+
* };
|
|
962
|
+
*
|
|
963
|
+
* // Add unit suffix
|
|
964
|
+
* const priceHeader: HeaderLabelRenderer = (ctx) => {
|
|
965
|
+
* const span = document.createElement('span');
|
|
966
|
+
* span.innerHTML = `${ctx.value} <small>(USD)</small>`;
|
|
967
|
+
* return span;
|
|
968
|
+
* };
|
|
969
|
+
*
|
|
970
|
+
* // Column config usage
|
|
971
|
+
* columns: [
|
|
972
|
+
* { field: 'name', headerLabelRenderer: requiredHeader },
|
|
973
|
+
* { field: 'price', headerLabelRenderer: priceHeader },
|
|
974
|
+
* ]
|
|
975
|
+
* ```
|
|
976
|
+
*
|
|
977
|
+
* @see {@link HeaderLabelContext} for context properties
|
|
978
|
+
* @see {@link HeaderRenderer} for full header control
|
|
569
979
|
*/
|
|
570
980
|
export type HeaderLabelRenderer<TRow = unknown> = (ctx: HeaderLabelContext<TRow>) => Node | string | void | null;
|
|
571
981
|
/**
|
|
572
982
|
* Header cell renderer function type.
|
|
573
983
|
* Full control over header cell content. User is responsible for all content and interactions.
|
|
984
|
+
*
|
|
985
|
+
* When using this, you have complete control but must manually include
|
|
986
|
+
* sort icons, filter buttons, and resize handles using the helper functions.
|
|
987
|
+
*
|
|
988
|
+
* @example
|
|
989
|
+
* ```typescript
|
|
990
|
+
* // Custom header with all standard elements
|
|
991
|
+
* const customHeader: HeaderRenderer = (ctx) => {
|
|
992
|
+
* const div = document.createElement('div');
|
|
993
|
+
* div.className = 'custom-header';
|
|
994
|
+
* div.innerHTML = `<span class="label">${ctx.value}</span>`;
|
|
995
|
+
*
|
|
996
|
+
* // Add sort icon (returns null if not sortable)
|
|
997
|
+
* const sortIcon = ctx.renderSortIcon();
|
|
998
|
+
* if (sortIcon) div.appendChild(sortIcon);
|
|
999
|
+
*
|
|
1000
|
+
* // Add filter button (returns null if not filterable)
|
|
1001
|
+
* const filterBtn = ctx.renderFilterButton();
|
|
1002
|
+
* if (filterBtn) div.appendChild(filterBtn);
|
|
1003
|
+
*
|
|
1004
|
+
* // Resize handles are added automatically for resizable columns
|
|
1005
|
+
* return div;
|
|
1006
|
+
* };
|
|
1007
|
+
*
|
|
1008
|
+
* // Minimal header (no sort/resize)
|
|
1009
|
+
* const minimalHeader: HeaderRenderer = (ctx) => {
|
|
1010
|
+
* return `<div class="minimal">${ctx.value}</div>`;
|
|
1011
|
+
* };
|
|
1012
|
+
*
|
|
1013
|
+
* // Column config usage
|
|
1014
|
+
* columns: [
|
|
1015
|
+
* { field: 'name', headerRenderer: customHeader },
|
|
1016
|
+
* ]
|
|
1017
|
+
* ```
|
|
1018
|
+
*
|
|
1019
|
+
* @see {@link HeaderCellContext} for context properties and helper functions
|
|
1020
|
+
* @see {@link HeaderLabelRenderer} for simpler label-only customization
|
|
574
1021
|
*/
|
|
575
1022
|
export type HeaderRenderer<TRow = unknown> = (ctx: HeaderCellContext<TRow>) => Node | string | void | null;
|
|
576
1023
|
/**
|
|
@@ -654,6 +1101,28 @@ export interface ColumnParsedAttributes {
|
|
|
654
1101
|
* Extended column config used internally.
|
|
655
1102
|
* Includes all internal properties needed during grid lifecycle.
|
|
656
1103
|
*
|
|
1104
|
+
* Plugin developers may need to access these when working with
|
|
1105
|
+
* column caching and compiled templates.
|
|
1106
|
+
*
|
|
1107
|
+
* @example
|
|
1108
|
+
* ```typescript
|
|
1109
|
+
* import type { ColumnInternal } from '@toolbox-web/grid';
|
|
1110
|
+
*
|
|
1111
|
+
* class MyPlugin extends BaseGridPlugin {
|
|
1112
|
+
* afterRender(): void {
|
|
1113
|
+
* // Access internal column properties
|
|
1114
|
+
* const columns = this.columns as ColumnInternal[];
|
|
1115
|
+
* for (const col of columns) {
|
|
1116
|
+
* // Check if column was auto-sized
|
|
1117
|
+
* if (col.__autoSized) {
|
|
1118
|
+
* console.log(`${col.field} was auto-sized`);
|
|
1119
|
+
* }
|
|
1120
|
+
* }
|
|
1121
|
+
* }
|
|
1122
|
+
* }
|
|
1123
|
+
* ```
|
|
1124
|
+
*
|
|
1125
|
+
* @see {@link ColumnConfig} for public column properties
|
|
657
1126
|
* @category Plugin Development
|
|
658
1127
|
* @internal
|
|
659
1128
|
*/
|
|
@@ -709,6 +1178,21 @@ export interface CompiledViewFunction<T = any> {
|
|
|
709
1178
|
/**
|
|
710
1179
|
* Runtime cell context used internally for compiled template execution.
|
|
711
1180
|
*
|
|
1181
|
+
* Contains the minimal context needed to render a cell: the row data,
|
|
1182
|
+
* cell value, field name, and column configuration.
|
|
1183
|
+
*
|
|
1184
|
+
* @example
|
|
1185
|
+
* ```typescript
|
|
1186
|
+
* import type { CellContext, ColumnInternal } from '@toolbox-web/grid';
|
|
1187
|
+
*
|
|
1188
|
+
* // Used internally by compiled templates
|
|
1189
|
+
* const renderCell = (ctx: CellContext) => {
|
|
1190
|
+
* return `<span title="${ctx.field}">${ctx.value}</span>`;
|
|
1191
|
+
* };
|
|
1192
|
+
* ```
|
|
1193
|
+
*
|
|
1194
|
+
* @see {@link CellRenderContext} for public cell render context
|
|
1195
|
+
* @see {@link EditorExecContext} for editor context with commit/cancel
|
|
712
1196
|
* @category Plugin Development
|
|
713
1197
|
*/
|
|
714
1198
|
export interface CellContext<T = any> {
|
|
@@ -720,6 +1204,27 @@ export interface CellContext<T = any> {
|
|
|
720
1204
|
/**
|
|
721
1205
|
* Internal editor execution context extending the generic cell context with commit helpers.
|
|
722
1206
|
*
|
|
1207
|
+
* Used internally by the editing system. For public editor APIs,
|
|
1208
|
+
* prefer using {@link ColumnEditorContext}.
|
|
1209
|
+
*
|
|
1210
|
+
* @example
|
|
1211
|
+
* ```typescript
|
|
1212
|
+
* import type { EditorExecContext } from '@toolbox-web/grid';
|
|
1213
|
+
*
|
|
1214
|
+
* // Internal editor template execution
|
|
1215
|
+
* const execEditor = (ctx: EditorExecContext) => {
|
|
1216
|
+
* const input = document.createElement('input');
|
|
1217
|
+
* input.value = String(ctx.value);
|
|
1218
|
+
* input.onkeydown = (e) => {
|
|
1219
|
+
* if (e.key === 'Enter') ctx.commit(input.value);
|
|
1220
|
+
* if (e.key === 'Escape') ctx.cancel();
|
|
1221
|
+
* };
|
|
1222
|
+
* return input;
|
|
1223
|
+
* };
|
|
1224
|
+
* ```
|
|
1225
|
+
*
|
|
1226
|
+
* @see {@link ColumnEditorContext} for public editor context
|
|
1227
|
+
* @see {@link CellContext} for base cell context
|
|
723
1228
|
* @category Plugin Development
|
|
724
1229
|
*/
|
|
725
1230
|
export interface EditorExecContext<T = any> extends CellContext<T> {
|
|
@@ -729,6 +1234,29 @@ export interface EditorExecContext<T = any> extends CellContext<T> {
|
|
|
729
1234
|
/**
|
|
730
1235
|
* Controller managing drag-based column resize lifecycle.
|
|
731
1236
|
*
|
|
1237
|
+
* Exposed internally for plugins that need to interact with resize behavior.
|
|
1238
|
+
*
|
|
1239
|
+
* @example
|
|
1240
|
+
* ```typescript
|
|
1241
|
+
* import type { ResizeController, InternalGrid } from '@toolbox-web/grid';
|
|
1242
|
+
*
|
|
1243
|
+
* class MyPlugin extends BaseGridPlugin {
|
|
1244
|
+
* handleColumnAction(colIndex: number): void {
|
|
1245
|
+
* const grid = this.grid as InternalGrid;
|
|
1246
|
+
* const resizeCtrl = grid._resizeController;
|
|
1247
|
+
*
|
|
1248
|
+
* // Check if resize is in progress
|
|
1249
|
+
* if (resizeCtrl?.isResizing) {
|
|
1250
|
+
* return; // Don't interfere
|
|
1251
|
+
* }
|
|
1252
|
+
*
|
|
1253
|
+
* // Reset column to configured width
|
|
1254
|
+
* resizeCtrl?.resetColumn(colIndex);
|
|
1255
|
+
* }
|
|
1256
|
+
* }
|
|
1257
|
+
* ```
|
|
1258
|
+
*
|
|
1259
|
+
* @see {@link ColumnResizeDetail} for resize event details
|
|
732
1260
|
* @category Plugin Development
|
|
733
1261
|
*/
|
|
734
1262
|
export interface ResizeController {
|
|
@@ -742,6 +1270,26 @@ export interface ResizeController {
|
|
|
742
1270
|
/**
|
|
743
1271
|
* Virtual window bookkeeping; modified in-place as scroll position changes.
|
|
744
1272
|
*
|
|
1273
|
+
* Tracks virtualization state for row rendering. The grid only renders
|
|
1274
|
+
* rows within the visible viewport window (start to end) plus overscan.
|
|
1275
|
+
*
|
|
1276
|
+
* @example
|
|
1277
|
+
* ```typescript
|
|
1278
|
+
* import type { VirtualState, InternalGrid } from '@toolbox-web/grid';
|
|
1279
|
+
*
|
|
1280
|
+
* class MyPlugin extends BaseGridPlugin {
|
|
1281
|
+
* logVirtualWindow(): void {
|
|
1282
|
+
* const grid = this.grid as InternalGrid;
|
|
1283
|
+
* const vs = grid.virtualization;
|
|
1284
|
+
*
|
|
1285
|
+
* console.log(`Row height: ${vs.rowHeight}px`);
|
|
1286
|
+
* console.log(`Visible rows: ${vs.start} to ${vs.end}`);
|
|
1287
|
+
* console.log(`Virtualization: ${vs.enabled ? 'on' : 'off'}`);
|
|
1288
|
+
* }
|
|
1289
|
+
* }
|
|
1290
|
+
* ```
|
|
1291
|
+
*
|
|
1292
|
+
* @see {@link GridConfig.rowHeight} for configuring row height
|
|
745
1293
|
* @category Plugin Development
|
|
746
1294
|
*/
|
|
747
1295
|
export interface VirtualState {
|
|
@@ -770,7 +1318,37 @@ export type InputLikeElement = HTMLInputElement | HTMLTextAreaElement | HTMLSele
|
|
|
770
1318
|
};
|
|
771
1319
|
/**
|
|
772
1320
|
* Group row rendering customization options.
|
|
773
|
-
*
|
|
1321
|
+
* Controls how group header rows are displayed in the GroupingRowsPlugin.
|
|
1322
|
+
*
|
|
1323
|
+
* @example
|
|
1324
|
+
* ```typescript
|
|
1325
|
+
* import { GroupingRowsPlugin } from '@toolbox-web/grid/all';
|
|
1326
|
+
*
|
|
1327
|
+
* new GroupingRowsPlugin({
|
|
1328
|
+
* groupBy: ['department', 'team'],
|
|
1329
|
+
* render: {
|
|
1330
|
+
* // Group row spans all columns
|
|
1331
|
+
* fullWidth: true,
|
|
1332
|
+
*
|
|
1333
|
+
* // Custom label format
|
|
1334
|
+
* formatLabel: (value, depth, key) => {
|
|
1335
|
+
* if (depth === 0) return `Department: ${value}`;
|
|
1336
|
+
* return `Team: ${value}`;
|
|
1337
|
+
* },
|
|
1338
|
+
*
|
|
1339
|
+
* // Show aggregates in group rows (when not fullWidth)
|
|
1340
|
+
* aggregators: {
|
|
1341
|
+
* salary: 'sum',
|
|
1342
|
+
* age: 'avg',
|
|
1343
|
+
* },
|
|
1344
|
+
*
|
|
1345
|
+
* // Custom CSS class
|
|
1346
|
+
* class: 'my-group-row',
|
|
1347
|
+
* },
|
|
1348
|
+
* });
|
|
1349
|
+
* ```
|
|
1350
|
+
*
|
|
1351
|
+
* @see {@link AggregatorRef} for aggregation options
|
|
774
1352
|
*/
|
|
775
1353
|
export interface RowGroupRenderConfig {
|
|
776
1354
|
/** If true, group rows span all columns (single full-width cell). Default false. */
|
|
@@ -782,12 +1360,78 @@ export interface RowGroupRenderConfig {
|
|
|
782
1360
|
/** Additional CSS class applied to each group row root element. */
|
|
783
1361
|
class?: string;
|
|
784
1362
|
}
|
|
1363
|
+
/**
|
|
1364
|
+
* Reference to an aggregation function for footer/group summaries.
|
|
1365
|
+
*
|
|
1366
|
+
* Can be either:
|
|
1367
|
+
* - A built-in aggregator name: `'sum'`, `'avg'`, `'min'`, `'max'`, `'count'`
|
|
1368
|
+
* - A custom function that calculates the aggregate value
|
|
1369
|
+
*
|
|
1370
|
+
* @example
|
|
1371
|
+
* ```typescript
|
|
1372
|
+
* // Built-in aggregator
|
|
1373
|
+
* { field: 'amount', aggregator: 'sum' }
|
|
1374
|
+
*
|
|
1375
|
+
* // Custom aggregator function
|
|
1376
|
+
* { field: 'price', aggregator: (rows, field) => {
|
|
1377
|
+
* const values = rows.map(r => r[field]).filter(v => v != null);
|
|
1378
|
+
* return values.length ? Math.max(...values) : null;
|
|
1379
|
+
* }}
|
|
1380
|
+
* ```
|
|
1381
|
+
*
|
|
1382
|
+
* @see {@link RowGroupRenderConfig} for using aggregators in group rows
|
|
1383
|
+
*/
|
|
785
1384
|
export type AggregatorRef = string | ((rows: unknown[], field: string, column?: unknown) => unknown);
|
|
786
|
-
/**
|
|
1385
|
+
/**
|
|
1386
|
+
* Result of automatic column inference from sample rows.
|
|
1387
|
+
*
|
|
1388
|
+
* When no columns are configured, the grid analyzes the first row of data
|
|
1389
|
+
* to automatically generate column definitions with inferred types.
|
|
1390
|
+
*
|
|
1391
|
+
* @example
|
|
1392
|
+
* ```typescript
|
|
1393
|
+
* // Automatic inference (no columns configured)
|
|
1394
|
+
* grid.rows = [
|
|
1395
|
+
* { name: 'Alice', age: 30, active: true, hireDate: new Date() },
|
|
1396
|
+
* ];
|
|
1397
|
+
* // Grid infers:
|
|
1398
|
+
* // - name: type 'string'
|
|
1399
|
+
* // - age: type 'number'
|
|
1400
|
+
* // - active: type 'boolean'
|
|
1401
|
+
* // - hireDate: type 'date'
|
|
1402
|
+
*
|
|
1403
|
+
* // Access inferred result programmatically
|
|
1404
|
+
* const config = await grid.getConfig();
|
|
1405
|
+
* console.log(config.columns); // Inferred columns
|
|
1406
|
+
* ```
|
|
1407
|
+
*
|
|
1408
|
+
* @see {@link ColumnConfig} for column configuration options
|
|
1409
|
+
* @see {@link ColumnType} for type inference rules
|
|
1410
|
+
*/
|
|
787
1411
|
export interface InferredColumnResult<TRow = unknown> {
|
|
1412
|
+
/** Generated column configurations based on data analysis */
|
|
788
1413
|
columns: ColumnConfigMap<TRow>;
|
|
1414
|
+
/** Map of field names to their inferred types */
|
|
789
1415
|
typeMap: Record<string, ColumnType>;
|
|
790
1416
|
}
|
|
1417
|
+
/**
|
|
1418
|
+
* Column sizing mode.
|
|
1419
|
+
*
|
|
1420
|
+
* - `'fixed'` - Columns use their configured widths. Horizontal scrolling if content overflows.
|
|
1421
|
+
* - `'stretch'` - Columns stretch proportionally to fill available width. No horizontal scrolling.
|
|
1422
|
+
*
|
|
1423
|
+
* @example
|
|
1424
|
+
* ```typescript
|
|
1425
|
+
* // Fixed widths - good for many columns
|
|
1426
|
+
* grid.fitMode = 'fixed';
|
|
1427
|
+
*
|
|
1428
|
+
* // Stretch to fill - good for few columns
|
|
1429
|
+
* grid.fitMode = 'stretch';
|
|
1430
|
+
*
|
|
1431
|
+
* // Via gridConfig
|
|
1432
|
+
* grid.gridConfig = { fitMode: 'stretch' };
|
|
1433
|
+
* ```
|
|
1434
|
+
*/
|
|
791
1435
|
export declare const FitModeEnum: {
|
|
792
1436
|
readonly STRETCH: "stretch";
|
|
793
1437
|
readonly FIXED: "fixed";
|
|
@@ -798,11 +1442,30 @@ export type FitMode = (typeof FitModeEnum)[keyof typeof FitModeEnum];
|
|
|
798
1442
|
* This interface is defined here to avoid circular imports with BaseGridPlugin.
|
|
799
1443
|
* All plugins must satisfy this shape (BaseGridPlugin implements it).
|
|
800
1444
|
*
|
|
801
|
-
* @
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1445
|
+
* @example
|
|
1446
|
+
* ```typescript
|
|
1447
|
+
* // Using plugins in grid config
|
|
1448
|
+
* import { SelectionPlugin, FilteringPlugin } from '@toolbox-web/grid/all';
|
|
1449
|
+
*
|
|
1450
|
+
* grid.gridConfig = {
|
|
1451
|
+
* plugins: [
|
|
1452
|
+
* new SelectionPlugin({ mode: 'row' }),
|
|
1453
|
+
* new FilteringPlugin({ debounceMs: 200 }),
|
|
1454
|
+
* ],
|
|
1455
|
+
* };
|
|
1456
|
+
*
|
|
1457
|
+
* // Accessing plugin instance at runtime
|
|
1458
|
+
* const selection = grid.getPlugin(SelectionPlugin);
|
|
1459
|
+
* if (selection) {
|
|
1460
|
+
* selection.selectAll();
|
|
1461
|
+
* }
|
|
1462
|
+
* ```
|
|
1463
|
+
*
|
|
1464
|
+
* @category Plugin Development
|
|
1465
|
+
*/
|
|
1466
|
+
export interface GridPlugin {
|
|
1467
|
+
/** Unique plugin identifier */
|
|
1468
|
+
readonly name: string;
|
|
806
1469
|
/** Plugin version */
|
|
807
1470
|
readonly version: string;
|
|
808
1471
|
/** CSS styles to inject into the grid */
|
|
@@ -840,7 +1503,11 @@ export interface GridPlugin {
|
|
|
840
1503
|
* ```
|
|
841
1504
|
*/
|
|
842
1505
|
export interface GridConfig<TRow = any> {
|
|
843
|
-
/**
|
|
1506
|
+
/**
|
|
1507
|
+
* Column definitions. Can also be set via `columns` prop or `<tbw-grid-column>` light DOM.
|
|
1508
|
+
* @see {@link ColumnConfig} for column options
|
|
1509
|
+
* @see {@link ColumnConfigMap}
|
|
1510
|
+
*/
|
|
844
1511
|
columns?: ColumnConfigMap<TRow>;
|
|
845
1512
|
/**
|
|
846
1513
|
* Dynamic CSS class(es) for data rows.
|
|
@@ -880,6 +1547,27 @@ export interface GridConfig<TRow = any> {
|
|
|
880
1547
|
* ```
|
|
881
1548
|
*/
|
|
882
1549
|
sortable?: boolean;
|
|
1550
|
+
/**
|
|
1551
|
+
* Grid-wide resizing toggle.
|
|
1552
|
+
* When false, disables column resizing for all columns regardless of their individual `resizable` setting.
|
|
1553
|
+
* When true (default), columns with `resizable: true` (or resizable not set, since it defaults to true) can be resized.
|
|
1554
|
+
*
|
|
1555
|
+
* This affects:
|
|
1556
|
+
* - Resize handle visibility in header cells
|
|
1557
|
+
* - Double-click to auto-size behavior
|
|
1558
|
+
*
|
|
1559
|
+
* @default true
|
|
1560
|
+
*
|
|
1561
|
+
* @example
|
|
1562
|
+
* ```typescript
|
|
1563
|
+
* // Disable all column resizing
|
|
1564
|
+
* gridConfig = { resizable: false };
|
|
1565
|
+
*
|
|
1566
|
+
* // Enable resizing (default) - individual columns can opt out with resizable: false
|
|
1567
|
+
* gridConfig = { resizable: true };
|
|
1568
|
+
* ```
|
|
1569
|
+
*/
|
|
1570
|
+
resizable?: boolean;
|
|
883
1571
|
/**
|
|
884
1572
|
* Row height in pixels for virtualization calculations.
|
|
885
1573
|
* The virtualization system assumes uniform row heights for performance.
|
|
@@ -1043,9 +1731,52 @@ export interface GridConfig<TRow = any> {
|
|
|
1043
1731
|
* ```
|
|
1044
1732
|
*/
|
|
1045
1733
|
gridAriaDescribedBy?: string;
|
|
1734
|
+
/**
|
|
1735
|
+
* Custom renderer for the loading overlay.
|
|
1736
|
+
*
|
|
1737
|
+
* When provided, replaces the default spinner with custom content.
|
|
1738
|
+
* Receives a context object with the current loading size.
|
|
1739
|
+
*
|
|
1740
|
+
* @example
|
|
1741
|
+
* ```typescript
|
|
1742
|
+
* // Simple text loading indicator
|
|
1743
|
+
* loadingRenderer: () => {
|
|
1744
|
+
* const el = document.createElement('div');
|
|
1745
|
+
* el.textContent = 'Loading...';
|
|
1746
|
+
* return el;
|
|
1747
|
+
* }
|
|
1748
|
+
*
|
|
1749
|
+
* // Custom spinner component
|
|
1750
|
+
* loadingRenderer: (ctx) => {
|
|
1751
|
+
* const spinner = document.createElement('my-spinner');
|
|
1752
|
+
* spinner.size = ctx.size === 'large' ? 48 : 24;
|
|
1753
|
+
* return spinner;
|
|
1754
|
+
* }
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
loadingRenderer?: LoadingRenderer;
|
|
1046
1758
|
}
|
|
1047
1759
|
/**
|
|
1048
1760
|
* Sort state passed to custom sort handlers.
|
|
1761
|
+
* Represents the current sorting configuration for a column.
|
|
1762
|
+
*
|
|
1763
|
+
* @example
|
|
1764
|
+
* ```typescript
|
|
1765
|
+
* // In a custom sort handler
|
|
1766
|
+
* const sortHandler: SortHandler = (rows, sortState, columns) => {
|
|
1767
|
+
* const { field, direction } = sortState;
|
|
1768
|
+
* console.log(`Sorting by ${field} ${direction === 1 ? 'ASC' : 'DESC'}`);
|
|
1769
|
+
*
|
|
1770
|
+
* return [...rows].sort((a, b) => {
|
|
1771
|
+
* const aVal = a[field];
|
|
1772
|
+
* const bVal = b[field];
|
|
1773
|
+
* return (aVal < bVal ? -1 : aVal > bVal ? 1 : 0) * direction;
|
|
1774
|
+
* });
|
|
1775
|
+
* };
|
|
1776
|
+
* ```
|
|
1777
|
+
*
|
|
1778
|
+
* @see {@link SortHandler} for custom sort handler signature
|
|
1779
|
+
* @see {@link SortChangeDetail} for sort change events
|
|
1049
1780
|
*/
|
|
1050
1781
|
export interface SortState {
|
|
1051
1782
|
/** Field to sort by */
|
|
@@ -1056,12 +1787,136 @@ export interface SortState {
|
|
|
1056
1787
|
/**
|
|
1057
1788
|
* Custom sort handler function signature.
|
|
1058
1789
|
*
|
|
1790
|
+
* Enables full control over sorting behavior including server-side sorting,
|
|
1791
|
+
* custom algorithms, or multi-column sorting.
|
|
1792
|
+
*
|
|
1059
1793
|
* @param rows - Current row array to sort
|
|
1060
1794
|
* @param sortState - Sort field and direction
|
|
1061
1795
|
* @param columns - Column configurations (for accessing sortComparator)
|
|
1062
1796
|
* @returns Sorted array (sync) or Promise resolving to sorted array (async)
|
|
1797
|
+
*
|
|
1798
|
+
* @example
|
|
1799
|
+
* ```typescript
|
|
1800
|
+
* // Custom client-side sort with locale awareness
|
|
1801
|
+
* const localeSortHandler: SortHandler<Employee> = (rows, state, cols) => {
|
|
1802
|
+
* const col = cols.find(c => c.field === state.field);
|
|
1803
|
+
* return [...rows].sort((a, b) => {
|
|
1804
|
+
* const aVal = String(a[state.field] ?? '');
|
|
1805
|
+
* const bVal = String(b[state.field] ?? '');
|
|
1806
|
+
* return aVal.localeCompare(bVal) * state.direction;
|
|
1807
|
+
* });
|
|
1808
|
+
* };
|
|
1809
|
+
*
|
|
1810
|
+
* // Server-side sorting
|
|
1811
|
+
* const serverSortHandler: SortHandler<Employee> = async (rows, state) => {
|
|
1812
|
+
* const response = await fetch(
|
|
1813
|
+
* `/api/employees?sortBy=${state.field}&dir=${state.direction}`
|
|
1814
|
+
* );
|
|
1815
|
+
* return response.json();
|
|
1816
|
+
* };
|
|
1817
|
+
*
|
|
1818
|
+
* grid.gridConfig = {
|
|
1819
|
+
* sortHandler: localeSortHandler,
|
|
1820
|
+
* };
|
|
1821
|
+
* ```
|
|
1822
|
+
*
|
|
1823
|
+
* @see {@link SortState} for the sort state object
|
|
1824
|
+
* @see {@link GridConfig.sortHandler} for configuring the handler
|
|
1825
|
+
* @see {@link BaseColumnConfig.sortComparator} for column-level comparators
|
|
1063
1826
|
*/
|
|
1064
1827
|
export type SortHandler<TRow = any> = (rows: TRow[], sortState: SortState, columns: ColumnConfig<TRow>[]) => TRow[] | Promise<TRow[]>;
|
|
1828
|
+
/**
|
|
1829
|
+
* Loading indicator size variant.
|
|
1830
|
+
*
|
|
1831
|
+
* - `'large'`: 48x48px max - used for grid-level loading overlay (`grid.loading = true`)
|
|
1832
|
+
* - `'small'`: Follows row height - used for row/cell loading states
|
|
1833
|
+
*
|
|
1834
|
+
* @example
|
|
1835
|
+
* ```typescript
|
|
1836
|
+
* // Custom loading renderer adapting to size
|
|
1837
|
+
* const myLoader: LoadingRenderer = (ctx) => {
|
|
1838
|
+
* if (ctx.size === 'large') {
|
|
1839
|
+
* // Full overlay spinner
|
|
1840
|
+
* return '<div class="spinner-lg"></div>';
|
|
1841
|
+
* }
|
|
1842
|
+
* // Inline row/cell spinner
|
|
1843
|
+
* return '<span class="spinner-sm"></span>';
|
|
1844
|
+
* };
|
|
1845
|
+
* ```
|
|
1846
|
+
*
|
|
1847
|
+
* @see {@link LoadingRenderer} for custom loading renderer
|
|
1848
|
+
* @see {@link LoadingContext} for context passed to renderers
|
|
1849
|
+
*/
|
|
1850
|
+
export type LoadingSize = 'large' | 'small';
|
|
1851
|
+
/**
|
|
1852
|
+
* Context passed to custom loading renderers.
|
|
1853
|
+
*
|
|
1854
|
+
* Provides information about the loading indicator being rendered,
|
|
1855
|
+
* allowing the renderer to adapt its appearance based on the size variant.
|
|
1856
|
+
*
|
|
1857
|
+
* @example
|
|
1858
|
+
* ```typescript
|
|
1859
|
+
* const myLoadingRenderer: LoadingRenderer = (ctx: LoadingContext) => {
|
|
1860
|
+
* if (ctx.size === 'large') {
|
|
1861
|
+
* // Full-size spinner for grid-level loading
|
|
1862
|
+
* return '<div class="large-spinner"></div>';
|
|
1863
|
+
* } else {
|
|
1864
|
+
* // Compact spinner for row/cell loading
|
|
1865
|
+
* return '<div class="small-spinner"></div>';
|
|
1866
|
+
* }
|
|
1867
|
+
* };
|
|
1868
|
+
* ```
|
|
1869
|
+
*
|
|
1870
|
+
* @see {@link LoadingRenderer} for the renderer function signature
|
|
1871
|
+
* @see {@link LoadingSize} for available size variants
|
|
1872
|
+
*/
|
|
1873
|
+
export interface LoadingContext {
|
|
1874
|
+
/** The size variant being rendered: 'large' for grid-level, 'small' for row/cell */
|
|
1875
|
+
size: LoadingSize;
|
|
1876
|
+
}
|
|
1877
|
+
/**
|
|
1878
|
+
* Custom loading renderer function.
|
|
1879
|
+
* Returns an element or HTML string to display as the loading indicator.
|
|
1880
|
+
*
|
|
1881
|
+
* Used with the `loadingRenderer` property in {@link GridConfig} to replace
|
|
1882
|
+
* the default spinner with custom content.
|
|
1883
|
+
*
|
|
1884
|
+
* @param context - Context containing size information
|
|
1885
|
+
* @returns HTMLElement or HTML string
|
|
1886
|
+
*
|
|
1887
|
+
* @example
|
|
1888
|
+
* ```typescript
|
|
1889
|
+
* // Simple text loading indicator
|
|
1890
|
+
* const textLoader: LoadingRenderer = () => {
|
|
1891
|
+
* const el = document.createElement('div');
|
|
1892
|
+
* el.textContent = 'Loading...';
|
|
1893
|
+
* return el;
|
|
1894
|
+
* };
|
|
1895
|
+
*
|
|
1896
|
+
* // Custom spinner with size awareness
|
|
1897
|
+
* const customSpinner: LoadingRenderer = (ctx) => {
|
|
1898
|
+
* const spinner = document.createElement('my-spinner');
|
|
1899
|
+
* spinner.size = ctx.size === 'large' ? 48 : 24;
|
|
1900
|
+
* return spinner;
|
|
1901
|
+
* };
|
|
1902
|
+
*
|
|
1903
|
+
* // Material Design-style progress bar
|
|
1904
|
+
* const progressBar: LoadingRenderer = () => {
|
|
1905
|
+
* const container = document.createElement('div');
|
|
1906
|
+
* container.className = 'progress-bar-container';
|
|
1907
|
+
* container.innerHTML = '<div class="progress-bar"></div>';
|
|
1908
|
+
* return container;
|
|
1909
|
+
* };
|
|
1910
|
+
*
|
|
1911
|
+
* grid.gridConfig = {
|
|
1912
|
+
* loadingRenderer: customSpinner,
|
|
1913
|
+
* };
|
|
1914
|
+
* ```
|
|
1915
|
+
*
|
|
1916
|
+
* @see {@link LoadingContext} for the context object passed to the renderer
|
|
1917
|
+
* @see {@link LoadingSize} for size variants ('large' | 'small')
|
|
1918
|
+
*/
|
|
1919
|
+
export type LoadingRenderer = (context: LoadingContext) => HTMLElement | string;
|
|
1065
1920
|
/**
|
|
1066
1921
|
* Indicates the origin of a data change.
|
|
1067
1922
|
* Used to prevent infinite loops in cascade update handlers.
|
|
@@ -1070,6 +1925,25 @@ export type SortHandler<TRow = any> = (rows: TRow[], sortState: SortState, colum
|
|
|
1070
1925
|
* - `'cascade'`: Triggered by `updateRow()` in an event handler
|
|
1071
1926
|
* - `'api'`: External programmatic update via `grid.updateRow()`
|
|
1072
1927
|
*
|
|
1928
|
+
* @example
|
|
1929
|
+
* ```typescript
|
|
1930
|
+
* grid.addEventListener('cell-change', (e) => {
|
|
1931
|
+
* const { source, field, newValue } = e.detail;
|
|
1932
|
+
*
|
|
1933
|
+
* // Only cascade updates for user edits
|
|
1934
|
+
* if (source === 'user' && field === 'price') {
|
|
1935
|
+
* // Update calculated field (marked as 'cascade')
|
|
1936
|
+
* grid.updateRow(e.detail.rowId, {
|
|
1937
|
+
* total: newValue * e.detail.row.quantity,
|
|
1938
|
+
* });
|
|
1939
|
+
* }
|
|
1940
|
+
*
|
|
1941
|
+
* // Ignore cascade updates to prevent infinite loops
|
|
1942
|
+
* if (source === 'cascade') return;
|
|
1943
|
+
* });
|
|
1944
|
+
* ```
|
|
1945
|
+
*
|
|
1946
|
+
* @see {@link CellChangeDetail} for the event detail containing source
|
|
1073
1947
|
* @category Data Management
|
|
1074
1948
|
*/
|
|
1075
1949
|
export type UpdateSource = 'user' | 'cascade' | 'api';
|
|
@@ -1077,6 +1951,28 @@ export type UpdateSource = 'user' | 'cascade' | 'api';
|
|
|
1077
1951
|
* Detail for cell-change event (emitted by core after mutation).
|
|
1078
1952
|
* This is an informational event that fires for ALL data mutations.
|
|
1079
1953
|
*
|
|
1954
|
+
* Use this event for:
|
|
1955
|
+
* - Logging/auditing changes
|
|
1956
|
+
* - Cascading updates (updating other fields based on a change)
|
|
1957
|
+
* - Syncing changes to external state
|
|
1958
|
+
*
|
|
1959
|
+
* @example
|
|
1960
|
+
* ```typescript
|
|
1961
|
+
* grid.addEventListener('cell-change', (e: CustomEvent<CellChangeDetail>) => {
|
|
1962
|
+
* const { row, rowId, field, oldValue, newValue, source } = e.detail;
|
|
1963
|
+
*
|
|
1964
|
+
* console.log(`${field} changed from ${oldValue} to ${newValue}`);
|
|
1965
|
+
* console.log(`Change source: ${source}`);
|
|
1966
|
+
*
|
|
1967
|
+
* // Cascade: update total when price changes
|
|
1968
|
+
* if (source === 'user' && field === 'price') {
|
|
1969
|
+
* grid.updateRow(rowId, { total: newValue * row.quantity });
|
|
1970
|
+
* }
|
|
1971
|
+
* });
|
|
1972
|
+
* ```
|
|
1973
|
+
*
|
|
1974
|
+
* @see {@link UpdateSource} for understanding change origins
|
|
1975
|
+
* @see {@link CellCommitDetail} for the commit event (editing lifecycle)
|
|
1080
1976
|
* @category Events
|
|
1081
1977
|
*/
|
|
1082
1978
|
export interface CellChangeDetail<TRow = unknown> {
|
|
@@ -1100,6 +1996,23 @@ export interface CellChangeDetail<TRow = unknown> {
|
|
|
1100
1996
|
/**
|
|
1101
1997
|
* Batch update specification for updateRows().
|
|
1102
1998
|
*
|
|
1999
|
+
* Used when you need to update multiple rows at once efficiently.
|
|
2000
|
+
* The grid will batch all updates and trigger a single re-render.
|
|
2001
|
+
*
|
|
2002
|
+
* @example
|
|
2003
|
+
* ```typescript
|
|
2004
|
+
* // Update multiple rows in a single batch
|
|
2005
|
+
* const updates: RowUpdate<Employee>[] = [
|
|
2006
|
+
* { id: 'emp-1', changes: { status: 'active', updatedAt: new Date() } },
|
|
2007
|
+
* { id: 'emp-2', changes: { status: 'inactive' } },
|
|
2008
|
+
* { id: 'emp-3', changes: { salary: 75000 } },
|
|
2009
|
+
* ];
|
|
2010
|
+
*
|
|
2011
|
+
* grid.updateRows(updates);
|
|
2012
|
+
* ```
|
|
2013
|
+
*
|
|
2014
|
+
* @see {@link CellChangeDetail} for individual change events
|
|
2015
|
+
* @see {@link GridConfig.getRowId} for row identification
|
|
1103
2016
|
* @category Data Management
|
|
1104
2017
|
*/
|
|
1105
2018
|
export interface RowUpdate<TRow = unknown> {
|
|
@@ -1113,6 +2026,20 @@ export interface RowUpdate<TRow = unknown> {
|
|
|
1113
2026
|
* - `true` or `'on'`: Animations always enabled
|
|
1114
2027
|
* - `false` or `'off'`: Animations always disabled
|
|
1115
2028
|
* - `'reduced-motion'`: Respects `prefers-reduced-motion` media query (default)
|
|
2029
|
+
*
|
|
2030
|
+
* @example
|
|
2031
|
+
* ```typescript
|
|
2032
|
+
* // Force animations on (ignore system preference)
|
|
2033
|
+
* grid.gridConfig = { animation: { mode: 'on' } };
|
|
2034
|
+
*
|
|
2035
|
+
* // Disable all animations
|
|
2036
|
+
* grid.gridConfig = { animation: { mode: false } };
|
|
2037
|
+
*
|
|
2038
|
+
* // Respect user's accessibility settings (default)
|
|
2039
|
+
* grid.gridConfig = { animation: { mode: 'reduced-motion' } };
|
|
2040
|
+
* ```
|
|
2041
|
+
*
|
|
2042
|
+
* @see {@link AnimationConfig} for full animation configuration
|
|
1116
2043
|
*/
|
|
1117
2044
|
export type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';
|
|
1118
2045
|
/**
|
|
@@ -1121,6 +2048,21 @@ export type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';
|
|
|
1121
2048
|
* - `'fade'`: Opacity fade animation
|
|
1122
2049
|
* - `'flip'`: FLIP technique for position changes (First, Last, Invert, Play)
|
|
1123
2050
|
* - `false`: No animation for this specific feature
|
|
2051
|
+
*
|
|
2052
|
+
* @example
|
|
2053
|
+
* ```typescript
|
|
2054
|
+
* // Plugin-specific animation styles
|
|
2055
|
+
* new TreePlugin({
|
|
2056
|
+
* expandAnimation: 'slide', // Slide children down when expanding
|
|
2057
|
+
* });
|
|
2058
|
+
*
|
|
2059
|
+
* new ReorderPlugin({
|
|
2060
|
+
* animation: 'flip', // FLIP animation for column reordering
|
|
2061
|
+
* });
|
|
2062
|
+
* ```
|
|
2063
|
+
*
|
|
2064
|
+
* @see {@link AnimationConfig} for grid-wide animation settings
|
|
2065
|
+
* @see {@link ExpandCollapseAnimation} for expand/collapse-specific styles
|
|
1124
2066
|
*/
|
|
1125
2067
|
export type AnimationStyle = 'slide' | 'fade' | 'flip' | false;
|
|
1126
2068
|
/**
|
|
@@ -1129,6 +2071,21 @@ export type AnimationStyle = 'slide' | 'fade' | 'flip' | false;
|
|
|
1129
2071
|
* - `'slide'`: Slide down/up animation for expanding/collapsing content
|
|
1130
2072
|
* - `'fade'`: Fade in/out animation
|
|
1131
2073
|
* - `false`: No animation
|
|
2074
|
+
*
|
|
2075
|
+
* @example
|
|
2076
|
+
* ```typescript
|
|
2077
|
+
* // Tree rows slide down when expanding
|
|
2078
|
+
* new TreePlugin({ expandAnimation: 'slide' });
|
|
2079
|
+
*
|
|
2080
|
+
* // Row groups fade in/out
|
|
2081
|
+
* new GroupingRowsPlugin({ expandAnimation: 'fade' });
|
|
2082
|
+
*
|
|
2083
|
+
* // Master-detail panels with no animation
|
|
2084
|
+
* new MasterDetailPlugin({ expandAnimation: false });
|
|
2085
|
+
* ```
|
|
2086
|
+
*
|
|
2087
|
+
* @see {@link AnimationStyle} for all animation styles
|
|
2088
|
+
* @see {@link AnimationConfig} for grid-wide settings
|
|
1132
2089
|
*/
|
|
1133
2090
|
export type ExpandCollapseAnimation = 'slide' | 'fade' | false;
|
|
1134
2091
|
/**
|
|
@@ -1136,12 +2093,51 @@ export type ExpandCollapseAnimation = 'slide' | 'fade' | false;
|
|
|
1136
2093
|
* - `'change'`: Flash highlight when row data changes (e.g., after cell edit)
|
|
1137
2094
|
* - `'insert'`: Slide-in animation for newly added rows
|
|
1138
2095
|
* - `'remove'`: Fade-out animation for rows being removed
|
|
2096
|
+
*
|
|
2097
|
+
* @example
|
|
2098
|
+
* ```typescript
|
|
2099
|
+
* // Internal usage - row animation is triggered automatically:
|
|
2100
|
+
* // - 'change' after cell-commit event
|
|
2101
|
+
* // - 'insert' when rows are added to the grid
|
|
2102
|
+
* // - 'remove' when rows are deleted
|
|
2103
|
+
*
|
|
2104
|
+
* // The animation respects AnimationConfig.mode
|
|
2105
|
+
* grid.gridConfig = {
|
|
2106
|
+
* animation: { mode: 'on', duration: 300 },
|
|
2107
|
+
* };
|
|
2108
|
+
* ```
|
|
2109
|
+
*
|
|
2110
|
+
* @see {@link AnimationConfig} for animation configuration
|
|
1139
2111
|
*/
|
|
1140
2112
|
export type RowAnimationType = 'change' | 'insert' | 'remove';
|
|
1141
2113
|
/**
|
|
1142
2114
|
* Grid-wide animation configuration.
|
|
1143
2115
|
* Controls global animation behavior - individual plugins define their own animation styles.
|
|
1144
2116
|
* Duration and easing values set corresponding CSS variables on the grid element.
|
|
2117
|
+
*
|
|
2118
|
+
* @example
|
|
2119
|
+
* ```typescript
|
|
2120
|
+
* // Enable animations regardless of system preferences
|
|
2121
|
+
* grid.gridConfig = {
|
|
2122
|
+
* animation: {
|
|
2123
|
+
* mode: 'on',
|
|
2124
|
+
* duration: 300,
|
|
2125
|
+
* easing: 'cubic-bezier(0.4, 0, 0.2, 1)',
|
|
2126
|
+
* },
|
|
2127
|
+
* };
|
|
2128
|
+
*
|
|
2129
|
+
* // Disable all animations
|
|
2130
|
+
* grid.gridConfig = {
|
|
2131
|
+
* animation: { mode: 'off' },
|
|
2132
|
+
* };
|
|
2133
|
+
*
|
|
2134
|
+
* // Respect user's reduced-motion preference (default)
|
|
2135
|
+
* grid.gridConfig = {
|
|
2136
|
+
* animation: { mode: 'reduced-motion' },
|
|
2137
|
+
* };
|
|
2138
|
+
* ```
|
|
2139
|
+
*
|
|
2140
|
+
* @see {@link AnimationMode} for mode options
|
|
1145
2141
|
*/
|
|
1146
2142
|
export interface AnimationConfig {
|
|
1147
2143
|
/**
|
|
@@ -1169,6 +2165,27 @@ export type IconValue = string | HTMLElement;
|
|
|
1169
2165
|
/**
|
|
1170
2166
|
* Grid-wide icon configuration.
|
|
1171
2167
|
* All icons are optional - sensible defaults are used when not specified.
|
|
2168
|
+
*
|
|
2169
|
+
* Icons can be text (including emoji), HTML strings (for SVG), or HTMLElement instances.
|
|
2170
|
+
*
|
|
2171
|
+
* @example
|
|
2172
|
+
* ```typescript
|
|
2173
|
+
* grid.gridConfig = {
|
|
2174
|
+
* icons: {
|
|
2175
|
+
* // Emoji icons
|
|
2176
|
+
* expand: '➕',
|
|
2177
|
+
* collapse: '➖',
|
|
2178
|
+
*
|
|
2179
|
+
* // Custom SVG icon
|
|
2180
|
+
* sortAsc: '<svg viewBox="0 0 16 16"><path d="M8 4l4 8H4z"/></svg>',
|
|
2181
|
+
*
|
|
2182
|
+
* // Font icon class (wrap in span)
|
|
2183
|
+
* filter: '<span class="icon icon-filter"></span>',
|
|
2184
|
+
* },
|
|
2185
|
+
* };
|
|
2186
|
+
* ```
|
|
2187
|
+
*
|
|
2188
|
+
* @see {@link IconValue} for allowed icon formats
|
|
1172
2189
|
*/
|
|
1173
2190
|
export interface GridIcons {
|
|
1174
2191
|
/** Expand icon for collapsed items (trees, groups, details). Default: '▶' */
|
|
@@ -1198,6 +2215,39 @@ export interface GridIcons {
|
|
|
1198
2215
|
export declare const DEFAULT_GRID_ICONS: Required<GridIcons>;
|
|
1199
2216
|
/**
|
|
1200
2217
|
* Shell configuration for the grid's optional header bar and tool panels.
|
|
2218
|
+
*
|
|
2219
|
+
* The shell provides a wrapper around the grid with:
|
|
2220
|
+
* - Header bar with title, toolbar buttons, and custom content
|
|
2221
|
+
* - Collapsible side panel for filters, column visibility, settings, etc.
|
|
2222
|
+
*
|
|
2223
|
+
* @example
|
|
2224
|
+
* ```typescript
|
|
2225
|
+
* grid.gridConfig = {
|
|
2226
|
+
* shell: {
|
|
2227
|
+
* header: {
|
|
2228
|
+
* title: 'Employee Directory',
|
|
2229
|
+
* },
|
|
2230
|
+
* toolPanel: {
|
|
2231
|
+
* position: 'right',
|
|
2232
|
+
* defaultOpen: 'columns', // Open by default
|
|
2233
|
+
* },
|
|
2234
|
+
* },
|
|
2235
|
+
* plugins: [new VisibilityPlugin()], // Adds "Columns" panel
|
|
2236
|
+
* };
|
|
2237
|
+
*
|
|
2238
|
+
* // Register custom tool panels
|
|
2239
|
+
* grid.registerToolPanel({
|
|
2240
|
+
* id: 'filters',
|
|
2241
|
+
* title: 'Filters',
|
|
2242
|
+
* icon: '🔍',
|
|
2243
|
+
* render: (container) => {
|
|
2244
|
+
* container.innerHTML = '<div>Filter controls...</div>';
|
|
2245
|
+
* },
|
|
2246
|
+
* });
|
|
2247
|
+
* ```
|
|
2248
|
+
*
|
|
2249
|
+
* @see {@link ShellHeaderConfig} for header options
|
|
2250
|
+
* @see {@link ToolPanelConfig} for tool panel options
|
|
1201
2251
|
*/
|
|
1202
2252
|
export interface ShellConfig {
|
|
1203
2253
|
/** Shell header bar configuration */
|
|
@@ -1280,6 +2330,34 @@ export interface ToolbarContentDefinition {
|
|
|
1280
2330
|
}
|
|
1281
2331
|
/**
|
|
1282
2332
|
* Tool panel definition registered by plugins or consumers.
|
|
2333
|
+
*
|
|
2334
|
+
* Register via `grid.registerToolPanel()` to add panels to the sidebar.
|
|
2335
|
+
* Panels appear as collapsible sections with icons and titles.
|
|
2336
|
+
*
|
|
2337
|
+
* @example
|
|
2338
|
+
* ```typescript
|
|
2339
|
+
* grid.registerToolPanel({
|
|
2340
|
+
* id: 'filters',
|
|
2341
|
+
* title: 'Filters',
|
|
2342
|
+
* icon: '🔍',
|
|
2343
|
+
* tooltip: 'Filter grid data',
|
|
2344
|
+
* order: 10, // Lower = appears first
|
|
2345
|
+
* render: (container) => {
|
|
2346
|
+
* container.innerHTML = `
|
|
2347
|
+
* <div class="filter-panel">
|
|
2348
|
+
* <input type="text" placeholder="Search..." />
|
|
2349
|
+
* </div>
|
|
2350
|
+
* `;
|
|
2351
|
+
* // Return cleanup function
|
|
2352
|
+
* return () => container.innerHTML = '';
|
|
2353
|
+
* },
|
|
2354
|
+
* onClose: () => {
|
|
2355
|
+
* console.log('Filter panel closed');
|
|
2356
|
+
* },
|
|
2357
|
+
* });
|
|
2358
|
+
* ```
|
|
2359
|
+
*
|
|
2360
|
+
* @see {@link ShellConfig} for shell configuration
|
|
1283
2361
|
*/
|
|
1284
2362
|
export interface ToolPanelDefinition {
|
|
1285
2363
|
/** Unique panel ID */
|
|
@@ -1299,6 +2377,33 @@ export interface ToolPanelDefinition {
|
|
|
1299
2377
|
}
|
|
1300
2378
|
/**
|
|
1301
2379
|
* Header content definition for plugins contributing to shell header center section.
|
|
2380
|
+
*
|
|
2381
|
+
* Register via `grid.registerHeaderContent()` to add content between
|
|
2382
|
+
* the title and toolbar buttons.
|
|
2383
|
+
*
|
|
2384
|
+
* @example
|
|
2385
|
+
* ```typescript
|
|
2386
|
+
* grid.registerHeaderContent({
|
|
2387
|
+
* id: 'row-count',
|
|
2388
|
+
* order: 10,
|
|
2389
|
+
* render: (container) => {
|
|
2390
|
+
* const span = document.createElement('span');
|
|
2391
|
+
* span.className = 'row-count';
|
|
2392
|
+
* span.textContent = `${grid.rows.length} rows`;
|
|
2393
|
+
* container.appendChild(span);
|
|
2394
|
+
*
|
|
2395
|
+
* // Update on data changes
|
|
2396
|
+
* const update = () => span.textContent = `${grid.rows.length} rows`;
|
|
2397
|
+
* grid.addEventListener('data-change', update);
|
|
2398
|
+
*
|
|
2399
|
+
* return () => {
|
|
2400
|
+
* grid.removeEventListener('data-change', update);
|
|
2401
|
+
* };
|
|
2402
|
+
* },
|
|
2403
|
+
* });
|
|
2404
|
+
* ```
|
|
2405
|
+
*
|
|
2406
|
+
* @see {@link ShellConfig} for shell configuration
|
|
1302
2407
|
*/
|
|
1303
2408
|
export interface HeaderContentDefinition {
|
|
1304
2409
|
/** Unique content ID */
|
|
@@ -1314,15 +2419,43 @@ export interface HeaderContentDefinition {
|
|
|
1314
2419
|
* State for a single column. Captures user-driven changes at runtime.
|
|
1315
2420
|
* Plugins can extend this interface via module augmentation to add their own state.
|
|
1316
2421
|
*
|
|
2422
|
+
* Used with `grid.getColumnState()` and `grid.columnState` for persisting
|
|
2423
|
+
* user customizations (column widths, order, visibility, sort).
|
|
2424
|
+
*
|
|
1317
2425
|
* @example
|
|
1318
|
-
* ```
|
|
1319
|
-
* //
|
|
2426
|
+
* ```typescript
|
|
2427
|
+
* // Save column state to localStorage
|
|
2428
|
+
* const state = grid.getColumnState();
|
|
2429
|
+
* localStorage.setItem('gridState', JSON.stringify(state));
|
|
2430
|
+
*
|
|
2431
|
+
* // Restore on page load
|
|
2432
|
+
* const saved = localStorage.getItem('gridState');
|
|
2433
|
+
* if (saved) {
|
|
2434
|
+
* grid.columnState = JSON.parse(saved);
|
|
2435
|
+
* }
|
|
2436
|
+
*
|
|
2437
|
+
* // Example column state structure
|
|
2438
|
+
* const state: GridColumnState = {
|
|
2439
|
+
* columns: [
|
|
2440
|
+
* { field: 'name', order: 0, width: 200, hidden: false },
|
|
2441
|
+
* { field: 'email', order: 1, width: 300, hidden: false },
|
|
2442
|
+
* { field: 'phone', order: 2, hidden: true }, // Hidden column
|
|
2443
|
+
* ],
|
|
2444
|
+
* sort: { field: 'name', direction: 1 },
|
|
2445
|
+
* };
|
|
2446
|
+
* ```
|
|
2447
|
+
*
|
|
2448
|
+
* @example
|
|
2449
|
+
* ```typescript
|
|
2450
|
+
* // Plugin augmentation example (in filtering plugin)
|
|
1320
2451
|
* declare module '@toolbox-web/grid' {
|
|
1321
2452
|
* interface ColumnState {
|
|
1322
2453
|
* filter?: FilterValue;
|
|
1323
2454
|
* }
|
|
1324
2455
|
* }
|
|
1325
2456
|
* ```
|
|
2457
|
+
*
|
|
2458
|
+
* @see {@link GridColumnState} for the full state object
|
|
1326
2459
|
*/
|
|
1327
2460
|
export interface ColumnState {
|
|
1328
2461
|
/** Column field identifier */
|
|
@@ -1333,11 +2466,15 @@ export interface ColumnState {
|
|
|
1333
2466
|
width?: number;
|
|
1334
2467
|
/** Visibility state */
|
|
1335
2468
|
visible: boolean;
|
|
1336
|
-
/** Sort state (undefined = not sorted) */
|
|
2469
|
+
/** Sort state (undefined = not sorted). */
|
|
1337
2470
|
sort?: ColumnSortState;
|
|
1338
2471
|
}
|
|
1339
2472
|
/**
|
|
1340
|
-
* Sort state for a column
|
|
2473
|
+
* Sort state for a column.
|
|
2474
|
+
* Used within {@link ColumnState} to track sort direction and priority.
|
|
2475
|
+
*
|
|
2476
|
+
* @see {@link ColumnState} for column state persistence
|
|
2477
|
+
* @see {@link SortChangeDetail} for sort change events
|
|
1341
2478
|
*/
|
|
1342
2479
|
export interface ColumnSortState {
|
|
1343
2480
|
/** Sort direction */
|
|
@@ -1348,8 +2485,22 @@ export interface ColumnSortState {
|
|
|
1348
2485
|
/**
|
|
1349
2486
|
* Complete grid column state for persistence.
|
|
1350
2487
|
* Contains state for all columns, including plugin-contributed properties.
|
|
2488
|
+
*
|
|
2489
|
+
* @example
|
|
2490
|
+
* ```typescript
|
|
2491
|
+
* // Save state
|
|
2492
|
+
* const state = grid.getColumnState();
|
|
2493
|
+
* localStorage.setItem('grid-state', JSON.stringify(state));
|
|
2494
|
+
*
|
|
2495
|
+
* // Restore state
|
|
2496
|
+
* grid.columnState = JSON.parse(localStorage.getItem('grid-state'));
|
|
2497
|
+
* ```
|
|
2498
|
+
*
|
|
2499
|
+
* @see {@link ColumnState} for individual column state
|
|
2500
|
+
* @see {@link PublicGrid.getColumnState} for retrieving state
|
|
1351
2501
|
*/
|
|
1352
2502
|
export interface GridColumnState {
|
|
2503
|
+
/** Array of column states. */
|
|
1353
2504
|
columns: ColumnState[];
|
|
1354
2505
|
}
|
|
1355
2506
|
/**
|
|
@@ -1417,6 +2568,18 @@ export interface ChangedRowsResetDetail<TRow = unknown> {
|
|
|
1417
2568
|
* Detail for a cell click event.
|
|
1418
2569
|
* Provides full context about the clicked cell including row data.
|
|
1419
2570
|
*
|
|
2571
|
+
* @example
|
|
2572
|
+
* ```typescript
|
|
2573
|
+
* grid.addEventListener('cell-click', (e: CustomEvent<CellClickDetail>) => {
|
|
2574
|
+
* const { row, field, value, rowIndex, colIndex } = e.detail;
|
|
2575
|
+
* console.log(`Clicked ${field} = ${value} in row ${rowIndex}`);\n *
|
|
2576
|
+
* // Access the full row data
|
|
2577
|
+
* if (row.status === 'pending') {
|
|
2578
|
+
* showApprovalDialog(row);
|
|
2579
|
+
* }
|
|
2580
|
+
* });
|
|
2581
|
+
* ```
|
|
2582
|
+
*
|
|
1420
2583
|
* @category Events
|
|
1421
2584
|
*/
|
|
1422
2585
|
export interface CellClickDetail<TRow = unknown> {
|
|
@@ -1439,6 +2602,20 @@ export interface CellClickDetail<TRow = unknown> {
|
|
|
1439
2602
|
* Detail for a row click event.
|
|
1440
2603
|
* Provides context about the clicked row.
|
|
1441
2604
|
*
|
|
2605
|
+
* @example
|
|
2606
|
+
* ```typescript
|
|
2607
|
+
* grid.addEventListener('row-click', (e: CustomEvent<RowClickDetail>) => {
|
|
2608
|
+
* const { row, rowIndex, rowEl } = e.detail;
|
|
2609
|
+
* console.log(`Clicked row ${rowIndex}: ${row.name}`);
|
|
2610
|
+
*
|
|
2611
|
+
* // Highlight the row
|
|
2612
|
+
* rowEl.classList.add('selected');
|
|
2613
|
+
*
|
|
2614
|
+
* // Open detail panel
|
|
2615
|
+
* showDetailPanel(row);
|
|
2616
|
+
* });
|
|
2617
|
+
* ```
|
|
2618
|
+
*
|
|
1442
2619
|
* @category Events
|
|
1443
2620
|
*/
|
|
1444
2621
|
export interface RowClickDetail<TRow = unknown> {
|
|
@@ -1454,6 +2631,25 @@ export interface RowClickDetail<TRow = unknown> {
|
|
|
1454
2631
|
/**
|
|
1455
2632
|
* Detail for a sort change (direction 0 indicates cleared sort).
|
|
1456
2633
|
*
|
|
2634
|
+
* @example
|
|
2635
|
+
* ```typescript
|
|
2636
|
+
* grid.addEventListener('sort-change', (e: CustomEvent<SortChangeDetail>) => {
|
|
2637
|
+
* const { field, direction } = e.detail;
|
|
2638
|
+
*
|
|
2639
|
+
* if (direction === 0) {
|
|
2640
|
+
* console.log(`Sort cleared on ${field}`);
|
|
2641
|
+
* } else {
|
|
2642
|
+
* const dir = direction === 1 ? 'ascending' : 'descending';
|
|
2643
|
+
* console.log(`Sorted by ${field} ${dir}`);
|
|
2644
|
+
* }
|
|
2645
|
+
*
|
|
2646
|
+
* // Fetch sorted data from server
|
|
2647
|
+
* fetchData({ sortBy: field, sortDir: direction });
|
|
2648
|
+
* });
|
|
2649
|
+
* ```
|
|
2650
|
+
*
|
|
2651
|
+
* @see {@link SortState} for the sort state object
|
|
2652
|
+
* @see {@link SortHandler} for custom sort handlers
|
|
1457
2653
|
* @category Events
|
|
1458
2654
|
*/
|
|
1459
2655
|
export interface SortChangeDetail {
|
|
@@ -1465,6 +2661,19 @@ export interface SortChangeDetail {
|
|
|
1465
2661
|
/**
|
|
1466
2662
|
* Column resize event detail containing final pixel width.
|
|
1467
2663
|
*
|
|
2664
|
+
* @example
|
|
2665
|
+
* ```typescript
|
|
2666
|
+
* grid.addEventListener('column-resize', (e: CustomEvent<ColumnResizeDetail>) => {
|
|
2667
|
+
* const { field, width } = e.detail;
|
|
2668
|
+
* console.log(`Column ${field} resized to ${width}px`);
|
|
2669
|
+
*
|
|
2670
|
+
* // Persist to user preferences
|
|
2671
|
+
* saveColumnWidth(field, width);
|
|
2672
|
+
* });
|
|
2673
|
+
* ```
|
|
2674
|
+
*
|
|
2675
|
+
* @see {@link ColumnState} for persisting column state
|
|
2676
|
+
* @see {@link ResizeController} for resize implementation
|
|
1468
2677
|
* @category Events
|
|
1469
2678
|
*/
|
|
1470
2679
|
export interface ColumnResizeDetail {
|
|
@@ -1478,6 +2687,7 @@ export interface ColumnResizeDetail {
|
|
|
1478
2687
|
* - `'keyboard'`: Enter key pressed on focused cell
|
|
1479
2688
|
* - `'pointer'`: Mouse/touch/pen click on cell
|
|
1480
2689
|
*
|
|
2690
|
+
* @see {@link CellActivateDetail} for the activation event detail
|
|
1481
2691
|
* @category Events
|
|
1482
2692
|
*/
|
|
1483
2693
|
export type CellActivateTrigger = 'keyboard' | 'pointer';
|
|
@@ -1485,6 +2695,27 @@ export type CellActivateTrigger = 'keyboard' | 'pointer';
|
|
|
1485
2695
|
* Fired when a cell is activated by user interaction (Enter key or click).
|
|
1486
2696
|
* Unified event for both keyboard and pointer activation.
|
|
1487
2697
|
*
|
|
2698
|
+
* @example
|
|
2699
|
+
* ```typescript
|
|
2700
|
+
* grid.addEventListener('cell-activate', (e: CustomEvent<CellActivateDetail>) => {
|
|
2701
|
+
* const { row, field, value, trigger, cellEl } = e.detail;
|
|
2702
|
+
*
|
|
2703
|
+
* if (trigger === 'keyboard') {
|
|
2704
|
+
* console.log('Activated via Enter key');
|
|
2705
|
+
* } else {
|
|
2706
|
+
* console.log('Activated via click/tap');
|
|
2707
|
+
* }
|
|
2708
|
+
*
|
|
2709
|
+
* // Start custom editing for specific columns
|
|
2710
|
+
* if (field === 'notes') {
|
|
2711
|
+
* openNotesEditor(row, cellEl);
|
|
2712
|
+
* e.preventDefault(); // Prevent default editing
|
|
2713
|
+
* }
|
|
2714
|
+
* });
|
|
2715
|
+
* ```
|
|
2716
|
+
*
|
|
2717
|
+
* @see {@link CellClickDetail} for click-only events
|
|
2718
|
+
* @see {@link CellActivateTrigger} for trigger types
|
|
1488
2719
|
* @category Events
|
|
1489
2720
|
*/
|
|
1490
2721
|
export interface CellActivateDetail<TRow = unknown> {
|
|
@@ -1520,7 +2751,22 @@ export interface ActivateCellDetail {
|
|
|
1520
2751
|
/**
|
|
1521
2752
|
* Event detail for mounting external view renderers.
|
|
1522
2753
|
*
|
|
1523
|
-
*
|
|
2754
|
+
* Emitted when a cell uses an external component spec (React, Angular, Vue)
|
|
2755
|
+
* and needs the framework adapter to mount the component.
|
|
2756
|
+
*
|
|
2757
|
+
* @example
|
|
2758
|
+
* ```typescript
|
|
2759
|
+
* // Framework adapter listens for this event
|
|
2760
|
+
* grid.addEventListener('mount-external-view', (e: CustomEvent<ExternalMountViewDetail>) => {
|
|
2761
|
+
* const { placeholder, spec, context } = e.detail;
|
|
2762
|
+
* // Mount framework component into placeholder
|
|
2763
|
+
* mountComponent(spec.component, placeholder, context);
|
|
2764
|
+
* });
|
|
2765
|
+
* ```
|
|
2766
|
+
*
|
|
2767
|
+
* @see {@link ColumnConfig.externalView} for external view spec
|
|
2768
|
+
* @see {@link FrameworkAdapter} for adapter interface
|
|
2769
|
+
* @category Framework Adapters
|
|
1524
2770
|
*/
|
|
1525
2771
|
export interface ExternalMountViewDetail<TRow = unknown> {
|
|
1526
2772
|
placeholder: HTMLElement;
|
|
@@ -1535,7 +2781,26 @@ export interface ExternalMountViewDetail<TRow = unknown> {
|
|
|
1535
2781
|
/**
|
|
1536
2782
|
* Event detail for mounting external editor renderers.
|
|
1537
2783
|
*
|
|
1538
|
-
*
|
|
2784
|
+
* Emitted when a cell uses an external editor component spec and needs
|
|
2785
|
+
* the framework adapter to mount the editor with commit/cancel bindings.
|
|
2786
|
+
*
|
|
2787
|
+
* @example
|
|
2788
|
+
* ```typescript
|
|
2789
|
+
* // Framework adapter listens for this event
|
|
2790
|
+
* grid.addEventListener('mount-external-editor', (e: CustomEvent<ExternalMountEditorDetail>) => {
|
|
2791
|
+
* const { placeholder, spec, context } = e.detail;
|
|
2792
|
+
* // Mount framework editor with commit/cancel wired
|
|
2793
|
+
* mountEditor(spec.component, placeholder, {
|
|
2794
|
+
* value: context.value,
|
|
2795
|
+
* onCommit: context.commit,
|
|
2796
|
+
* onCancel: context.cancel,
|
|
2797
|
+
* });
|
|
2798
|
+
* });
|
|
2799
|
+
* ```
|
|
2800
|
+
*
|
|
2801
|
+
* @see {@link ColumnEditorSpec} for external editor spec
|
|
2802
|
+
* @see {@link FrameworkAdapter} for adapter interface
|
|
2803
|
+
* @category Framework Adapters
|
|
1539
2804
|
*/
|
|
1540
2805
|
export interface ExternalMountEditorDetail<TRow = unknown> {
|
|
1541
2806
|
placeholder: HTMLElement;
|
|
@@ -1552,6 +2817,26 @@ export interface ExternalMountEditorDetail<TRow = unknown> {
|
|
|
1552
2817
|
/**
|
|
1553
2818
|
* Maps event names to their detail payload types.
|
|
1554
2819
|
*
|
|
2820
|
+
* Use this interface for strongly typed event handling.
|
|
2821
|
+
*
|
|
2822
|
+
* @example
|
|
2823
|
+
* ```typescript
|
|
2824
|
+
* // Type-safe event listener
|
|
2825
|
+
* function handleEvent<K extends keyof DataGridEventMap>(
|
|
2826
|
+
* grid: DataGridElement,
|
|
2827
|
+
* event: K,
|
|
2828
|
+
* handler: (detail: DataGridEventMap[K]) => void,
|
|
2829
|
+
* ): void {
|
|
2830
|
+
* grid.addEventListener(event, (e: CustomEvent) => handler(e.detail));
|
|
2831
|
+
* }
|
|
2832
|
+
*
|
|
2833
|
+
* handleEvent(grid, 'cell-click', (detail) => {
|
|
2834
|
+
* console.log(detail.field); // Type-safe access
|
|
2835
|
+
* });
|
|
2836
|
+
* ```
|
|
2837
|
+
*
|
|
2838
|
+
* @see {@link DataGridCustomEvent} for typed CustomEvent wrapper
|
|
2839
|
+
* @see {@link DGEvents} for event name constants
|
|
1555
2840
|
* @category Events
|
|
1556
2841
|
*/
|
|
1557
2842
|
export interface DataGridEventMap<TRow = unknown> {
|
|
@@ -1573,12 +2858,45 @@ export interface DataGridEventMap<TRow = unknown> {
|
|
|
1573
2858
|
/**
|
|
1574
2859
|
* Extracts the event detail type for a given event name.
|
|
1575
2860
|
*
|
|
2861
|
+
* Utility type for getting the detail payload type of a specific event.
|
|
2862
|
+
*
|
|
2863
|
+
* @example
|
|
2864
|
+
* ```typescript
|
|
2865
|
+
* // Extract detail type for specific event
|
|
2866
|
+
* type ClickDetail = DataGridEventDetail<'cell-click', Employee>;
|
|
2867
|
+
* // Equivalent to: CellClickDetail<Employee>
|
|
2868
|
+
*
|
|
2869
|
+
* // Use in generic handler
|
|
2870
|
+
* function logDetail<K extends keyof DataGridEventMap>(
|
|
2871
|
+
* eventName: K,
|
|
2872
|
+
* detail: DataGridEventDetail<K>,
|
|
2873
|
+
* ): void {
|
|
2874
|
+
* console.log(`${eventName}:`, detail);
|
|
2875
|
+
* }
|
|
2876
|
+
* ```
|
|
2877
|
+
*
|
|
2878
|
+
* @see {@link DataGridEventMap} for all event types
|
|
1576
2879
|
* @category Events
|
|
1577
2880
|
*/
|
|
1578
2881
|
export type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];
|
|
1579
2882
|
/**
|
|
1580
2883
|
* Custom event type for DataGrid events with typed detail payload.
|
|
1581
2884
|
*
|
|
2885
|
+
* Use this type when you need to cast or declare event handler parameters.
|
|
2886
|
+
*
|
|
2887
|
+
* @example
|
|
2888
|
+
* ```typescript
|
|
2889
|
+
* // Strongly typed event handler
|
|
2890
|
+
* function onCellClick(e: DataGridCustomEvent<'cell-click', Employee>): void {
|
|
2891
|
+
* const { row, field, value } = e.detail;
|
|
2892
|
+
* console.log(`Clicked ${field} = ${value} on ${row.name}`);
|
|
2893
|
+
* }
|
|
2894
|
+
*
|
|
2895
|
+
* grid.addEventListener('cell-click', onCellClick);
|
|
2896
|
+
* ```
|
|
2897
|
+
*
|
|
2898
|
+
* @see {@link DataGridEventMap} for all event types
|
|
2899
|
+
* @see {@link DataGridEventDetail} for extracting detail type only
|
|
1582
2900
|
* @category Events
|
|
1583
2901
|
*/
|
|
1584
2902
|
export type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<DataGridEventMap<TRow>[K]>;
|