@keenmate/web-grid 1.0.4 → 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.
package/README.md CHANGED
@@ -2,24 +2,21 @@
2
2
 
3
3
  A feature-rich, framework-agnostic data grid web component built with TypeScript. Sorting, filtering, pagination, inline editing (8 editor types), cell range selection, clipboard support, row toolbar, context menus, frozen columns, column reorder/resize, fill handle, virtual scroll, dark mode, and full CSS variable theming — all in a Shadow DOM encapsulated `<web-grid>` element.
4
4
 
5
- ## What's New in v1.0.4
5
+ ## What's New in v1.1.0
6
6
 
7
- - **Dirty cell/row indicator**: New `isDirtyIndicatorVisible` property (default: `true`). Edited cells show a subtle orange tint + corner triangle; row numbers get an orange left border. Themable via `--wg-dirty-*` variables. Public methods: `isCellDirty()`, `isRowDirty()`.
8
- - **Dropdown positioning fix**: Fixed dropdown editors appearing offset in shadow DOM by switching from `position: fixed` to `position: absolute`.
7
+ - **Tree / hierarchy mode**: Render tree-structured data using ltree-style path strings (`"1.2.3"`, `"/a/b/c"`, `"C:\\Win\\Sys"` — separator auto-detected). Mark one column with `isTree: true` to add depth-based indentation and an expand/collapse chevron. Sort is sibling-aware, filter auto-expands ancestors of matches, pagination operates on visible rows. New props: `treePathMember`, `treeLevelMember`, `treeParentMember`, `treeSeparator`, `treeDataSorted`, `expandedPaths`, `defaultExpandDepth`. Methods: `toggleExpandedPath`, `expandAll`, `collapseAll`, `getRowTreeInfo`. Event: `onexpandedpathschange`.
8
+ - **Custom chevron icons via `treeChevronCallback`**: Receives `{ expanded, hasChildren, row, level, path }`, returns HTML for the chevron's inner content. Result is cached per `(row, expanded, hasChildren)` so the callback fires at most a handful of times per row. Use it for file/folder icons, status indicators, anything per-row. Static fallbacks: `treeExpandedGlyph` / `treeCollapsedGlyph`.
9
+ - **Double-click to expand/collapse**: New `treeDoubleClickBehavior: 'none' | 'toggle'`. When set to `'toggle'`, double-clicking anywhere in the tree column expands/collapses the node — works reliably even when the cell DOM is re-rendered between clicks (uses `MouseEvent.detail`, not the unreliable native `dblclick`).
10
+ - **No more spurious `onrowchange` events**: Entering edit mode and exiting via arrow keys without typing no longer fires phantom "X → X" change events. `commitEdit` now only fires `onrowchange` when the value actually changed (or validation failed).
11
+ - **Pathological filler-cell width fix**: Removed `min-width: max-content` from `.wg__table` — it was redundant with `table-layout: fixed` and triggered intrinsic-size computation that ballooned the filler to hundreds of thousands of pixels in some browsers (Firefox especially) when cells contained absolutely-positioned editors.
9
12
 
10
- ### v1.0.3
13
+ ## What's New in v1.0.5
11
14
 
