@keenmate/web-grid 1.1.0 → 1.2.0-rc02
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 +20 -15
- package/ai/fill-handle.txt +1 -1
- package/ai/frozen-columns.txt +2 -2
- package/ai/row-locking.txt +1 -1
- package/ai/styling-theming.txt +13 -12
- package/component-variables.manifest.json +8 -7
- package/dist/grid.d.ts +98 -11
- package/dist/logger.d.ts +1 -0
- package/dist/modules/click-events/index.d.ts +5 -1
- package/dist/modules/contextmenu/index.d.ts +10 -1
- package/dist/modules/datepicker/datepicker.d.ts +2 -0
- package/dist/modules/rendering/tree-render.d.ts +8 -0
- package/dist/perf.d.ts +15 -0
- package/dist/types.d.ts +15 -4
- package/dist/web-component.d.ts +31 -3
- package/dist/web-grid.js +3009 -2411
- package/dist/web-grid.umd.js +109 -114
- package/package.json +9 -3
- package/src/css/animations.css +14 -0
- package/src/css/{_cells.css → cells.css} +1 -1
- package/src/css/controls.css +3 -0
- package/src/css/dark-mode.css +67 -0
- package/src/css/{_dialogs.css → dialogs.css} +9 -68
- package/src/css/{_dirty-indicator.css → dirty-indicator.css} +37 -37
- package/src/css/{_dropdown.css → dropdown.css} +1 -1
- package/src/css/{_editors.css → editors.css} +14 -8
- package/src/css/floating.css +71 -0
- package/src/css/{_freeze.css → freeze.css} +5 -5
- package/src/css/{_header.css → header.css} +1 -1
- package/src/css/main.css +53 -48
- package/src/css/{_navigation.css → navigation.css} +10 -5
- package/src/css/{_resize.css → resize.css} +1 -1
- package/src/css/{_selection.css → selection.css} +1 -1
- package/src/css/{_shortcuts.css → shortcuts.css} +4 -4
- package/src/css/{_modifiers.css → states.css} +8 -2
- package/src/css/tree.css +71 -0
- package/src/css/{_variables.css → variables.css} +55 -42
- package/src/css/_dark-mode.css +0 -93
- package/src/css/_tree.css +0 -73
- /package/src/css/{_cell-selection.css → cell-selection.css} +0 -0
- /package/src/css/{_fill-handle.css → fill-handle.css} +0 -0
- /package/src/css/{_pagination.css → pagination.css} +0 -0
- /package/src/css/{_reorder.css → reorder.css} +0 -0
- /package/src/css/{_row-locking.css → row-locking.css} +0 -0
- /package/src/css/{_table.css → table.css} +0 -0
- /package/src/css/{_toolbar.css → toolbar.css} +0 -0
- /package/src/css/{_virtual-scroll.css → virtual-scroll.css} +0 -0
package/README.md
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
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.
|
|
5
|
+
## What's New in v1.2.0-rc02
|
|
6
6
|
|
|
7
|
-
- **
|
|
8
|
-
- **
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
11
|
-
- **
|
|
7
|
+
- **Excel-like commit-on-click-away** — new `shouldCommitOnClickAway` property (defaults to `true`). Clicking a different cell while editing now commits the in-progress value through `beforeCommitCallback` / `validateCallback` instead of silently discarding it. Set to `false` to keep the legacy discard behavior. Companion fix: row focus (`.wg__row--focused`) now applies on the first cell click — previously it only landed on the second.
|
|
8
|
+
- **CSS surface taxonomy aligned with pure-admin's `--base-*` names** — `--wg-surface-1/2/3` renamed to `--wg-main-bg` / `--wg-elevated-bg`, `--wg-surface-floating` → `--wg-dropdown-bg` (`--wg-surface-3` merged into `--wg-hover-bg`). Consumer overrides of the old names need to migrate; see the CHANGELOG mapping table.
|
|
9
|
+
- **Dark mode reworked to respect consumer themes** — the dark-mode override blocks used to set `--wg-*` to hardcoded literals, silently overriding pure-admin / theme-designer values. They now just flip `color-scheme` and let `light-dark()` in `variables.css` do the work. The consumer's `--base-*` chain stays the source of truth.
|
|
10
|
+
- **OS-aware dark mode via `light-dark()` fallbacks** — every hardcoded fallback in `variables.css` is wrapped in `light-dark(<light>, <dark>)`. A consumer who only sets `body { color-scheme: dark }` (no overrides, no framework class) now gets a sensible dark palette automatically.
|
|
11
|
+
- **CSS architecture refactor: `@layer` cascade + canonical file set** — `main.css` now declares `@layer variables, component, overrides;` so consumer rules beat library defaults without `!important`. CSS filenames dropped the underscore prefix (pure-CSS doesn't use SASS-partial convention). New canonical Tier-2 files (`controls.css` / `floating.css` / `states.css` / `animations.css`) match the cross-component pattern.
|
|
12
|
+
- **`--base-active-bg` + `--base-inverse-bg`** — two new cross-component theming hooks. `--base-active-bg` distinguishes pressed from hover; `--base-inverse-bg` covers tooltip and future inverse UI surfaces. Both are optional and additive.
|
|
13
|
+
- **Context menu was leaking through stale `--base-*` names** — was referencing `--base-layer-01` / `--base-stroke-default` / `--base-error-color` (all long-removed in pure-admin). Now uses the canonical names; consumers loading a pure-admin theme finally get themed context menus instead of silent fallbacks.
|
|
14
|
+
- **WCAG contrast e2e suite** — 18 assertions check header / body / hover / focused / selected-row / editing-cell contrast against the 3:1 threshold (WCAG AA for non-text UI). Catches theme regressions invisible to visual smoke tests.
|
|
12
15
|
|
|
13
|
-
## What's New in v1.0
|
|
16
|
+
## What's New in v1.2.0-rc01
|
|
14
17
|
|
|
15
|
-
- **
|
|
16
|
-
- **
|
|
17
|
-
- **
|
|
18
|
-
- **
|
|
19
|
-
- **
|
|
18
|
+
- **Tree mode polish**: The column marked `isTree: true` is now read-only by default (editing it conflicts with the toggle); Tab / Shift-Tab in heterogeneous trees skip rows where every cell is read-only, and the next editable cell auto-opens its editor after a Tab commit; `'expand-all'`, `'collapse-all'`, `'expand-tree'`, `'collapse-tree'` are now usable as predefined context-menu items via string shorthand or `{ type: ... }` object form. Tree chevron is a single rotating glyph (120 ms transition) on an inline-flex layout. CSS class `wg__tree-cell-content` renamed to `wg__tree-cell-body`.
|
|
19
|
+
- **Tree-row expand is ~40× faster**: Memoized the data pipeline (`displayItems` / `paginatedItems`) and added a surgical render path that replaces only inner contents instead of rebuilding the table. Expanding a 700-row tree drops from ~700 ms to ~16 ms. Render timings surface via the new `GRID:PERF` logger category.
|
|
20
|
+
- **Paste API is first-class on `<web-grid>`**: `onpaste`, `onbeforepaste`, `pasteMode`, and `shouldValidateOnPaste` are now reachable directly on the GridElement instead of requiring `el.grid.*`. `onpaste` shadows the native HTMLElement property and delivers a `PasteDetail`, not a raw `ClipboardEvent`. TypeScript consumers should cast on assignment or use `el.grid.onpaste = fn` for full type fidelity; JS consumers have no friction.
|
|
21
|
+
- **Datepicker no longer closes when you click its own chrome**: previously, clicking the chevrons / month/year header / disabled days appeared as "outside the grid" to two internal listeners and silently tore the picker down. Now both listeners recognize our `document.body`-hosted popovers (datepicker + cell/header context menus) as part of the grid's logical scope. Same fix protects the cell and header context menus from the same class of bug.
|
|
22
|
+
- **Datepicker `outputFormat` is honored from the action pipeline**: a column declaring `editorOptions.outputFormat: 'timestamp'` (or `'date'`) now actually emits a number (or `Date`) instead of an ISO string when commit goes through the modern pipeline executor (`editTrigger='always'` and keyboard-driven select).
|
|
20
23
|
|
|
21
24
|
## Installation
|
|
22
25
|
|
|
@@ -930,7 +933,7 @@ web-grid {
|
|
|
930
933
|
/* Or set base variables for all KeenMate components */
|
|
931
934
|
:root {
|
|
932
935
|
--base-accent-color: #10b981;
|
|
933
|
-
--base-
|
|
936
|
+
--base-main-bg: #ffffff;
|
|
934
937
|
}
|
|
935
938
|
```
|
|
936
939
|
|
|
@@ -953,8 +956,10 @@ This means:
|
|
|
953
956
|
|----------|-------------|
|
|
954
957
|
| `--wg-accent-color` | Primary accent color |
|
|
955
958
|
| `--wg-text-color-1` | Primary text color |
|
|
956
|
-
| `--wg-
|
|
957
|
-
| `--wg-
|
|
959
|
+
| `--wg-main-bg` | Background color (table body, cells) |
|
|
960
|
+
| `--wg-elevated-bg` | Alternate row/header background |
|
|
961
|
+
| `--wg-hover-bg` | Hover background (rows, buttons) |
|
|
962
|
+
| `--wg-dropdown-bg` | Floating surfaces (toolbar, context menu, dropdowns) |
|
|
958
963
|
| `--wg-border-color` | Border color |
|
|
959
964
|
|
|
960
965
|
### Component Variables Manifest
|
package/ai/fill-handle.txt
CHANGED
|
@@ -113,7 +113,7 @@ prevents accidental fills from clicks). During the drag:
|
|
|
113
113
|
|
|
114
114
|
CSS variables for the fill handle visual:
|
|
115
115
|
--wg-fill-handle-size Default: 8px
|
|
116
|
-
--wg-fill-handle-bg Default: var(--wg-
|
|
116
|
+
--wg-fill-handle-bg Default: var(--wg-main-bg)
|
|
117
117
|
--wg-fill-handle-border-color Default: var(--wg-accent-color)
|
|
118
118
|
--wg-fill-handle-border-width Default: 2px
|
|
119
119
|
--wg-fill-range-bg Default: color-mix(accent 15%, transparent)
|
package/ai/frozen-columns.txt
CHANGED
|
@@ -74,7 +74,7 @@ color against the opaque frozen column base color instead of transparent:
|
|
|
74
74
|
|
|
75
75
|
.wg__row--selected > .wg__cell--frozen {
|
|
76
76
|
background: color-mix(in srgb, var(--wg-accent-color) 15%,
|
|
77
|
-
var(--wg-frozen-column-bg, var(--wg-
|
|
77
|
+
var(--wg-frozen-column-bg, var(--wg-main-bg))) !important;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
|
|
@@ -105,7 +105,7 @@ The shadow only appears when the wg--scrolled-horizontal class is present
|
|
|
105
105
|
on the container (set when scrollLeft > 0).
|
|
106
106
|
|
|
107
107
|
Other frozen column CSS variables:
|
|
108
|
-
--wg-frozen-column-bg Default: var(--wg-
|
|
108
|
+
--wg-frozen-column-bg Default: var(--wg-main-bg)
|
|
109
109
|
--wg-frozen-header-bg Default: color-mix(in srgb, accent 8%, header-bg)
|
|
110
110
|
--wg-frozen-column-border Default: 2px solid var(--wg-border-color)
|
|
111
111
|
--wg-cell-splitter-color Border color for frozen column separator
|
package/ai/row-locking.txt
CHANGED
|
@@ -148,7 +148,7 @@ Locked rows receive the following visual treatment:
|
|
|
148
148
|
- Lock icon has cursor: help and full opacity (overrides row opacity)
|
|
149
149
|
|
|
150
150
|
CSS variables:
|
|
151
|
-
--wg-row-locked-bg Default: var(--base-disabled-bg, var(--wg-
|
|
151
|
+
--wg-row-locked-bg Default: var(--base-disabled-bg, var(--wg-elevated-bg))
|
|
152
152
|
--wg-row-locked-opacity Default: 0.7
|
|
153
153
|
|
|
154
154
|
|
package/ai/styling-theming.txt
CHANGED
|
@@ -9,7 +9,7 @@ Each --wg-* variable falls back to a --base-* variable, then to a hardcoded defa
|
|
|
9
9
|
|
|
10
10
|
:host {
|
|
11
11
|
--wg-accent-color: var(--base-accent-color, #0078d4);
|
|
12
|
-
--wg-
|
|
12
|
+
--wg-main-bg: var(--base-main-bg, #ffffff);
|
|
13
13
|
--wg-font-size-base: calc(var(--base-font-size-sm, 1.4) * var(--wg-rem));
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -23,7 +23,7 @@ Example -- theme all KeenMate components at once:
|
|
|
23
23
|
|
|
24
24
|
:root {
|
|
25
25
|
--base-accent-color: #10b981;
|
|
26
|
-
--base-
|
|
26
|
+
--base-main-bg: #ffffff;
|
|
27
27
|
--base-font-family: 'Inter', sans-serif;
|
|
28
28
|
}
|
|
29
29
|
|
|
@@ -54,16 +54,17 @@ Colors:
|
|
|
54
54
|
Falls back to: --base-text-color-1, default: #242424
|
|
55
55
|
--wg-text-color-2 Secondary text (row numbers, labels)
|
|
56
56
|
--wg-text-color-3 Muted text (placeholders, empty states)
|
|
57
|
-
--wg-
|
|
57
|
+
--wg-main-bg Main background (table body, cells)
|
|
58
58
|
Falls back to: --base-main-bg, default: #ffffff
|
|
59
|
-
--wg-
|
|
59
|
+
--wg-elevated-bg Elevated background (headers, striped rows)
|
|
60
60
|
Falls back to: --base-elevated-bg, default: #f5f5f5
|
|
61
|
-
--wg-
|
|
61
|
+
--wg-hover-bg Hover background (row hover, button hover)
|
|
62
|
+
Falls back to: --base-hover-bg, default: color-mix derived
|
|
62
63
|
--wg-border-color All borders (table, cells, header separators)
|
|
63
64
|
Falls back to: --base-border-color, default: #e0e0e0
|
|
64
65
|
|
|
65
66
|
Header:
|
|
66
|
-
--wg-header-bg Header row background (defaults to --wg-
|
|
67
|
+
--wg-header-bg Header row background (defaults to --wg-elevated-bg)
|
|
67
68
|
--wg-header-bg-hover Sortable header on hover
|
|
68
69
|
--wg-header-color Header text color
|
|
69
70
|
--wg-header-border Bottom border below header (2px solid)
|
|
@@ -71,8 +72,8 @@ Header:
|
|
|
71
72
|
--wg-header-font-weight Header text weight (semibold)
|
|
72
73
|
|
|
73
74
|
Rows:
|
|
74
|
-
--wg-row-bg-hover Row hover when isHoverable=true (defaults to --wg-
|
|
75
|
-
--wg-row-bg-even Striped even rows when isStriped=true (defaults to --wg-
|
|
75
|
+
--wg-row-bg-hover Row hover when isHoverable=true (defaults to --wg-hover-bg)
|
|
76
|
+
--wg-row-bg-even Striped even rows when isStriped=true (defaults to --wg-elevated-bg)
|
|
76
77
|
--wg-row-border Border between rows
|
|
77
78
|
|
|
78
79
|
Typography:
|
|
@@ -104,15 +105,15 @@ Dark mode activates automatically. No configuration needed. Detection methods:
|
|
|
104
105
|
|
|
105
106
|
All four methods apply the same dark palette overrides:
|
|
106
107
|
|
|
107
|
-
--wg-
|
|
108
|
-
--wg-
|
|
109
|
-
--wg-
|
|
108
|
+
--wg-main-bg: #1f1f1f
|
|
109
|
+
--wg-elevated-bg: #2b2b2b
|
|
110
|
+
--wg-hover-bg: #3a3a3a
|
|
110
111
|
--wg-text-color-1: #e0e0e0
|
|
111
112
|
--wg-text-color-2: #c0c0c0
|
|
112
113
|
--wg-text-color-3: #a0a0a0
|
|
113
114
|
--wg-border-color: #3d3d3d
|
|
114
115
|
--wg-input-bg: #1f1f1f
|
|
115
|
-
--wg-
|
|
116
|
+
--wg-active-bg: #4a4a4a
|
|
116
117
|
--wg-danger-color: #f87c86
|
|
117
118
|
--wg-danger-bg-light: #442726
|
|
118
119
|
--wg-dirty-indicator-color: #ffa940
|
|
@@ -13,10 +13,12 @@
|
|
|
13
13
|
{ "name": "base-text-color-on-accent", "required": false, "usage": "Text color on accent-colored backgrounds" },
|
|
14
14
|
{ "name": "base-text-inverted", "required": false, "usage": "Text on dark backgrounds (selected row numbers)" },
|
|
15
15
|
{ "name": "base-main-bg", "required": true, "usage": "Table body background, cell backgrounds, input fields" },
|
|
16
|
-
{ "name": "base-elevated-bg", "required": true, "usage": "Header row, striped even rows, pagination bar" },
|
|
17
|
-
{ "name": "base-hover-bg", "required": false, "usage": "Row hover, sorted column header, button hover
|
|
16
|
+
{ "name": "base-elevated-bg", "required": true, "usage": "Header row, striped even rows, pagination bar; also secondary fallback for base-dropdown-bg" },
|
|
17
|
+
{ "name": "base-hover-bg", "required": false, "usage": "Row hover, sorted column header, button hover background (primary lookup; falls back to text/main color-mix)" },
|
|
18
|
+
{ "name": "base-active-bg", "required": false, "usage": "Pressed/active button background, selected option background (primary lookup; falls back to text/main color-mix)" },
|
|
18
19
|
{ "name": "base-disabled-bg", "required": false, "usage": "Read-only and disabled cell backgrounds, locked row backgrounds" },
|
|
19
|
-
{ "name": "base-dropdown-bg", "required": false, "usage": "Floating toolbar, context menu, dropdown panels" },
|
|
20
|
+
{ "name": "base-dropdown-bg", "required": false, "usage": "Floating toolbar, context menu, dropdown panels (primary lookup; chains through base-elevated-bg)" },
|
|
21
|
+
{ "name": "base-inverse-bg", "required": false, "usage": "Secondary fallback for tooltip background (inverse-colored surface — dark in light mode, light in dark mode)" },
|
|
20
22
|
{ "name": "base-border-color", "required": true, "usage": "Table border, cell borders, header separators" },
|
|
21
23
|
{ "name": "base-input-bg", "required": false, "usage": "Filter input fields, editor text inputs" },
|
|
22
24
|
{ "name": "base-input-color", "required": false, "usage": "Text color in filter and editor inputs" },
|
|
@@ -54,10 +56,9 @@
|
|
|
54
56
|
{ "name": "wg-text-color-3", "category": "text", "usage": "Placeholder text, disabled text, empty state message, date trigger" },
|
|
55
57
|
{ "name": "wg-text-on-accent", "category": "text", "usage": "Text on accent backgrounds (Go button, active pagination)" },
|
|
56
58
|
|
|
57
|
-
{ "name": "wg-
|
|
58
|
-
{ "name": "wg-
|
|
59
|
-
{ "name": "wg-
|
|
60
|
-
{ "name": "wg-surface-floating", "category": "surface", "usage": "Floating toolbar background, context menu, dropdown panels" },
|
|
59
|
+
{ "name": "wg-main-bg", "category": "surface", "usage": "Table body, cell backgrounds, filter input background" },
|
|
60
|
+
{ "name": "wg-elevated-bg", "category": "surface", "usage": "Header row, striped even rows, read-only cells, shortcut key badges" },
|
|
61
|
+
{ "name": "wg-dropdown-bg", "category": "surface", "usage": "Floating toolbar background, context menu, dropdown panels" },
|
|
61
62
|
|
|
62
63
|
{ "name": "wg-border-color", "category": "border", "usage": "Table border, cell borders, header bottom, toolbar dividers" },
|
|
63
64
|
{ "name": "wg-border-color-hover", "category": "border", "usage": "Pagination button border on hover" },
|
package/dist/grid.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Column, CellValidationState, RowToolbarConfig,
|
|
1
|
+
import type { Column, CellValidationState, RowToolbarConfig, ContextMenuConfig, 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
2
|
type TreeNode = {
|
|
3
3
|
path: string;
|
|
4
4
|
parent: string | null;
|
|
@@ -29,8 +29,10 @@ export declare class WebGrid<T = unknown> {
|
|
|
29
29
|
protected _isStriped: boolean;
|
|
30
30
|
protected _isHoverable: boolean;
|
|
31
31
|
protected _isEditable: boolean;
|
|
32
|
+
protected _isRowEditable: boolean | ((row: T) => boolean) | undefined;
|
|
32
33
|
protected _editTrigger: EditTrigger;
|
|
33
34
|
protected _editStartSelection: EditStartSelection;
|
|
35
|
+
protected _shouldCommitOnClickAway: boolean;
|
|
34
36
|
protected _mode: GridMode;
|
|
35
37
|
protected _dropdownToggleVisibility: ToggleVisibility;
|
|
36
38
|
protected _shouldShowDropdownOnFocus: boolean;
|
|
@@ -53,7 +55,7 @@ export declare class WebGrid<T = unknown> {
|
|
|
53
55
|
protected _cellToolbarOffset: number | string;
|
|
54
56
|
protected _toolbarBtnMinWidth: string | undefined;
|
|
55
57
|
protected _inlineActionsTitle: string;
|
|
56
|
-
protected _contextMenu:
|
|
58
|
+
protected _contextMenu: ContextMenuConfig<T>[] | undefined;
|
|
57
59
|
protected _contextMenuXOffset: number;
|
|
58
60
|
protected _contextMenuYOffset: number;
|
|
59
61
|
protected _headerContextMenu: HeaderMenuConfig<T>[] | undefined;
|
|
@@ -105,6 +107,8 @@ export declare class WebGrid<T = unknown> {
|
|
|
105
107
|
}) => void) | undefined;
|
|
106
108
|
protected _sort: SortState[];
|
|
107
109
|
protected _filters: Record<string, string>;
|
|
110
|
+
protected _onfilterchange: ((filters: Record<string, string>) => void) | undefined;
|
|
111
|
+
protected _columnMinWidth: string | undefined;
|
|
108
112
|
protected _currentPage: number;
|
|
109
113
|
protected _totalItems: number | null;
|
|
110
114
|
protected _showPagination: boolean | 'auto';
|
|
@@ -170,6 +174,8 @@ export declare class WebGrid<T = unknown> {
|
|
|
170
174
|
protected _treePathMember: string | null;
|
|
171
175
|
protected _treeLevelMember: string | null;
|
|
172
176
|
protected _treeParentMember: string | null;
|
|
177
|
+
protected _idMember: string | null;
|
|
178
|
+
protected _hasWarnedAboutMissingRowId: boolean;
|
|
173
179
|
protected _treeSeparator: string | null;
|
|
174
180
|
protected _treeDataSorted: boolean;
|
|
175
181
|
protected _expandedPaths: Set<string>;
|
|
@@ -182,6 +188,8 @@ export declare class WebGrid<T = unknown> {
|
|
|
182
188
|
protected _treeCollapsedGlyph: string;
|
|
183
189
|
protected _treeChevronCallback: TreeChevronCallback<T> | undefined;
|
|
184
190
|
protected _treeChevronCache: WeakMap<object, Map<string, string>>;
|
|
191
|
+
protected _paginatedItemsCache: T[] | null;
|
|
192
|
+
protected _displayItemsCache: T[] | null;
|
|
185
193
|
get items(): T[];
|
|
186
194
|
set items(value: T[]);
|
|
187
195
|
get columns(): Column<T>[];
|
|
@@ -190,6 +198,10 @@ export declare class WebGrid<T = unknown> {
|
|
|
190
198
|
set sortMode(value: SortMode);
|
|
191
199
|
get isFilterable(): boolean;
|
|
192
200
|
set isFilterable(value: boolean);
|
|
201
|
+
get onfilterchange(): ((filters: Record<string, string>) => void) | undefined;
|
|
202
|
+
set onfilterchange(value: ((filters: Record<string, string>) => void) | undefined);
|
|
203
|
+
get columnMinWidth(): string | undefined;
|
|
204
|
+
set columnMinWidth(value: string | undefined);
|
|
193
205
|
get isPageable(): boolean;
|
|
194
206
|
set isPageable(value: boolean);
|
|
195
207
|
get pageSize(): number;
|
|
@@ -204,10 +216,14 @@ export declare class WebGrid<T = unknown> {
|
|
|
204
216
|
set isHoverable(value: boolean);
|
|
205
217
|
get isEditable(): boolean;
|
|
206
218
|
set isEditable(value: boolean);
|
|
219
|
+
get isRowEditable(): boolean | ((row: T) => boolean) | undefined;
|
|
220
|
+
set isRowEditable(value: boolean | ((row: T) => boolean) | undefined);
|
|
207
221
|
get editTrigger(): EditTrigger;
|
|
208
222
|
set editTrigger(value: EditTrigger);
|
|
209
223
|
get editStartSelection(): EditStartSelection;
|
|
210
224
|
set editStartSelection(value: EditStartSelection);
|
|
225
|
+
get shouldCommitOnClickAway(): boolean;
|
|
226
|
+
set shouldCommitOnClickAway(value: boolean);
|
|
211
227
|
get mode(): GridMode;
|
|
212
228
|
set mode(value: GridMode);
|
|
213
229
|
get dropdownToggleVisibility(): ToggleVisibility;
|
|
@@ -285,8 +301,8 @@ export declare class WebGrid<T = unknown> {
|
|
|
285
301
|
set toolbarBtnMinWidth(value: string | undefined);
|
|
286
302
|
get inlineActionsTitle(): string;
|
|
287
303
|
set inlineActionsTitle(value: string);
|
|
288
|
-
get contextMenu():
|
|
289
|
-
set contextMenu(value:
|
|
304
|
+
get contextMenu(): ContextMenuConfig<T>[] | undefined;
|
|
305
|
+
set contextMenu(value: ContextMenuConfig<T>[] | undefined);
|
|
290
306
|
get contextMenuXOffset(): number;
|
|
291
307
|
set contextMenuXOffset(value: number);
|
|
292
308
|
get contextMenuYOffset(): number;
|
|
@@ -377,6 +393,7 @@ export declare class WebGrid<T = unknown> {
|
|
|
377
393
|
get ontoolbarclick(): ((detail: ToolbarClickDetail<T>) => void) | undefined;
|
|
378
394
|
set ontoolbarclick(value: ((detail: ToolbarClickDetail<T>) => void) | undefined);
|
|
379
395
|
set onrowaction(value: ((detail: RowActionClickDetail<T>) => void) | undefined);
|
|
396
|
+
get oncontextmenuopen(): ((context: ContextMenuContext<T>) => void) | undefined;
|
|
380
397
|
set oncontextmenuopen(value: ((context: ContextMenuContext<T>) => void) | undefined);
|
|
381
398
|
get onheadercontextmenuopen(): ((context: HeaderMenuContext<T>) => void) | undefined;
|
|
382
399
|
set onheadercontextmenuopen(value: ((context: HeaderMenuContext<T>) => void) | undefined);
|
|
@@ -467,6 +484,17 @@ export declare class WebGrid<T = unknown> {
|
|
|
467
484
|
set newRowIndicator(value: string);
|
|
468
485
|
get createEmptyRowCallback(): (() => T | Promise<T>) | undefined;
|
|
469
486
|
set createEmptyRowCallback(value: (() => T | Promise<T>) | undefined);
|
|
487
|
+
get idMember(): string | null;
|
|
488
|
+
set idMember(value: string | null);
|
|
489
|
+
/**
|
|
490
|
+
* Return a stable string key for a row. Coalesce order: idMember value → treePathMember
|
|
491
|
+
* value → displayed-index string fallback. Used to key drafts and invalid-cell markers
|
|
492
|
+
* so they don't drift when the displayed order changes (sort, filter, page, tree toggle).
|
|
493
|
+
*
|
|
494
|
+
* Distinct from `getRowId` which is the row-locking identifier (any type).
|
|
495
|
+
*/
|
|
496
|
+
getStableRowKey(item: T, displayedIndex: number): string;
|
|
497
|
+
private maybeWarnAboutRowIdentity;
|
|
470
498
|
get treePathMember(): string | null;
|
|
471
499
|
set treePathMember(value: string | null);
|
|
472
500
|
get treeLevelMember(): string | null;
|
|
@@ -488,6 +516,22 @@ export declare class WebGrid<T = unknown> {
|
|
|
488
516
|
toggleExpandedPath(path: string): void;
|
|
489
517
|
expandAll(): void;
|
|
490
518
|
collapseAll(): void;
|
|
519
|
+
/**
|
|
520
|
+
* Expand a path's entire subtree (the path itself + all descendants).
|
|
521
|
+
* Used by the per-row "expand all" context-menu action — file-explorer convention
|
|
522
|
+
* is "expand everything *under here*", not the entire dataset.
|
|
523
|
+
*/
|
|
524
|
+
expandSubtree(path: string): void;
|
|
525
|
+
/**
|
|
526
|
+
* Collapse a path's entire subtree (the path itself + all descendants).
|
|
527
|
+
*/
|
|
528
|
+
collapseSubtree(path: string): void;
|
|
529
|
+
/**
|
|
530
|
+
* Walk up the path's ancestor chain looking for the nearest expanded ancestor.
|
|
531
|
+
* Used by Ctrl+ArrowLeft: when the focused row has no expanded children to collapse,
|
|
532
|
+
* step up to the closest ancestor that is expanded so the user gets meaningful action.
|
|
533
|
+
*/
|
|
534
|
+
findNearestExpandedAncestor(path: string): string | null;
|
|
491
535
|
/** Returns tree info for a row (used by rendering) */
|
|
492
536
|
getRowTreeInfo(item: T): {
|
|
493
537
|
path: string;
|
|
@@ -575,6 +619,15 @@ export declare class WebGrid<T = unknown> {
|
|
|
575
619
|
copyCellSelectionToClipboard(): Promise<boolean>;
|
|
576
620
|
get isNavigateMode(): boolean;
|
|
577
621
|
get filteredItems(): T[];
|
|
622
|
+
/**
|
|
623
|
+
* Returns the set of fields whose current filter input is "incomplete" (column.filter
|
|
624
|
+
* returned null when probed against the first row). Renderers can use this to mark the
|
|
625
|
+
* input visually (red border) without excluding all rows.
|
|
626
|
+
*/
|
|
627
|
+
getInvalidFilterFields(): Set<string>;
|
|
628
|
+
get filters(): Record<string, string>;
|
|
629
|
+
set filters(value: Record<string, string>);
|
|
630
|
+
setFilter(field: string, value: string): void;
|
|
578
631
|
get sortedItems(): T[];
|
|
579
632
|
get paginatedItems(): T[];
|
|
580
633
|
get totalPages(): number;
|
|
@@ -630,6 +683,12 @@ export declare class WebGrid<T = unknown> {
|
|
|
630
683
|
*/
|
|
631
684
|
protected checkToolbarConflicts(): void;
|
|
632
685
|
protected requestUpdate(): void;
|
|
686
|
+
/**
|
|
687
|
+
* Drop memoized pipeline output. The web-component's requestUpdate override
|
|
688
|
+
* calls this on every state mutation, so callers don't need to invoke it
|
|
689
|
+
* directly — but it's safe to call any time the cache might be stale.
|
|
690
|
+
*/
|
|
691
|
+
invalidateDisplayCache(): void;
|
|
633
692
|
getRowDraft(rowIndex: number): T | undefined;
|
|
634
693
|
hasRowDraft(rowIndex: number): boolean;
|
|
635
694
|
/**
|
|
@@ -650,6 +709,8 @@ export declare class WebGrid<T = unknown> {
|
|
|
650
709
|
discardAllDrafts(): void;
|
|
651
710
|
getCellRawValue(item: T, rowIndex: number, field: string): unknown;
|
|
652
711
|
getCellValue(item: T, column: Column<T>, rowIndex?: number): string;
|
|
712
|
+
/** Compute the stable rowKey for a displayed-row index, or null if no row at that index. */
|
|
713
|
+
private rowKeyForIndex;
|
|
653
714
|
isCellInvalid(rowIndex: number, field: string): boolean;
|
|
654
715
|
getCellValidationError(rowIndex: number, field: string): string | null;
|
|
655
716
|
addInvalidCell(rowIndex: number, field: string, error: string): void;
|
|
@@ -696,16 +757,42 @@ export declare class WebGrid<T = unknown> {
|
|
|
696
757
|
*/
|
|
697
758
|
updateDraftValue(rowIndex: number, field: string, newValue: unknown): void;
|
|
698
759
|
/**
|
|
699
|
-
* Check if a column's cells can be edited
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
*
|
|
704
|
-
|
|
705
|
-
|
|
760
|
+
* Check if a column's cells can be edited.
|
|
761
|
+
* `row` is required to evaluate function-form predicates; pass it for per-row checks,
|
|
762
|
+
* omit it for static column-level checks (e.g. building toolbar UI). When omitted and a
|
|
763
|
+
* predicate exists, conservatively returns true so the column isn't hidden.
|
|
764
|
+
*
|
|
765
|
+
* Resolution order:
|
|
766
|
+
* 0. tree column in tree mode → not editable (the tree column displays hierarchy, not data)
|
|
767
|
+
* 1. column.isEditable === false → not editable (hard veto)
|
|
768
|
+
* 2. column.isEditable === function with row → must return true to continue
|
|
769
|
+
* 3. grid.isRowEditable with row → must pass to continue
|
|
770
|
+
* 4. column.isEditable === true → editable (overrides grid-level)
|
|
771
|
+
* 5. else → defer to grid-level isEditable
|
|
772
|
+
*/
|
|
773
|
+
isCellEditable(column: Column<T>, row?: T): boolean;
|
|
774
|
+
/**
|
|
775
|
+
* Get all editable columns with their indices.
|
|
776
|
+
* Pass `row` to evaluate per-row predicates (function-form `isEditable`, `isRowEditable`).
|
|
777
|
+
* Without `row`, returns columns that are *potentially* editable (any row could pass).
|
|
778
|
+
*/
|
|
779
|
+
getEditableColumns(row?: T): {
|
|
706
780
|
index: number;
|
|
707
781
|
column: Column<T>;
|
|
708
782
|
}[];
|
|
783
|
+
/**
|
|
784
|
+
* Walk rows in `direction` looking for the next row that has at least one editable
|
|
785
|
+
* column at or beyond `fromColEditableIndex` (or beyond the last when going forward
|
|
786
|
+
* past the row's editable list). Used by Tab traversal in heterogeneous trees so the
|
|
787
|
+
* cursor skips read-only rows (e.g. team rows) entirely instead of landing on a cell
|
|
788
|
+
* with no tabindex and silently losing focus.
|
|
789
|
+
*
|
|
790
|
+
* Returns null if no editable cell exists in the requested direction.
|
|
791
|
+
*/
|
|
792
|
+
findNextEditableCell(fromRowIndex: number, fromColEditableIndex: number, direction: 'forward' | 'backward'): {
|
|
793
|
+
rowIndex: number;
|
|
794
|
+
colIndex: number;
|
|
795
|
+
} | null;
|
|
709
796
|
/**
|
|
710
797
|
* Check if a specific cell is currently focused
|
|
711
798
|
*/
|
package/dist/logger.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ export declare const initLogger: any;
|
|
|
28
28
|
export declare const dataLogger: any;
|
|
29
29
|
export declare const uiLogger: any;
|
|
30
30
|
export declare const interactionLogger: any;
|
|
31
|
+
export declare const perfLogger: any;
|
|
31
32
|
export default log;
|
|
32
33
|
/**
|
|
33
34
|
* List of all logging categories for introspection
|
|
@@ -22,8 +22,12 @@ export interface ClickEventManager {
|
|
|
22
22
|
* Initialize click listeners
|
|
23
23
|
* @param container - The grid container element (.wg)
|
|
24
24
|
* @param hostElement - The custom element host (<web-grid>) for outside detection
|
|
25
|
+
* @param isPathInOwnedScope - Optional predicate; when it returns true for a
|
|
26
|
+
* click's composed path, the click is treated as inside the grid (used for
|
|
27
|
+
* document.body-hosted popovers like the datepicker and context menus
|
|
28
|
+
* that need to extend the grid's "inside" scope beyond the host element).
|
|
25
29
|
*/
|
|
26
|
-
init(container: HTMLElement, hostElement: HTMLElement): void;
|
|
30
|
+
init(container: HTMLElement, hostElement: HTMLElement, isPathInOwnedScope?: (path: EventTarget[]) => boolean): void;
|
|
27
31
|
/**
|
|
28
32
|
* Remove all listeners and subscriptions (called in disconnectedCallback)
|
|
29
33
|
*/
|
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
import type { ContextMenuItem, ContextMenuContext, HeaderMenuConfig, HeaderMenuItem, HeaderMenuContext } from '../../types.js';
|
|
1
|
+
import type { ContextMenuItem, ContextMenuConfig, ContextMenuContext, HeaderMenuConfig, HeaderMenuItem, HeaderMenuContext } from '../../types.js';
|
|
2
2
|
import type { GridContext } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Normalize a ContextMenuConfig list (which may contain string shorthand and/or items
|
|
5
|
+
* with `type` set) into a flat list of fully-defined ContextMenuItem objects.
|
|
6
|
+
* Predefined types get default labels, icons, visibility predicates, and onclick handlers.
|
|
7
|
+
*
|
|
8
|
+
* The grid is needed at click time for `expand-all` etc. to call `expandSubtree(path)`,
|
|
9
|
+
* so we close over it rather than passing it through later.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeContextMenu<T>(config: ContextMenuConfig<T>[], grid: GridContext<T>['grid']): ContextMenuItem<T>[];
|
|
3
12
|
/**
|
|
4
13
|
* Open context menu at position
|
|
5
14
|
*/
|
|
@@ -11,6 +11,8 @@ export declare class DatePicker {
|
|
|
11
11
|
private anchor;
|
|
12
12
|
private input;
|
|
13
13
|
private cleanupAutoUpdate;
|
|
14
|
+
/** The picker's outer container (mounted in document.body), or null when closed. */
|
|
15
|
+
getElement(): HTMLElement | null;
|
|
14
16
|
private previousInputValue;
|
|
15
17
|
private boundHandleClickOutside;
|
|
16
18
|
private boundHandleKeyDown;
|
|
@@ -4,6 +4,14 @@ import type { GridContext } from '../types.js';
|
|
|
4
4
|
* Wrap cell content with tree indent + chevron when column is the tree column.
|
|
5
5
|
* Returns the original innerHtml unchanged when tree mode is off or column isn't isTree.
|
|
6
6
|
*
|
|
7
|
+
* Layout: inline-flex wrapper with `padding-inline-start: level * --wg-tree-indent`.
|
|
8
|
+
* Inside the wrapper sits either a chevron <button> (branch) or an empty
|
|
9
|
+
* <span class="wg__tree-leaf-spacer"> (leaf, keeps alignment with branches).
|
|
10
|
+
*
|
|
11
|
+
* Chevron rotation: when treeExpandedGlyph === treeCollapsedGlyph (the default
|
|
12
|
+
* setup) we add `.wg__tree-chevron--expanded` and CSS rotates the glyph 90°.
|
|
13
|
+
* When the glyphs differ, the glyph is swapped on state change instead.
|
|
14
|
+
*
|
|
7
15
|
* Used by both the full-table render (table.ts) and the surgical single-cell
|
|
8
16
|
* render (cell.ts), so both paths keep the chevron after focus/edit transitions.
|
|
9
17
|
*/
|
package/dist/perf.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export declare const gridPerf: {
|
|
2
|
+
counters: {
|
|
3
|
+
displayItems: number;
|
|
4
|
+
displayItemsMs: number;
|
|
5
|
+
paginatedItems: number;
|
|
6
|
+
paginatedItemsMs: number;
|
|
7
|
+
treeSorted: number;
|
|
8
|
+
treeSortedMs: number;
|
|
9
|
+
treeVisible: number;
|
|
10
|
+
treeVisibleMs: number;
|
|
11
|
+
cellDisplayItemsLookup: number;
|
|
12
|
+
};
|
|
13
|
+
reset(): void;
|
|
14
|
+
summary(): string;
|
|
15
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -75,7 +75,7 @@ export type CustomEditorContext<T> = {
|
|
|
75
75
|
cancel: () => void;
|
|
76
76
|
};
|
|
77
77
|
export type CellValidationState = {
|
|
78
|
-
|
|
78
|
+
rowKey: string;
|
|
79
79
|
field: string;
|
|
80
80
|
error: string;
|
|
81
81
|
};
|
|
@@ -104,6 +104,8 @@ export type Column<T> = {
|
|
|
104
104
|
maxWidth?: string;
|
|
105
105
|
textOverflow?: 'wrap' | 'ellipsis';
|
|
106
106
|
maxLines?: number;
|
|
107
|
+
nowrap?: boolean;
|
|
108
|
+
filter?: (filterValue: string, row: T) => boolean | null;
|
|
107
109
|
horizontalAlign?: "left" | "center" | "right" | "justify";
|
|
108
110
|
verticalAlign?: "top" | "middle" | "bottom";
|
|
109
111
|
headerHorizontalAlign?: "left" | "center" | "right" | "justify";
|
|
@@ -113,7 +115,7 @@ export type Column<T> = {
|
|
|
113
115
|
formatCallback?: (value: unknown, row: T) => string;
|
|
114
116
|
templateCallback?: (row: T) => string;
|
|
115
117
|
renderCallback?: CellRenderCallback<T>;
|
|
116
|
-
isEditable?: boolean;
|
|
118
|
+
isEditable?: boolean | ((row: T) => boolean);
|
|
117
119
|
editor?: EditorType;
|
|
118
120
|
editTrigger?: EditTrigger;
|
|
119
121
|
dropdownToggleVisibility?: ToggleVisibility;
|
|
@@ -214,23 +216,27 @@ export type ContextMenuContext<T> = {
|
|
|
214
216
|
column: Column<T>;
|
|
215
217
|
cellValue: unknown;
|
|
216
218
|
};
|
|
219
|
+
export type PredefinedContextMenuItemType = 'expand-all' | 'collapse-all' | 'expand-tree' | 'collapse-tree';
|
|
217
220
|
export type ContextMenuItem<T> = {
|
|
218
221
|
id: string;
|
|
219
|
-
label
|
|
222
|
+
label?: string | ((context: ContextMenuContext<T>) => string);
|
|
220
223
|
icon?: string | ((context: ContextMenuContext<T>) => string);
|
|
221
224
|
shortcut?: string;
|
|
222
225
|
disabled?: boolean | ((context: ContextMenuContext<T>) => boolean);
|
|
223
226
|
visible?: boolean | ((context: ContextMenuContext<T>) => boolean);
|
|
224
227
|
danger?: boolean;
|
|
225
228
|
dividerBefore?: boolean;
|
|
229
|
+
type?: PredefinedContextMenuItemType;
|
|
226
230
|
onclick?: (context: ContextMenuContext<T>) => void | Promise<void>;
|
|
227
231
|
};
|
|
232
|
+
export type ContextMenuConfig<T> = PredefinedContextMenuItemType | ContextMenuItem<T>;
|
|
228
233
|
export type HeaderMenuContext<T> = {
|
|
229
234
|
column: Column<T>;
|
|
230
235
|
field: string;
|
|
231
236
|
columnIndex: number;
|
|
232
237
|
sortDirection: 'asc' | 'desc' | null;
|
|
233
238
|
isFrozen: boolean;
|
|
239
|
+
isTreeMode: boolean;
|
|
234
240
|
allColumns: Column<T>[];
|
|
235
241
|
labels: GridLabels;
|
|
236
242
|
};
|
|
@@ -301,6 +307,8 @@ export type QuickGridProps<T> = {
|
|
|
301
307
|
items: T[];
|
|
302
308
|
columns: Column<T>[];
|
|
303
309
|
isFilterable?: boolean;
|
|
310
|
+
columnMinWidth?: string;
|
|
311
|
+
onfilterchange?: (filters: Record<string, string>) => void;
|
|
304
312
|
isPageable?: boolean;
|
|
305
313
|
pageSize?: number;
|
|
306
314
|
isStriped?: boolean;
|
|
@@ -313,6 +321,7 @@ export type QuickGridProps<T> = {
|
|
|
313
321
|
style?: string;
|
|
314
322
|
customStylesCallback?: () => string;
|
|
315
323
|
rowClassCallback?: (row: T, rowIndex: number) => string | null;
|
|
324
|
+
idMember?: keyof T | string;
|
|
316
325
|
sort?: SortState[];
|
|
317
326
|
sortMode?: SortMode;
|
|
318
327
|
currentPage?: number;
|
|
@@ -327,8 +336,10 @@ export type QuickGridProps<T> = {
|
|
|
327
336
|
summaryContentCallback?: SummaryContentCallback<T>;
|
|
328
337
|
isSummaryInline?: boolean;
|
|
329
338
|
isEditable?: boolean;
|
|
339
|
+
isRowEditable?: boolean | ((row: T) => boolean);
|
|
330
340
|
editTrigger?: EditTrigger;
|
|
331
341
|
editStartSelection?: EditStartSelection;
|
|
342
|
+
shouldCommitOnClickAway?: boolean;
|
|
332
343
|
mode?: GridMode;
|
|
333
344
|
dropdownToggleVisibility?: ToggleVisibility;
|
|
334
345
|
shouldShowDropdownOnFocus?: boolean;
|
|
@@ -347,7 +358,7 @@ export type QuickGridProps<T> = {
|
|
|
347
358
|
cellToolbarOffset?: number | string;
|
|
348
359
|
toolbarBtnMinWidth?: string;
|
|
349
360
|
inlineActionsTitle?: string;
|
|
350
|
-
contextMenu?:
|
|
361
|
+
contextMenu?: ContextMenuConfig<T>[];
|
|
351
362
|
contextMenuXOffset?: number;
|
|
352
363
|
contextMenuYOffset?: number;
|
|
353
364
|
oncontextmenuopen?: (context: ContextMenuContext<T>) => void;
|