@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 +217 -37
- package/dist/grid.d.ts +86 -1
- package/dist/index.d.ts +1 -1
- package/dist/modules/rendering/tree-render.d.ts +10 -0
- package/dist/types.d.ts +35 -0
- package/dist/web-component.d.ts +28 -0
- package/dist/web-grid.js +3752 -3327
- package/dist/web-grid.umd.js +126 -133
- package/package.json +1 -1
- package/src/css/_table.css +7 -1
- package/src/css/_tree.css +73 -0
- package/src/css/_variables.css +1 -1
- package/src/css/main.css +1 -0
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
|
|
5
|
+
## What's New in v1.1.0
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
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
|
-
|
|
13
|
+
## What's New in v1.0.5
|
|
11
14
|
|
|
12
|
-
-
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
###
|
|
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
|
-
|
|
60
|
-
<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;
|
package/dist/web-component.d.ts
CHANGED
|
@@ -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';
|