12
- - **`onrowfocus` fixes**: No longer fires during cell range selection (drag/shift+click). Mouse-triggered row focus now defers to click (mouseup) instead of mousedown; keyboard navigation still fires immediately.
13
- - **Cell selection visual fix**: Focused cell outline no longer persists during cell range drag.
14
- - **Toolbar fixes**: Tooltip hides when toolbar moves/closes. Selections cleared on toolbar action click. `triggerElement` in `ontoolbarclick` detail stays alive (no longer detached by synchronous re-render).
15
- - **Z-index layer system**: All z-index values now use CSS custom properties (`--wg-z-header`, `--wg-z-frozen`, etc.). Fixes cell selection bleeding through sticky header and frozen header stacking.
16
-
17
- ### v1.0.2
18
-
19
- - **Tooltip positioning in transformed containers**: Fixed tooltips appearing at grid's top-left when ancestor has CSS `transform`. Switched to `position: absolute` with `:host` as positioning context.
20
- - **HTML tooltips**: New `isTooltipHtml` column option for rich tooltip content.
21
- - **`ontoolbarclick` detail**: Now includes `event` (MouseEvent) and `triggerElement` (HTMLElement) for anchoring popovers to toolbar buttons inside shadow DOM.
22
- - **Tooltip show delay**: Reduced default from 400ms to 200ms.
15
+ - **Context menus flip at screen edges**: Both cell and header context menus now correctly flip to the opposite side when opened near a viewport edge (they were clipping before). Switched the root menu to `strategy: 'fixed'` + `position: fixed` so Floating UI's `flip`/`shift` run in viewport coordinates.
16
+ - **Header submenus are viewport-aware**: Header menu submenus (e.g. Column Visibility) now use Floating UI with `placement: 'right-start'` and `flip` fallbacks, replacing the old CSS-only `left: 100%` positioning. A short hide delay lets the cursor cross the gap between parent item and submenu.
17
+ - **Context menu closes on grid-internal scroll**: The menu now subscribes to both the `'window'` and `'container'` scroll sources. Previously, scrolling within the grid didn't close the menu because scroll events aren't composed across shadow DOM.
18
+ - **Context menu offset flips with placement**: `contextMenuXOffset`/`contextMenuYOffset` are now applied via Floating UI's `offset` middleware (`mainAxis` / `alignmentAxis`), so the gap between cursor and menu stays correct even when the menu flips to `*-end` or `top-*`.
19
+ - **Dropdown selected option readable in dark mode**: The selected option in select/combobox/autocomplete dropdowns no longer renders as pale blue against the dark surface. `--wg-accent-color-light` now falls back to a transparent `color-mix` that blends with the underlying surface in either theme.
23
20
 
24
21
  ## Installation
25
22
 
@@ -27,37 +24,220 @@ A feature-rich, framework-agnostic data grid web component built with TypeScript
27
24
  npm install @keenmate/web-grid
28
25
  ```
29
26
 
30
- ## Quick Start
27
+ Or via CDN (no bundler):
28
+
29
+ ```html
30
+ <script type="module" src="https://unpkg.com/@keenmate/web-grid"></script>
31
+ ```
32
+
33
+ ## Getting Started
34
+
35
+ ### Step 1 — Import the component
31
36
 
32
- ### ES Module (recommended)
37
+ Importing the package registers the `<web-grid>` custom element globally. You only need to import it once anywhere in your app.
38
+
39
+ ```javascript
40
+ import '@keenmate/web-grid'
41
+ ```
42
+
43
+ For vanilla HTML without a bundler:
33
44
 
34
45
  ```html
