@keenmate/web-grid 1.0.5 → 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 +8 -5
- 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 +3621 -3225
- package/dist/web-grid.umd.js +119 -119
- package/package.json +1 -1
- package/src/css/_table.css +7 -1
- package/src/css/_tree.css +73 -0
- package/src/css/main.css +1 -0
package/README.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
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.1.0
|
|
6
|
+
|
|
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.
|
|
12
|
+
|
|
5
13
|
## What's New in v1.0.5
|
|
6
14
|
|
|
7
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.
|
|
@@ -10,11 +18,6 @@ A feature-rich, framework-agnostic data grid web component built with TypeScript
|
|
|
10
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-*`.
|
|
11
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.
|
|
12
20
|
|
|
13
|
-
## What's New in v1.0.4
|
|
14
|
-
|
|
15
|
-
- **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()`.
|
|
16
|
-
- **Dropdown positioning fix**: Fixed dropdown editors appearing offset in shadow DOM by switching from `position: fixed` to `position: absolute`.
|
|
17
|
-
|
|
18
21
|
## Installation
|
|
19
22
|
|
|
20
23
|
```bash
|
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';
|