35
- <script type="module">
36
- import '@keenmate/web-grid'
37
- </script>
46
+ <script type="module" src="https://unpkg.com/@keenmate/web-grid"></script>
47
+ ```
48
+
49
+ ### Step 2 — Drop the element into your markup
38
50
 
39
- <web-grid id="grid"></web-grid>
51
+ ```html
52
+ <web-grid id="grid" style="max-height: 400px"></web-grid>
53
+ ```
54
+
55
+ `max-height` (or `height`) on the host is how you control sizing — see [Height Modes](#height-modes).
56
+
57
+ ### Step 3 — Assign data and columns
58
+
59
+ The grid reads its configuration from properties on the element, not attributes. Grab a reference and set what you need:
60
+
61
+ ```javascript
62
+ const grid = document.getElementById('grid')
63
+
64
+ grid.items = [
65
+ { id: 1, name: 'Alice', age: 28, department: 'Engineering' },
66
+ { id: 2, name: 'Bob', age: 34, department: 'Marketing' }
67
+ ]
68
+
69
+ grid.columns = [
70
+ { field: 'id', title: 'ID', width: '60px' },
71
+ { field: 'name', title: 'Name', width: '160px' },
72
+ { field: 'age', title: 'Age', width: '80px', horizontalAlign: 'right' },
73
+ { field: 'department', title: 'Department', width: '160px' }
74
+ ]
75
+ ```
40
76
 
41
- <script type="module">
42
- const grid = document.getElementById('grid')
43
- grid.items = [
44
- { id: 1, name: 'Alice', age: 28 },
45
- { id: 2, name: 'Bob', age: 34 }
46
- ]
47
- grid.columns = [
48
- { field: 'id', title: 'ID', width: '60px' },
49
- { field: 'name', title: 'Name' },
50
- { field: 'age', title: 'Age' }
51
- ]
52
- grid.sortMode = 'multi' // Enable multi-column sorting
53
- </script>
77
+ That's the minimum — a read-only grid with the headers, rows, and columns you defined.
78
+
79
+ ### Step 4 — Enable the features you need
80
+
81
+ Turn on whatever the page calls for. Everything is off by default:
82
+
83
+ ```javascript
84
+ grid.isStriped = true // alternating row backgrounds
85
+ grid.isHoverable = true // hover highlight
86
+ grid.isRowNumbersVisible = true // leftmost # column
87
+ grid.sortMode = 'multi' // click headers to sort, Ctrl+click to add
88
+ grid.isPageable = true
89
+ grid.pageSize = 25
90
+ grid.isEditable = true
91
+ grid.editTrigger = 'navigate' // Excel-like: type to edit the focused cell
92
+ ```
93
+
94
+ ### Essential Properties
95
+
96
+ | Property | Type | Purpose |
97
+ |----------|------|---------|
98
+ | `items` | `T[]` | Row data — the only required "content" property |
99
+ | `columns` | `Column<T>[]` | Column definitions with at least a `field` each |
100
+ | `isStriped` / `isHoverable` / `isRowNumbersVisible` | `boolean` | Cosmetic toggles |
101
+ | `sortMode` | `'none' \| 'single' \| 'multi'` | Column sort behavior |
102
+ | `isPageable` + `pageSize` | `boolean` + `number` | Pagination |
103
+ | `isEditable` + `editTrigger` | `boolean` + `EditTrigger` | Enable inline editing and how it starts |
104
+ | `isFilterable` | `boolean` | Per-column filter row under the headers |
105
+ | `mode` | `'read-only' \| 'excel' \| 'input-matrix'` | Presets that set several of the above together |
106
+
107
+ ## Common Configurations
108
+
109
+ Short recipes for common setups. Each sets only what's necessary on top of the Step 3 minimum.
110
+
111
+ ### Sortable grid
112
+
113
+ ```javascript
114
+ grid.sortMode = 'multi' // or 'single'
115
+ ```
116
+
117
+ ### Paginated grid with top + bottom pager
118
+
119
+ ```javascript
120
+ grid.isPageable = true
121
+ grid.pageSize = 25
122
+ grid.paginationPosition = 'top-center|bottom-center'
123
+ ```
124
+
125
+ ### Editable grid (Excel-like navigation)
126
+
127
+ ```javascript
128
+ grid.mode = 'excel' // isEditable + editTrigger='navigate' + cellSelectionMode='click'
129
+ ```
130
+
131
+ Or opt in manually for finer control:
132
+
133
+ ```javascript
134
+ grid.isEditable = true
135
+ grid.editTrigger = 'navigate'
54
136
  ```
55
137
 
56
- ### UMD (Script Tag)
138
+ ### Per-column editors
139
+
140
+ ```javascript
141
+ grid.columns = [
142
+ { field: 'name', title: 'Name', editor: 'text' },
143
+ { field: 'salary', title: 'Salary', editor: 'number', editorOptions: { min: 0, step: 1000 } },
144
+ { field: 'department', title: 'Dept', editor: 'select',
145
+ editorOptions: {
146
+ options: [
147
+ { value: 'eng', label: 'Engineering' },
148
+ { value: 'sal', label: 'Sales' }
149
+ ]
150
+ }
151
+ }
152
+ ]
153
+ ```
154
+
155
+ ### Height modes
156
+
157
+ The grid's shadow DOM uses `max-height: inherit`, so setting `max-height` on the host controls its internal scroll container:
57
158
 
58
159
  ```html
59
- <script src="https://unpkg.com/@keenmate/web-grid"></script>
60
- <web-grid id="grid"></web-grid>
160
+ <!-- Container: grid caps at 400px, scrolls internally -->
161
+ <web-grid style="max-height: 400px"></web-grid>
162
+
163
+ <!-- Full height: grid expands with content, page handles scrolling -->
164
+ <web-grid style="height: 100%"></web-grid>
165
+ ```
166
+
167
+ For full-height mode, also set `tableBorderOnly = true` so the grid doesn't capture wheel events:
168
+
169
+ ```javascript
170
+ grid.tableBorderOnly = true
171
+ ```
172
+
173
+ ### Header context menu with column hide + visibility submenu
174
+
175
+ ```javascript
176
+ grid.headerContextMenu = [
177
+ 'sortAsc', 'sortDesc', 'clearSort',
178
+ { dividerBefore: true },
179
+ 'hideColumn', // hide the right-clicked column
180
+ 'columnVisibility' // submenu with a toggle per column
181
+ ]
182
+ ```
183
+
184
+ ### Hide a column programmatically
185
+
186
+ ```javascript
187
+ grid.columns.find(c => c.field === 'email').isHidden = true
188
+ grid.columns = [...grid.columns] // reassign to trigger re-render
189
+ ```
190
+
191
+ ### Row toolbar
192
+
193
+ ```javascript
194
+ grid.isRowToolbarVisible = true
195
+ grid.rowToolbar = ['add', 'delete', 'duplicate', 'moveUp', 'moveDown']
196
+ ```
197
+
198
+ ### Master/detail — react to row focus
199
+
200
+ ```javascript
201
+ grid.onrowfocus = ({ row, rowIndex }) => {
202
+ showDetailsFor(row)
203
+ }
204
+ ```
205
+
206
+ ### Validate a cell before commit
207
+
208
+ ```javascript
209
+ grid.columns = [{
210
+ field: 'email',
211
+ title: 'Email',
212
+ editor: 'text',
213
+ beforeCommitCallback: ({ value }) => {
214
+ if (!/^[^@]+@[^@]+$/.test(String(value))) {
215
+ return { valid: false, message: 'Invalid email address' }
216
+ }
217
+ return { valid: true }
218
+ }
219
+ }]
220
+ ```
221
+
222
+ ### TypeScript
223
+
224
+ Full type definitions ship with the package. Import types from the root:
225
+
226
+ ```typescript
227
+ import '@keenmate/web-grid'
228
+ import type { Column, RowChangeDetail } from '@keenmate/web-grid'
229
+
230
+ interface Employee {
231
+ id: number
232
+ name: string
233
+ salary: number
234
+ }
235
+
236
+ const columns: Column<Employee>[] = [
237
+ { field: 'id', title: 'ID', width: '60px' },
238
+ { field: 'name', title: 'Name', width: '200px' },
239
+ { field: 'salary', title: 'Salary', editor: 'number' }
240
+ ]
61
241
  ```
62
242
 
63
243
  ## Features
package/dist/grid.d.ts CHANGED
@@ -1,4 +1,16 @@
1
- import type { Column, CellValidationState, RowToolbarConfig, ContextMenuItem, RowShortcut, RowChangeDetail, ToolbarClickDetail, RowActionClickDetail, ContextMenuContext, HeaderMenuConfig, HeaderMenuContext, EditTrigger, EditStartSelection, EditingCell, FocusedCell, SortMode, SortState, DataRequestDetail, DataRequestTrigger, BeforeCommitResult, GridMode, ToggleVisibility, PaginationLabelsCallback, SummaryContentCallback, ValidationTooltipContext, ToolbarPosition, GridLabels, RowLockInfo, RowLockingOptions, RowLockChangeDetail, ColumnWidthState, ColumnResizeDetail, ColumnOrderState, ColumnReorderDetail, FillDragDetail, FillDirection, RangeShortcut, CellSelectionMode, CellRange, CellSelectionChangeDetail, RowFocusDetail, PasteMode, BeforePasteDetail, PasteDetail, CreateRowCallback, NewRowPosition } from './types.js';
1
+ import type { Column, CellValidationState, RowToolbarConfig, ContextMenuItem, RowShortcut, RowChangeDetail, ToolbarClickDetail, RowActionClickDetail, ContextMenuContext, HeaderMenuConfig, HeaderMenuContext, EditTrigger, EditStartSelection, EditingCell, FocusedCell, SortMode, SortState, DataRequestDetail, DataRequestTrigger, BeforeCommitResult, GridMode, ToggleVisibility, PaginationLabelsCallback, SummaryContentCallback, ValidationTooltipContext, ToolbarPosition, GridLabels, RowLockInfo, RowLockingOptions, RowLockChangeDetail, ColumnWidthState, ColumnResizeDetail, ColumnOrderState, ColumnReorderDetail, FillDragDetail, FillDirection, RangeShortcut, CellSelectionMode, CellRange, CellSelectionChangeDetail, RowFocusDetail, PasteMode, BeforePasteDetail, PasteDetail, CreateRowCallback, NewRowPosition, TreeExpandedChangeDetail, TreeChevronCallback, TreeDoubleClickBehavior } from './types.js';
2
+ type TreeNode = {
3
+ path: string;
4
+ parent: string | null;
5
+ level: number;
6
+ rowIndex: number;
7
+ childPaths: string[];
8
+ };
9
+ type TreeIndex = {
10
+ separator: string;
11
+ nodes: Map<string, TreeNode>;
12
+ rootPaths: string[];
13
+ };
2
14
  /**
3
15
  * WebGrid - Core logic class for the data grid
4
16
  *
@@ -155,6 +167,21 @@ export declare class WebGrid<T = unknown> {
155
167
  protected _newRowIndicator: string;
156
168
  protected _createEmptyRowCallback: (() => T | Promise<T>) | undefined;
157
169
  protected _emptyRowDraft: T | null;
170
+ protected _treePathMember: string | null;
171
+ protected _treeLevelMember: string | null;
172
+ protected _treeParentMember: string | null;
173
+ protected _treeSeparator: string | null;
174
+ protected _treeDataSorted: boolean;
175
+ protected _expandedPaths: Set<string>;
176
+ protected _expandedPathsExternal: Set<string> | null;
177
+ protected _defaultExpandDepth: number | null;
178
+ protected _treeIndex: TreeIndex | null;
179
+ protected _onexpandedpathschange: ((detail: TreeExpandedChangeDetail) => void) | undefined;
180
+ protected _treeDoubleClickBehavior: TreeDoubleClickBehavior;
181
+ protected _treeExpandedGlyph: string;
182
+ protected _treeCollapsedGlyph: string;
183
+ protected _treeChevronCallback: TreeChevronCallback<T> | undefined;
184
+ protected _treeChevronCache: WeakMap<object, Map<string, string>>;
158
185
  get items(): T[];
159
186
  set items(value: T[]);
160
187
  get columns(): Column<T>[];
@@ -440,6 +467,64 @@ export declare class WebGrid<T = unknown> {
440
467
  set newRowIndicator(value: string);
441
468
  get createEmptyRowCallback(): (() => T | Promise<T>) | undefined;
442
469
  set createEmptyRowCallback(value: (() => T | Promise<T>) | undefined);
470
+ get treePathMember(): string | null;
471
+ set treePathMember(value: string | null);
472
+ get treeLevelMember(): string | null;
473
+ set treeLevelMember(value: string | null);
474
+ get treeParentMember(): string | null;
475
+ set treeParentMember(value: string | null);
476
+ get treeSeparator(): string | null;
477
+ set treeSeparator(value: string | null);
478
+ get treeDataSorted(): boolean;
479
+ set treeDataSorted(value: boolean);
480
+ get expandedPaths(): Set<string>;
481
+ set expandedPaths(value: Set<string> | null | undefined);
482
+ get defaultExpandDepth(): number | null;
483
+ set defaultExpandDepth(value: number | null);
484
+ get onexpandedpathschange(): ((detail: TreeExpandedChangeDetail) => void) | undefined;
485
+ set onexpandedpathschange(value: ((detail: TreeExpandedChangeDetail) => void) | undefined);
486
+ get isTreeMode(): boolean;
487
+ isPathExpanded(path: string): boolean;
488
+ toggleExpandedPath(path: string): void;
489
+ expandAll(): void;
490
+ collapseAll(): void;
491
+ /** Returns tree info for a row (used by rendering) */
492
+ getRowTreeInfo(item: T): {
493
+ path: string;
494
+ level: number;
495
+ hasChildren: boolean;
496
+ } | null;
497
+ get treeDoubleClickBehavior(): TreeDoubleClickBehavior;
498
+ set treeDoubleClickBehavior(value: TreeDoubleClickBehavior);
499
+ get treeExpandedGlyph(): string;
500
+ set treeExpandedGlyph(value: string);
501
+ get treeCollapsedGlyph(): string;
502
+ set treeCollapsedGlyph(value: string);
503
+ get treeChevronCallback(): TreeChevronCallback<T> | undefined;
504
+ set treeChevronCallback(value: TreeChevronCallback<T> | undefined);
505
+ /**
506
+ * Resolve the inner HTML for a row's chevron, using callback (cached) or static glyph.
507
+ * Cache is keyed per (row reference, expanded). When the row reference changes
508
+ * (immutable update) or items are replaced, stale entries are dropped via WeakMap GC
509
+ * or explicit cache clear.
510
+ */
511
+ getTreeChevronHtml(item: T, expanded: boolean, hasChildren: boolean, level: number, path: string): string;
512
+ /** Read the path field from a row */
513
+ protected getRowTreePath(item: T): string | null;
514
+ /** Detect separator from a sample path */
515
+ protected detectTreeSeparator(path: string): string;
516
+ /** Rebuild _treeIndex from current items */
517
+ protected rebuildTreeState(): void;
518
+ /** Re-initialize the active expanded set after items/config changes */
519
+ protected rebuildExpandedPaths(): void;
520
+ /** Compare two tree nodes using current sort state, falling back to path */
521
+ protected compareTreeNodes(a: TreeNode, b: TreeNode): number;
522
+ /** Compute paths matched by current text filters, plus their ancestors */
523
+ protected getTreeFilterAllowedPaths(): Set<string> | null;
524
+ /** Items in tree-aware sort order (depth-first, sibling-sorted), with filter applied */
525
+ protected getTreeSortedItems(): T[];
526
+ /** Items currently visible (post-collapse). When filter is active, ancestors of matches are auto-expanded. */
527
+ protected getTreeVisibleItems(): T[];
443
528
  selectCellRange(range: CellRange): void;
444
529
  clearCellSelection(): void;
445
530
  /** Clear cell selection state without triggering re-render (caller handles visuals) */
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { GridElement } from './web-component.js';
2
2
  export { WebGrid } from './grid.js';
3
- export type { EditorType, EditTrigger, OptionsLoadTrigger, DateOutputFormat, EditorOption, EditorOptions, CustomEditorContext, CellValidationState, ValidationResult, BeforeCommitContext, BeforeCommitResult, Column, CellRenderCallback, RowChangeDetail, RowFocusDetail, PredefinedToolbarItemType, ToolbarPosition, NewRowPosition, ToolbarTooltip, RowToolbarItem, RowToolbarConfig, NormalizedToolbarItem, ToolbarClickDetail, RowActionType, RowActionClickDetail, ContextMenuContext, ContextMenuItem, QuickGridProps, SortState, DataRequestDetail, DataRequestTrigger, GridLabels, RowLockInfo, RowLockingOptions, RowLockChangeDetail, LockedRowEditBehavior, ColumnWidthState, ColumnResizeDetail, ColumnOrderState, ColumnReorderDetail, FillDragDetail, FillDirection, RangeShortcut, RangeShortcutContext, CellSelectionMode, CellRange, CellSelectionChangeDetail, PasteMode, PasteColumnMapping, BeforePasteDetail, PasteCellResult, PasteDetail, CreateRowCallback, EditingCell, FocusedCell, SortDirection, ToolbarRowGroup, PopupPosition, ConnectorArrowDir } from './types.js';
3
+ export type { EditorType, EditTrigger, OptionsLoadTrigger, DateOutputFormat, EditorOption, EditorOptions, CustomEditorContext, CellValidationState, ValidationResult, BeforeCommitContext, BeforeCommitResult, Column, CellRenderCallback, RowChangeDetail, RowFocusDetail, PredefinedToolbarItemType, ToolbarPosition, NewRowPosition, ToolbarTooltip, RowToolbarItem, RowToolbarConfig, NormalizedToolbarItem, ToolbarClickDetail, RowActionType, RowActionClickDetail, ContextMenuContext, ContextMenuItem, QuickGridProps, SortState, DataRequestDetail, DataRequestTrigger, GridLabels, RowLockInfo, RowLockingOptions, RowLockChangeDetail, LockedRowEditBehavior, ColumnWidthState, ColumnResizeDetail, ColumnOrderState, ColumnReorderDetail, FillDragDetail, FillDirection, RangeShortcut, RangeShortcutContext, CellSelectionMode, CellRange, CellSelectionChangeDetail, PasteMode, PasteColumnMapping, BeforePasteDetail, PasteCellResult, PasteDetail, CreateRowCallback, TreeExpandedChangeDetail, TreeChevronContext, TreeChevronCallback, TreeDoubleClickBehavior, EditingCell, FocusedCell, SortDirection, ToolbarRowGroup, PopupPosition, ConnectorArrowDir } from './types.js';
4
4
  export { GridElement as default } from './web-component.js';
@@ -0,0 +1,10 @@
1
+ import type { Column } from '../../types.js';
2
+ import type { GridContext } from '../types.js';
3
+ /**
4
+ * Wrap cell content with tree indent + chevron when column is the tree column.
5
+ * Returns the original innerHtml unchanged when tree mode is off or column isn't isTree.
6
+ *
7
+ * Used by both the full-table render (table.ts) and the surgical single-cell
8
+ * render (cell.ts), so both paths keep the chevron after focus/edit transitions.
9
+ */
10
+ export declare function wrapTreeCell<T>(ctx: GridContext<T>, column: Column<T>, item: T, innerHtml: string): string;
package/dist/types.d.ts CHANGED
@@ -134,6 +134,7 @@ export type Column<T> = {
134
134
  isMovable?: boolean;
135
135
  fillDirection?: FillDirection;
136
136
  isHidden?: boolean;
137
+ isTree?: boolean;
137
138
  };
138
139
  export type ValidationTooltipContext<T> = {
139
140
  field: string;
@@ -401,6 +402,18 @@ export type QuickGridProps<T> = {
401
402
  cellSelectionMode?: CellSelectionMode;
402
403
  shouldCopyWithHeaders?: boolean;
403
404
  oncellselectionchange?: (detail: CellSelectionChangeDetail) => void;
405
+ treePathMember?: keyof T | string;
406
+ treeLevelMember?: keyof T | string;
407
+ treeParentMember?: keyof T | string;
408
+ treeSeparator?: string;
409
+ treeDataSorted?: boolean;
410
+ expandedPaths?: Set<string>;
411
+ defaultExpandDepth?: number;
412
+ treeDoubleClickBehavior?: TreeDoubleClickBehavior;
413
+ onexpandedpathschange?: (detail: TreeExpandedChangeDetail) => void;
414
+ treeExpandedGlyph?: string;
415
+ treeCollapsedGlyph?: string;
416
+ treeChevronCallback?: TreeChevronCallback<T>;
404
417
  isNewRowEnabled?: boolean;
405
418
  newRowPosition?: NewRowPosition;
406
419
  newRowIndicator?: string;
@@ -595,3 +608,25 @@ export type PasteDetail<T> = {
595
608
  };
596
609
  /** Callback to create new rows from pasted data */
597
610
  export type CreateRowCallback<T> = (pastedData: Record<string, unknown>, rowIndex: number) => T;
611
+ /** What happens when the user double-clicks a tree-column cell */
612
+ export type TreeDoubleClickBehavior = 'none' | 'toggle';
613
+ /** Detail passed to onexpandedpathschange when user toggles a tree node */
614
+ export type TreeExpandedChangeDetail = {
615
+ path: string;
616
+ expanded: boolean;
617
+ expandedPaths: Set<string>;
618
+ };
619
+ /** Context passed to treeChevronCallback */
620
+ export type TreeChevronContext<T> = {
621
+ expanded: boolean;
622
+ hasChildren: boolean;
623
+ row: T;
624
+ level: number;
625
+ path: string;
626
+ };
627
+ /**
628
+ * Callback that returns HTML for the chevron's inner content.
629
+ * Result is cached per (row, expanded) — the callback is invoked at most twice
630
+ * per row, once per expanded state. Invalidated when items or the callback change.
631
+ */
632
+ export type TreeChevronCallback<T> = (context: TreeChevronContext<T>) => string;
@@ -196,6 +196,34 @@ export declare class GridElement<T = unknown> extends HTMLElement implements Gri
196
196
  set newRowIndicator(value: string);
197
197
  get createEmptyRowCallback(): () => T | Promise<T>;
198
198
  set createEmptyRowCallback(value: () => T | Promise<T>);
199
+ get treePathMember(): string | null;
200
+ set treePathMember(value: string | null);
201
+ get treeLevelMember(): string | null;
202
+ set treeLevelMember(value: string | null);
203
+ get treeParentMember(): string | null;
204
+ set treeParentMember(value: string | null);
205
+ get treeSeparator(): string | null;
206
+ set treeSeparator(value: string | null);
207
+ get treeDataSorted(): boolean;
208
+ set treeDataSorted(value: boolean);
209
+ get expandedPaths(): Set<string>;
210
+ set expandedPaths(value: Set<string> | null | undefined);
211
+ get defaultExpandDepth(): number | null;
212
+ set defaultExpandDepth(value: number | null);
213
+ set onexpandedpathschange(value: ((detail: import('./types.js').TreeExpandedChangeDetail) => void) | undefined);
214
+ get onexpandedpathschange(): ((detail: import("./types.js").TreeExpandedChangeDetail) => void) | undefined;
215
+ isPathExpanded(path: string): boolean;
216
+ toggleExpandedPath(path: string): void;
217
+ expandAll(): void;
218
+ collapseAll(): void;
219
+ get treeDoubleClickBehavior(): import('./types.js').TreeDoubleClickBehavior;
220
+ set treeDoubleClickBehavior(value: import('./types.js').TreeDoubleClickBehavior);
221
+ get treeExpandedGlyph(): string;
222
+ set treeExpandedGlyph(value: string);
223
+ get treeCollapsedGlyph(): string;
224
+ set treeCollapsedGlyph(value: string);
225
+ get treeChevronCallback(): import('./types.js').TreeChevronCallback<T> | undefined;
226
+ set treeChevronCallback(value: import('./types.js').TreeChevronCallback<T> | undefined);
199
227
  get isShortcutsHelpVisible(): boolean;
200
228
  set isShortcutsHelpVisible(value: boolean);
201
229
  get shortcutsHelpPosition(): 'top-right' | 'top-left';