@rozie-ui/data-table-lit 0.7.1 → 0.7.3
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/CHANGELOG.md +429 -0
- package/README.md +11 -11
- package/dist/index.cjs +97 -17
- package/dist/index.d.cts +174 -77
- package/dist/index.d.mts +174 -77
- package/dist/index.mjs +114 -34
- package/package.json +4 -3
- package/src/Column.ts +8 -6
- package/src/DataTable.ts +275 -206
- package/src/FilterNumberRange.ts +10 -1
- package/src/index.ts +2 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
# @rozie-ui/data-table-lit
|
|
2
|
+
|
|
3
|
+
## 0.7.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cf49212: Regenerated with the compiler fixes found while building `@rozie-ui/select`. Depending on the target and the component:
|
|
8
|
+
- Angular: a two-way (`model: true`) prop is now controlled while the consumer binds a value — a write by the component only emits `<name>Change`, so a consumer that refuses the change keeps the control on its value. The prop keeps its `ModelSignal` type. `$onUpdate` hooks now run (after every render); they never ran before. An output named like a native bubbling event (`change`, `submit`, `reset`, `select`) no longer also delivers the native event to a consumer's binding.
|
|
9
|
+
- Vue: a two-way prop bound one-way (`:value` without `v-model`) is now controlled too: the component's write emits `update:value` and the control keeps the consumer's value.
|
|
10
|
+
- React: an `$expose` verb now runs in `flushSync`, so calls made in one tick each see what the call before left; primitive script constants are plain `const`s; a body statement no longer reads a binding declared after it on the first render.
|
|
11
|
+
- Svelte, Solid: a `$watch` now sees a `$data` write made in `$onMount`.
|
|
12
|
+
- Lit: a `$watch` whose getter reads both props and `$data` re-runs when only the prop changes; duplicated comments in lifecycle bodies are gone.
|
|
13
|
+
- @rozie/runtime-lit@0.10.0
|
|
14
|
+
|
|
15
|
+
## 0.7.2
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- c362398: The npm package now includes `CHANGELOG.md`. It was written for every release but left out of the tarball, because npm no longer adds a changelog by itself, so a behaviour change recorded there (such as the 0.7.0 combobox Ctrl/Cmd/Alt+Enter change) was invisible to anyone reading the installed package.
|
|
20
|
+
- edb2ec4: DataTable: all 17 event handlers, the imperative handle and the scoped-slot parameters now have real types instead of `(...args: any[]) => void`, `(...args: any[]) => any` and `any`.
|
|
21
|
+
- Events: the state events carry the matching TanStack Table type (`sort-change` → `SortingState`, `selection-change` → `RowSelectionState`, `page-change` → `PaginationState`, and likewise `expand-change`, `group-change`, `pin-change`, `reorder-change`, `resize-change`, `visibility-change`). The rest have exported payload types: `DataTableFilterChangePayload`, `DataTableActiveCellChangePayload`, `DataTableCellEditCommitPayload`, `DataTableRowEditCommitPayload`, `DataTableRowActivatePayload`, `DataTableRangeChangePayload`, `DataTableHistoryChangePayload` and `DataTableVisibleRangeChangePayload`.
|
|
22
|
+
- Handle: every verb now has a typed signature (for example `sortColumn(colId: string, desc?: boolean)`, `getActiveCell(): DataTableActiveCell`, `getSelectedRange(): DataTableRange`, `pinColumn(colId, 'left' | 'right' | false)`). `DataTableCellPosition`, `DataTableActiveCell`, `DataTableRange` and `DataTableGroupableColumn` are exported.
|
|
23
|
+
- Slots: the `#cell`, `#editor`, `#colHeader`, `#filter`, `#selectAll`, `#selectCell`, `#groupBar`, `#placeholder` and `#detail` parameters are typed (`columnId: string`, `commit: (value: any) => void`, `column: Column<any, unknown>`, …).
|
|
24
|
+
|
|
25
|
+
Row data has no generic, so row-data positions (`row`, `value`, `oldValue`, `newValue`) stay `any`. A handler written against a different payload shape may now be rejected by the type checker.
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [c362398]
|
|
28
|
+
- @rozie/runtime-lit@0.9.0
|
|
29
|
+
|
|
30
|
+
## 0.7.1
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- a9d67cb: Accept `@rozie-ui/popover-<framework>` 0.3 as a peer dependency, alongside the 0.2 range (and, for data-table, the 0.1 range). Popover 0.3 adds the `reference` prop; the popover features these components already use are unchanged.
|
|
35
|
+
- a9d67cb: Regenerated with the union-aware Lit attribute converter. On `<rozie-full-calendar>`, a numeric `height` attribute (`height="600"`) now arrives as the number `600`; on `<rozie-column>`, a numeric `width` attribute (`width="120"`) now arrives as the number `120`. A CSS string such as `height="auto"` or `width="120px"` still arrives as a string. Rendering is unchanged, because both components already treated a numeric string as pixels.
|
|
36
|
+
- 5abc36f: Lit: a standalone `$onUnmount(() => { … })` now runs when the element is removed. The Lit emitter spliced the callback into `disconnectedCallback` as an uncalled arrow, so the teardown never ran on Lit (the other five targets were unaffected, as was an `$onMount` cleanup return or an `$onUnmount` paired with an `$onMount(fn)`). The body now runs as its own function, so an early `return` in it cannot skip the remaining teardown.
|
|
37
|
+
|
|
38
|
+
Leaves regenerated with the fix: `combobox-lit` now tears down its list virtualizer on removal, `command-palette-lit` clears its pending search debounce timer, `data-table-lit` removes its document `pointerdown` listener, and `toast-lit` stops its auto-dismiss timers (which could otherwise fire against a removed element).
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [a9d67cb]
|
|
41
|
+
- @rozie/runtime-lit@0.8.0
|
|
42
|
+
|
|
43
|
+
## 0.7.0
|
|
44
|
+
|
|
45
|
+
### Minor Changes
|
|
46
|
+
|
|
47
|
+
- d6e1909: Added `scrollToRow(index, options?)` and `getScrollElement()` to the DataTable imperative handle.
|
|
48
|
+
|
|
49
|
+
Previously, scrolling a `virtual` table to a specific row programmatically meant reaching into the internal `.rdt-scroll` class selector — the only prior way to do it, and one that breaks on any internal refactor. `scrollToRow` closes that gap: it mirrors TanStack virtual-core's own `scrollToIndex` API shape exactly, forwarding `options` (`{ align?: 'start' | 'center' | 'end' | 'auto', behavior?: 'auto' | 'smooth' | 'instant' }`) unchanged. `getScrollElement()` returns the same `.rdt-scroll` DOM node the row/column virtualizer(s) observe, for a consumer that wants to read scroll position directly.
|
|
50
|
+
|
|
51
|
+
Both verbs are independent of `focusCell`'s grid-mode focus — `scrollToRow` never moves the roving active cell or fires `activecell-change`, and is callable in either `interactionMode`, table or grid. Both are safe no-ops/`null` when the table is not `virtual`.
|
|
52
|
+
|
|
53
|
+
## 0.6.0
|
|
54
|
+
|
|
55
|
+
### Minor Changes
|
|
56
|
+
|
|
57
|
+
- 4f2148d: Three new capabilities, a cross-target `<select>` correctness fix, an extensive keyboard/a11y
|
|
58
|
+
hardening pass, and the theming + packaging fixes shared with the rest of this release.
|
|
59
|
+
|
|
60
|
+
**`getRowId` — selection and expansion now follow the row, not its position.** `table-core`
|
|
61
|
+
keys `rowSelection`/`expanded` by row position unless given a `getRowId`, so a row inserted
|
|
62
|
+
above a checked one silently moved the selection (and an open detail row) onto a different row.
|
|
63
|
+
New prop `getRowId: (originalRow, index, parentRow?) => string | number`. The active cell and an
|
|
64
|
+
in-progress edit remain positional by design of the grid — a push insert above them still shifts
|
|
65
|
+
the keyboard cursor; that is a separate, deliberately deferred design question.
|
|
66
|
+
|
|
67
|
+
**`row-activate` — open a row by click, or Enter in grid mode.** New event
|
|
68
|
+
`row-activate { row, index, trigger }` (`trigger` is `'click' | 'keyboard'`) for list-style use
|
|
69
|
+
(e.g. opening an email thread). Fires on a plain click outside any control in the clicked cell,
|
|
70
|
+
or on Enter over a non-editable, control-less cell in grid mode; never on group-header or detail
|
|
71
|
+
rows, selection gestures, drag-select, or a `singleClickEdit` click. Table mode is click-only (no
|
|
72
|
+
row focus there). Purely additive.
|
|
73
|
+
|
|
74
|
+
**Virtual lazy loading — sparse data up to `rowCount`, placeholder rows, and a
|
|
75
|
+
`visible-range-change` event.** Under `virtual` + `manual` + `rowCount`, `data` may now be
|
|
76
|
+
sparse: any `undefined`/`null` entry, or anything past `data.length`, is treated as a
|
|
77
|
+
not-yet-loaded row and fed to `table-core` as a cached placeholder. Placeholder rows carry
|
|
78
|
+
`aria-busy`, a new `#placeholder` scoped slot per data cell (`{ index, columnId }`, default a
|
|
79
|
+
skeleton bar), and are excluded from selection, expansion, editing and activation. New event
|
|
80
|
+
`visible-range-change { start, end }` reports the rendered window (overscan included) over the
|
|
81
|
+
full row space, deduped, so a consumer can fetch the rows currently on screen. Placeholder rows
|
|
82
|
+
are keyed `__rdt_ph_<index>` until they fill.
|
|
83
|
+
|
|
84
|
+
**Fixed: a filled placeholder row's cached measured size now migrates to its real id** (and the
|
|
85
|
+
migration is compatible with `@tanstack/virtual-core` `<=3.14`, whose cache-invalidation shape
|
|
86
|
+
differs from `3.15+`). Previously, when a lazy-loaded placeholder row filled with real data and
|
|
87
|
+
changed id, the virtualizer's size cache kept the stale entry — the new id had no cached size,
|
|
88
|
+
got re-estimated, and every re-keyed row's remeasure nudged the scroll anchor (measured: -4px per
|
|
89
|
+
row on React). No API surface change; this is purely an internal scroll-anchoring correctness fix
|
|
90
|
+
for `virtual` + `manual` + `getRowId` tables that lazy-fill placeholder rows.
|
|
91
|
+
|
|
92
|
+
**Cross-target — a bound `<select>`'s value now survives its own options rendering after it**
|
|
93
|
+
(Lit and Angular only; see the `@rozie/core` changeset in this same release for the emitter-level
|
|
94
|
+
fix). Consumer-visible symptom before this fix: a `<select>`-backed editor or filter could commit
|
|
95
|
+
whatever option happened to render first, not the value you set.
|
|
96
|
+
|
|
97
|
+
**An extensive keyboard/a11y hardening pass**, covering (non-exhaustively): pinned columns now
|
|
98
|
+
stick exactly past the pinned columns before them; a header-active-cell move that reaches an
|
|
99
|
+
off-window column keeps the tab stop; the active row stays rendered when the viewport scrolls it
|
|
100
|
+
away; Ctrl+Shift+Arrow extends a range from its focus corner rather than the active cell;
|
|
101
|
+
Shift+Space selects the row and Ctrl+Space the column, Escape drops an in-progress range; the
|
|
102
|
+
`GroupBar` is now fully keyboard-operable; the grid keymap now reaches a custom `#editor`
|
|
103
|
+
drop-in; the clipboard cluster (copy/cut/paste/fill/delete) is closed and each operation
|
|
104
|
+
announces the correct verb to assistive tech instead of always saying "pasted"; the column axis
|
|
105
|
+
and cell-range selection now carry a real accessible surface (`aria-colindex`/`aria-colcount` on
|
|
106
|
+
header and body cells, matching the row-axis equivalent that already existed); filter re-sync and
|
|
107
|
+
date-coercion edge cases are fixed; every built-in drop-in editor (not just `EditorText`) now
|
|
108
|
+
focuses on open and refocuses correctly on a reactive re-open; `width` now actually maps to the
|
|
109
|
+
underlying column `size` (it was previously a fully inert documented prop); a text filter's
|
|
110
|
+
commit timing no longer races the input's own draft write; and column headers announce their
|
|
111
|
+
label text to assistive tech instead of the raw column id.
|
|
112
|
+
|
|
113
|
+
**Theming — design-system bridges now yield to an ancestor's own tokens, and apply correctly on
|
|
114
|
+
Lit.** Same fix as every other themed family in this release: previously a bridge (`bootstrap`,
|
|
115
|
+
`material`, `shadcn`) assigned the tokens it maps directly on the table's own class, which always
|
|
116
|
+
beats an ancestor's value by CSS specificity rules regardless of import order — so with a bridge
|
|
117
|
+
imported, an ancestor override of a public `--rozie-data-table-*` token had no effect, and on Lit
|
|
118
|
+
(whose classes render inside a shadow root) every bridge was completely inert. Bridges now
|
|
119
|
+
redeclare only the wiring they map, with the design system's own variable as the fallback,
|
|
120
|
+
resolved at the table itself — so it follows the nearest theme scope and works identically
|
|
121
|
+
whichever CSS is imported first.
|
|
122
|
+
|
|
123
|
+
**Fixed: the toolbar keeps its shape under a host page's typography CSS.** A host page that
|
|
124
|
+
styles bare `<summary>` elements (VitePress, Tailwind's `prose`, most CMS themes) grew the
|
|
125
|
+
column-visibility `<details>` and stretched the search input to match, because the component set
|
|
126
|
+
padding/border but not margin on its own `<summary>`. `margin: 0` is now set explicitly.
|
|
127
|
+
|
|
128
|
+
**Solid packaging.** `@rozie-ui/data-table-solid` ships the same compiled-JS-by-default,
|
|
129
|
+
JSX-under-the-`solid`-condition packaging shape as every other published Solid leaf this release
|
|
130
|
+
— see the dedicated Solid packaging changeset for the full description. No API change.
|
|
131
|
+
|
|
132
|
+
No breaking type-shape change: the public `.d.ts` surface only grows (`getRowId`, the
|
|
133
|
+
`row-activate` event, the `visible-range-change` event, and the `#placeholder` slot scope).
|
|
134
|
+
|
|
135
|
+
### Patch Changes
|
|
136
|
+
|
|
137
|
+
- 4f2148d: Fixed: the `themes/*.css` design-token bridge files' example `import` line named the wrong package (`@rozie-ui/<family>-react`) on every non-React target — copied byte-for-byte from one canonical source. It now names `@rozie-ui/data-table-lit`, this package's own.
|
|
138
|
+
|
|
139
|
+
**Fixed: lazy placeholder rows are now excluded from every clipboard write and from full-row
|
|
140
|
+
edit**, making the "excluded from selection, expansion, editing and activation" claim above
|
|
141
|
+
actually true. A paste, a cut, the Delete/Backspace range clear, and a fill-drag that span a
|
|
142
|
+
placeholder row (including a Ctrl+A that spans the whole `rowCount` space) no longer fabricate
|
|
143
|
+
a `cell-edit-commit` for it, inflate the N-of-M aria-live announce, or write a partial row into
|
|
144
|
+
the still-unloaded sparse hole; `editRow()` (and Shift+F2) on a placeholder row is now a no-op
|
|
145
|
+
instead of opening a row editor seeded with `undefined`s.
|
|
146
|
+
|
|
147
|
+
**Docs: the `placeholder` slot's per-target prop name is now documented**, alongside
|
|
148
|
+
`detail`/`groupBar`/`filter`, in the Slots section below (`renderPlaceholder` on React,
|
|
149
|
+
`placeholderSlot` on Solid, the `.placeholder` property on Lit, `#placeholder` elsewhere) — it
|
|
150
|
+
was previously listed only in the generic slots table with no naming note.
|
|
151
|
+
- @rozie/runtime-lit@0.7.5
|
|
152
|
+
|
|
153
|
+
## 0.5.0
|
|
154
|
+
|
|
155
|
+
### Minor Changes
|
|
156
|
+
|
|
157
|
+
- b084200: Two behavior changes plus the documentation remediation pass.
|
|
158
|
+
|
|
159
|
+
**`pinned` is now a real initial pin seed.** A per-column `pinned` of `left`/`right` — from `<Column pinned>` or from the `columns` config array — was previously inert metadata. It is now applied once as the table's starting `columnPinning` state, so `getIsPinned()` reports it and the column joins the matching sticky rail. That also **reorders** the column, since table-core orders visible cells `[left-pinned, center, right-pinned]`. A consumer who has already expressed a pin owns the slice and the declaration is ignored; an interactive unpin is never re-applied.
|
|
160
|
+
|
|
161
|
+
**The four theme bridges now assign at `:root`.** `bootstrap`, `material`, and `shadcn` previously assigned their tokens only on the table's own classes. On the Lit target both `.rozie-data-table` and `.rozie-data-table-wrap` render inside the component's shadow root, where a document-level class selector can never match — so all four bridges were inert on Lit. Trade-off, stated rather than buried: two tables on one page can no longer take different bridges by ancestor class alone. Per-instance overrides still work on the five light-DOM targets via the class selectors, and on all six by setting the public tokens on any ancestor.
|
|
162
|
+
|
|
163
|
+
Also fixes a column resize that did nothing on React (stale mount-time `columnSizingInfo`), a column-window versioning bug that collapsed the column spacer, and a focus steal in the grid active-cell path. The published token surface is now documented accurately: 18 public `--rozie-data-table-*` tokens wired onto 82 internal `--rdt-*` tokens, with the 58 that have no public counterpart named explicitly rather than implied to be covered.
|
|
164
|
+
|
|
165
|
+
No type-shape change: the `.d.ts` delta is documentation comments only, with nothing removed.
|
|
166
|
+
|
|
167
|
+
### Patch Changes
|
|
168
|
+
|
|
169
|
+
- @rozie/runtime-lit@0.7.4
|
|
170
|
+
|
|
171
|
+
## 0.4.0
|
|
172
|
+
|
|
173
|
+
### Minor Changes
|
|
174
|
+
|
|
175
|
+
- 5ad5ef4: Adds a **per-column dynamic-name slot family** on each of data-table's four column-scoped slots — `cell`, `colHeader`, `filter`, and `editor`. Previously the only way to customize one column's rendering was the single catch-all `#cell` slot, branched by hand on the `columnId` scoped param; that branch does not compose, and the same story applied to `#colHeader`, `#filter`, and `#editor`. Now you can fill a specific column directly with an ordinary static named slot, e.g.:
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<DataTable :columns="columns">
|
|
179
|
+
<template #cell-price="{ row, value }">
|
|
180
|
+
<span class="price">{{ formatCurrency(value) }}</span>
|
|
181
|
+
</template>
|
|
182
|
+
</DataTable>
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
replacing `cell-price` with `colHeader-price`, `filter-price`, or `editor-price` for the other three seams. On React, Solid, Svelte, Lit, and Vue the slot key is template-literal-typed with the same scoped-parameter typing the generic slot already had (`{ row, value }` for `cell`, not an untyped `(...args) => ReactNode`). Angular's `templates()` intake stays `Record<string, TemplateRef<unknown>>`, type-erased per key — see `docs/parity.md` for that divergence.
|
|
186
|
+
|
|
187
|
+
**Precedence is three-tier and structural, not a runtime presence check:** a family fill (`#cell-price`) wins over a generic fill (`#cell`), which wins over the built-in render. A column that only fills `#cell` behaves exactly as before for every other column; a column that fills neither renders the built-in cell.
|
|
188
|
+
|
|
189
|
+
**Both `filter-<id>` and `editor-<id>` are gated the same way their generic siblings already were:** a `filter-<id>` fill only applies to a column with `filterable: true`, and an `editor-<id>` fill only applies to a column with `editor: 'custom'` — filling `#editor-status` on a column that isn't `editor: 'custom'` is a no-op, matching today's `#editor` behavior. `cell-<id>` and `colHeader-<id>` are ungated (every column is eligible), matching their generic siblings.
|
|
190
|
+
|
|
191
|
+
**The built-in editor inputs now carry a `data-builtin-editor` marker attribute.** This is an internal implementation detail of the editor-owns-focus contract (the host needs to tell "this is my own editor" from "this is a consumer's drop-in editor" without a runtime slot-presence test) — it has no effect on a consumer's own markup or fills, and no prop or event surface changed.
|
|
192
|
+
|
|
193
|
+
**Emitted markup is no longer byte-identical to 0.3.2 on any of the six targets**, even for a consumer who never touches a family slot: the filter seam's built-in `<input>` moved from a sibling gated by a presence check into a slot fallback, the editor chain now nests inside a fourth branch, and the built-in editors gained the marker attribute above. Behavior for an existing consumer is unchanged — this is a structural refactor of how the same three tiers (family / generic / built-in) are expressed, verified by the full data-table behavioral suite and a dedicated six-target focus-contract spec, not by comparing generated source. See the "Per-column slot families" section on the data-table columns docs page for the full precedence and gating reference.
|
|
194
|
+
|
|
195
|
+
This release also folds in a separately-landed grouped-header width fix (`.changeset/data-table-grouped-header-width-fix.md`): under `table-layout: fixed`, a multi-level group header's `<th>` now correctly sums its spanned leaf columns' widths instead of reporting table-core's flat single-column default.
|
|
196
|
+
|
|
197
|
+
### Patch Changes
|
|
198
|
+
|
|
199
|
+
- c62c832: Fixes a grouped-header rendering defect: under `table-layout: fixed` (which every windowed/virtualized data-table already opts into), a multi-level group header's `<th>` reported table-core's flat single-column default size (150px) instead of the sum of its spanned leaf columns' sizes, so the browser divided that single declared width across every leaf column the group spans — a 5-column group rendered each leaf at 30px (150 / 5) instead of its real declared width. This was a pre-existing gap (tracked since Phase 87 as "Gap 1" in that phase's deferred-items ledger) that surfaced whenever a grouped-header table's rendered column window happened to include the grouped columns; it is now fixed at the source — the header-width chrome helper reads table-core's own `Header.getSize()` (which already sums descendant leaf-column sizes correctly for a group header, and is behavior-identical to the old code path for an ordinary leaf header) instead of the ungrouped `Column.getSize()` accessor. No prop or markup changes; any consumer using multi-level grouped headers will simply see the grouped columns render at their correct, intended width.
|
|
200
|
+
|
|
201
|
+
Also hardens the fill-drag edge-auto-scroll gesture's cell hit-test (`fillDrag.rzts`, shared by the drag-select gesture too): the single-exact-pixel `elementFromPoint` probe used to resolve which cell sits under a stationary pointer while content auto-scrolls beneath it now falls back to a small nearest-cell horizontal search when the exact point misses, making the gesture robust against a narrow column transiting past the fixed probe point within a single animation frame. This is defense-in-depth alongside the width fix above (which was the actual root cause of the one observed user-facing symptom, a fill-drag range that failed to extend past a grouped-header column region during auto-scroll) — behavior is unchanged on every ordinary (non-edge-case) frame.
|
|
202
|
+
- @rozie/runtime-lit@0.7.3
|
|
203
|
+
|
|
204
|
+
## 0.3.2
|
|
205
|
+
|
|
206
|
+
### Patch Changes
|
|
207
|
+
|
|
208
|
+
- Fix eight defects in grouped columns and grouped rows. All six targets.
|
|
209
|
+
|
|
210
|
+
**Nested group leaf columns now resolve their own column definition.** A leaf declared inside a `columns:` group entry was never registered in the internal by-id lookup, so every definition-derived lookup fell back: the `<th>` rendered the raw column id instead of the declared `header`, the column could never be filterable or editable whatever its config said, and the clipboard/fill accessor fell back to the column id instead of the configured `field` — writing to the wrong key. Nested leaves now behave identically to top-level leaves.
|
|
211
|
+
|
|
212
|
+
**A group-header row no longer receives an unrelated record in scoped slots.** The table engine builds each group-header row from its first leaf's record, which the `#cell`, `#selectCell` and `#editor` slots passed straight through as `row` — so a template reading `{{ row.someField }}` painted the first leaf's value on the group line. These slots now receive a group descriptor on group-header rows:
|
|
213
|
+
|
|
214
|
+
```js
|
|
215
|
+
{
|
|
216
|
+
isGroupRow: (true, groupId, groupingColumnId, groupingValue, leafCount);
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
Ordinary rows are unchanged and still receive their record. If your template reads record fields off `row`, guard with `row.isGroupRow` — the previous value on those rows was another row's data, never this one's.
|
|
221
|
+
|
|
222
|
+
**Group-header rows are no longer editable, and bulk writes skip them.** Enter, F2, Space, a printable key, or a click on an editable non-grouping column would open an editor on a group-header row, and committing wrote to the first leaf's record. Paste, cut, clear and fill-drag wrote through the same path. Group rows are now guarded at every edit and write entry point; Enter on a group row toggles the group, as it already did for the grouping column. Copy is unaffected — it still serializes the displayed aggregate.
|
|
223
|
+
|
|
224
|
+
**The group member count now counts records, not nodes.** Under multi-level grouping the count included intermediate sub-group nodes, so a region with 2 sub-groups and 3 records displayed `(5)`. It now displays `(3)`. Single-level grouping is unchanged.
|
|
225
|
+
|
|
226
|
+
**`groupableColumns` (the `#groupBar` slot prop) now offers the right set.** Group columns are no longer offered — a group entry has no accessor and cannot be a grouping key. Groupable leaves nested under a group header are now offered, in declaration order; they were previously omitted entirely. Opt a leaf out with `groupable: false` as before.
|
|
227
|
+
|
|
228
|
+
## 0.3.1
|
|
229
|
+
|
|
230
|
+
### Patch Changes
|
|
231
|
+
|
|
232
|
+
- Performance: `columnDefs()` is now memoized, removing an accidental O(columns² × rows) recompute from the per-cell metadata path.
|
|
233
|
+
|
|
234
|
+
`columnDefs()` rebuilt the entire column-definition array on every call, and its caller chain (`defFor` → `editMetaOf` / `columnEditable` / `editorTypeOf` / `isEditing`) runs once per rendered cell, per render commit. On a 60-column table that is roughly 72,000 column-definition rebuilds per commit — repeated on every frame during a sustained scroll gesture, because the fill-drag edge auto-scroll bumps the window version each frame.
|
|
235
|
+
|
|
236
|
+
The array is now cached on the reference identity of `:columns` and the internal `<Column>` registry — the same two dependencies the component's existing re-feed watch already treats as reference-only. A cache hit is an O(1) identity check; a miss recomputes exactly as before. Output is byte-identical; this is a pure memoization with no behavior or API change.
|
|
237
|
+
|
|
238
|
+
The cost was identical on all six targets, but it was most visible on Vue, where `:columns` and the column registry are reactive proxies and every property read the builder performs pays proxy-tracking overhead on top of the redundant work. Under CPU contention that combination could starve the fill-drag auto-scroll loop badly enough that a drag toward the bottom edge scrolled the viewport without the selection range keeping pace with the newly revealed rows. That is fixed, and the same fix removes the wasted work for every other target too.
|
|
239
|
+
|
|
240
|
+
## 0.3.0
|
|
241
|
+
|
|
242
|
+
### Minor Changes
|
|
243
|
+
|
|
244
|
+
- 877dbdf: Data-table gains column (horizontal) windowing and content-driven auto-measure — the shared windowing engine's two remaining "what Rozie defers" bullets are closed. Combobox picks up the shared engine's new host-contract stubs as dead code, no behavior change. Command-palette republishes in lockstep, with one genuine bug fix on its Lit leaf. The compiler's inliner gets a small tree-shaking fix that this phase's own pure-helper extraction needed.
|
|
245
|
+
|
|
246
|
+
**`data-table` — column windowing.** `virtual` widens from a Boolean to a value grammar: `false` (default, unchanged) | `true` / `'rows'` (unchanged from before — every existing consumer is untouched) | `'columns'` (new: horizontal windowing) | `'both'` (new: both axes). Under column windowing, pinned columns, the active cell's column, and a single in-progress edit's column always stay rendered regardless of scroll position; every header level (including grouped headers) windows on the same slice as the body with a clamped `colSpan`; the dedicated filter row windows the same way; and `focusCell` / `getActiveCell` / `activecell-change` all resolve against the absolute (unwindowed) column index, so off-window columns stay fully addressable through the handle. Fill-drag gets edge auto-scroll on all four container edges — this also closes a pre-existing gap on the row axis, where a fill drag previously could not reach unrendered rows past the top or bottom edge of a row-windowed table either.
|
|
247
|
+
|
|
248
|
+
Two consumer-visible consequences: `virtual='columns'` (and `'both'`) moves the `<table>` inside an `rdt-scroll` `<div>` wrapper, the same wrapper row windowing already uses; and the windowed path applies `table-layout: fixed`, so columns stop auto-fitting their content — size them via `:columnSizing`, a `<Column size>` attribute, or the resize handle. `virtual={false}` and `virtual` unset remain byte-identical to before this change; `virtual={true}` / `'rows'` remains byte-behavior-identical to today's row-only windowing.
|
|
249
|
+
|
|
250
|
+
The `.d.ts` for `virtual` widens from `boolean` to `boolean | string` on all six leaves — a typed-surface change existing consumers with strict TypeScript will see, even though the runtime default is unchanged.
|
|
251
|
+
|
|
252
|
+
**`data-table` — `autoMeasure`.** A new, independent `autoMeasure: Boolean` prop (default `false`). When on, the windowing engine feeds `estimateSize()` a running mean of measured row heights instead of the fixed `estimateRowHeight` seed, so `getTotalSize()` (and the scrollbar it drives) converges toward the true content total on a large table with variable-height rows, instead of staying pinned at `rowCount x estimateRowHeight` forever. The re-feed is hysteresis-gated and anchor-preserving — the topmost rendered row's position holds steady while the estimate refines, so content does not visibly lurch. `estimateRowHeight` is unchanged and un-deprecated: it remains the required first-paint seed (the very first render has zero measurements regardless of `autoMeasure`), and remains the explicit, permanent override when `autoMeasure` is off.
|
|
253
|
+
|
|
254
|
+
**`combobox` — engine only, no behavior change.** Gains the shared windowing engine's new required host-contract one-liners (`rowsWindowed()`, `autoMeasureOn()`) preserving today's exact semantics: `autoMeasureOn()` returns `false`, so `windowing.rzts`'s content-driven-estimate accumulator stays dead code here, exactly as before. (A gap-closure during review removed five _additional_ column-axis stub declarations — `colVirtualizer`, `colsWindowed()`, `columnCount()`, `columnSize()`, `forcedColumns()` — that an earlier draft of this same patch had also added under a mistaken premise about the compiler's tree-shaking requirements; they were genuinely dead code with zero callers, verified by tracing every function combobox imports from `windowing.rzts` back to its body. Their removal is folded into this same patch bump since neither of them has shipped to npm yet — see `87-16-SUMMARY.md`.) No new props, no behavior change, regenerated dist only. (Listbox's source gets the identical mechanical `rowsWindowed()`/`autoMeasureOn()` addition, but listbox has never been published — per standing policy this repo does not version or changelog packages that are not yet on npm, so listbox is not part of this changeset; its addition ships whenever listbox itself is first published.)
|
|
255
|
+
|
|
256
|
+
**`@rozie/core` (and the rest of the toolchain fixed group) — two compiler-level fixes surfaced by this phase.** `@rozie/target-lit` (inlined into `@rozie/core` / `@rozie/cli` / `@rozie/unplugin` / `@rozie/babel-plugin` at build time) fixes a real bug in Lit's emitted output for any `[Boolean, String]` union prop: Lit's built-in `type: Boolean` attribute converter collapses any non-null static attribute value to `true`, silently discarding a string value. `emitNonModelProp()` now emits a custom `converter.fromAttribute` for this prop shape instead. This is the SAME fix that closes `command-palette-lit`'s `appendTo` bug above — one emitter fix, two visible symptoms. Separately, this phase's own `DataTable.rozie` pure-helper extraction (moving framework-agnostic logic into colocated `.ts` helpers) surfaced a real gap in `inlineScriptPartials()`'s tree-shaking: a script partial that only re-exports a name introduced by its own plain-module import (no local declaration body) was not recognized as a valid tree-shake target, silently dropping the backing import and producing a `TS2304` in the emitted leaves. Both are compiler-level fixes; no `.rozie`-author-visible API change.
|
|
257
|
+
|
|
258
|
+
**`command-palette` — lockstep republish, with one genuine fix.** Five of its six leaves have a byte-identical emitted diff from this phase: command-palette composes the _published_ combobox package for its own target at compile time rather than the combobox source, so the shared engine change does not reach those five leaves' bytes at all — this patch keeps them in the same release wave as their combobox peer, per the release-mechanics decision that every published leaf should ride the same windowing engine generation. The Lit leaf is the one exception with a real emitted change: its `appendTo` property's Lit `@property({ type: Boolean })` converter previously discarded a string value (`appendTo="body"`, the documented example) by coercing it to `true` via Lit's default Boolean-attribute converter — the string was silently dropped. It now uses an explicit converter that preserves `true` / `false` / a string value correctly, matching the `[Boolean, String]` union type the prop has always declared.
|
|
259
|
+
|
|
260
|
+
### Patch Changes
|
|
261
|
+
|
|
262
|
+
- @rozie/runtime-lit@0.7.2
|
|
263
|
+
|
|
264
|
+
## 0.2.7
|
|
265
|
+
|
|
266
|
+
### Patch Changes
|
|
267
|
+
|
|
268
|
+
- 287dbf2: All six `@rozie-ui/data-table-<target>` leaves widen their `@rozie-ui/popover-<target>` peer dependency from `^0.1.0` to `^0.1.0 || ^0.2.0`.
|
|
269
|
+
|
|
270
|
+
**Why both, not just the new one.** Combobox moves its own popover peer to `^0.2.0` in this same wave. A caret range on a 0.x version pins the minor, so leaving data-table's range at `^0.1.0` would give any consumer installing both the combobox family and the data-table family two mutually exclusive ranges for the same `@rozie-ui/popover-<target>` package — an unsatisfiable peer pair. Forcing data-table forward to `^0.2.0` alone would avoid that conflict but strand existing data-table consumers on a popover upgrade they have no reason to take, since data-table does not use any of popover's five new props. Admitting both ranges is the option that resolves the conflict without an unnecessary forced upgrade.
|
|
271
|
+
|
|
272
|
+
**Why it is safe to admit both.** Data-table composes a `<Popover>` at exactly two sites in source (`DataTable.rozie`), and both are byte-identical, binding only four props: `trigger="click"`, `placement="bottom-end"`, `strategy="fixed"`, `:offset="4"`. All four are present, unchanged, in both `0.1.x` and `0.2.0` — data-table binds none of popover's five new props (`bare`, `disablePositioning`, `keepMounted`, `matchWidth`, `disableDismiss`). The one behavioral change in this wave that touches existing consumers — `aria-haspopup`/`aria-expanded` gating on `hasGestureTrigger()` — only affects `trigger="manual"` popovers; data-table's `trigger="click"` stays on the gesture-trigger branch and sees no ARIA change in either version.
|
|
273
|
+
|
|
274
|
+
**Scope of the change.** There is no runtime, API, or DOM change in `@rozie-ui/data-table-<target>` itself. The entire leaf diff is the one peer dependency range line per target, six lines total. This changeset is deliberately separate from the popover-promotion changeset covering combobox and popover: it is its own story about data-table's peer contract, not a restatement of theirs.
|
|
275
|
+
- @rozie/runtime-lit@0.7.1
|
|
276
|
+
|
|
277
|
+
## 0.2.6
|
|
278
|
+
|
|
279
|
+
### Patch Changes
|
|
280
|
+
|
|
281
|
+
- 6943820: Lit and Angular dropped every leading comment on a top-level declaration promoted into
|
|
282
|
+
the component class — 1370 apiece across the shipped corpus. Both emitters build each
|
|
283
|
+
class member as a hand-built string (`generate(decl)` / `renderExpression` / a rebuilt
|
|
284
|
+
arrow or `t.classMethod`), and none of those carries the STATEMENT's own comments, so an
|
|
285
|
+
author's documentation simply vanished from the emitted component.
|
|
286
|
+
|
|
287
|
+
Both now run a printed-comment ledger keyed on comment OBJECT IDENTITY. Identity rather
|
|
288
|
+
than source offsets is load-bearing: a `.rzts` script partial is parsed as its own file,
|
|
289
|
+
so its comment offsets collide with unrelated host comments. A per-branch rule cannot
|
|
290
|
+
work here at all, because @babel/parser attaches a comment sitting BETWEEN two statements
|
|
291
|
+
to BOTH neighbours at once — whichever side a local rule picks, the other side either
|
|
292
|
+
double-prints it or drops it.
|
|
293
|
+
|
|
294
|
+
Three properties this needed, each found by measuring the corpus rather than by reading
|
|
295
|
+
code:
|
|
296
|
+
|
|
297
|
+
**It looks back, not just down.** Each statement claims the PREVIOUS statement's
|
|
298
|
+
still-unclaimed trailing comments as well as its own leading ones, rendering both above
|
|
299
|
+
its member. Inline, one parse hands the same comment object to both sides, so it prints
|
|
300
|
+
once. Across a `.rzts` splice boundary the successor comes from a different parse with
|
|
301
|
+
nothing attached, and the previous statement's trailing side is the only place the
|
|
302
|
+
comment exists. Without this the inline host printed a comment the partial-inlined host
|
|
303
|
+
could not, and the partial-vs-inline byte-identity guards went red.
|
|
304
|
+
|
|
305
|
+
**The ledger spans the import block.** A comment between the last import and the first
|
|
306
|
+
promoted declaration is printed by the module-scope import generation — a separate
|
|
307
|
+
printer with its own dedup set. Unseeded, 132 comments printed twice on Lit and 155 on
|
|
308
|
+
Angular. Seeding from every comment merely ATTACHED to an import node over-corrected and
|
|
309
|
+
lost 16, since a comment can hang off a node the block never prints; the seed is taken
|
|
310
|
+
from what the block actually emitted.
|
|
311
|
+
|
|
312
|
+
**It unclaims.** A statement can be consumed by another pass — a `$computed`, a lifecycle
|
|
313
|
+
hook, a `$provide` directive — and produce no class member at all. When the flush finds
|
|
314
|
+
no target it releases the claim so whichever printer does emit that statement still
|
|
315
|
+
renders its comments. Claiming without emitting is how a ledger silently drops comments,
|
|
316
|
+
which is strictly worse than double-printing, and this is why both targets report zero
|
|
317
|
+
lost despite several statement kinds never reaching a ledger-owned array.
|
|
318
|
+
|
|
319
|
+
Net effect: 5311 comments restored across 53 Lit leaves and 5266 across the Angular
|
|
320
|
+
leaves, with ZERO comments dropped and ZERO non-comment bytes changed, plus 16
|
|
321
|
+
pre-existing double-prints fixed on each target (a comment that had been emitted both at
|
|
322
|
+
module scope and again inside the mount hook). Verified by parsing every file before and
|
|
323
|
+
after, comparing the parser's own comment list as a multiset, and comparing
|
|
324
|
+
`generate(ast, { comments: false })` on both sides — never by reading the diff.
|
|
325
|
+
|
|
326
|
+
Emitted code is unchanged in every case; this is documentation fidelity only.
|
|
327
|
+
|
|
328
|
+
Eighteen further Lit/Angular leaves drifted the same comment-only way but are
|
|
329
|
+
deliberately absent from the front matter — dialog, lexical, listbox, maplibre,
|
|
330
|
+
number-field, pagination, resizable, slider and switch (both targets) are all in
|
|
331
|
+
`.changeset/config.json`'s `ignore` list, and listing an ignored package beside a
|
|
332
|
+
non-ignored one makes `changeset status` fail outright.
|
|
333
|
+
|
|
334
|
+
- Updated dependencies [dcc3336]
|
|
335
|
+
- @rozie/runtime-lit@0.7.0
|
|
336
|
+
|
|
337
|
+
## 0.2.5
|
|
338
|
+
|
|
339
|
+
### Patch Changes
|
|
340
|
+
|
|
341
|
+
- @rozie/runtime-lit@0.6.0
|
|
342
|
+
|
|
343
|
+
## 0.2.4
|
|
344
|
+
|
|
345
|
+
### Patch Changes
|
|
346
|
+
|
|
347
|
+
- `@rozie/runtime-lit` gains a new public export, `RozieSlotDistributor` — a reactive
|
|
348
|
+
controller that performs manual slot assignment for a Lit shadow root, used wherever a
|
|
349
|
+
component needs to route projected children into loop-generated `<slot>` targets
|
|
350
|
+
(e.g. carousel slides). Adopting manual `slotAssignment` turns OFF the browser's
|
|
351
|
+
automatic Text-node projection for that shadow root: raw text children must now be
|
|
352
|
+
assigned to a slot explicitly, and any code reading `assignedNodes()` needs to guard
|
|
353
|
+
for the manual-assignment case. A host that adopts the controller needs its
|
|
354
|
+
`shadowRootOptions` typed as `ShadowRootInit`.
|
|
355
|
+
|
|
356
|
+
`@rozie/core` adds two new compile-time diagnostics and one new template sigil:
|
|
357
|
+
- **ROZ148** — flags a prop whose name collides with an emitted callback name, before
|
|
358
|
+
it becomes a runtime shadowing bug on a target that lowers the prop to a method.
|
|
359
|
+
- **ROZ210** — flags a reserved slot name so it can't silently collide with an
|
|
360
|
+
internally-generated one.
|
|
361
|
+
- **`$slotted`** — a new member sigil authors can read inside a loop to get the live,
|
|
362
|
+
reactively-assigned elements projected into that iteration's slot.
|
|
363
|
+
|
|
364
|
+
`@rozie/{cli,unplugin,babel-plugin}` bundle `@rozie/core`'s compiler, so this release
|
|
365
|
+
carries the same diagnostics and `$slotted` lowering through to every consumer of
|
|
366
|
+
those packages — the compiler itself moved even though none of these three changed
|
|
367
|
+
their own source. `$slotted` lowers to a reactive assigned-elements signal on the Lit
|
|
368
|
+
target (backed by `RozieSlotDistributor`, gated behind a new `shouldDistributeSlots`
|
|
369
|
+
check so it only emits where a loop actually needs manual slot assignment) and to a
|
|
370
|
+
plain `[]` on the five hostless targets (React, Vue, Svelte, Solid, Angular), where
|
|
371
|
+
there's no shadow root to distribute into.
|
|
372
|
+
|
|
373
|
+
`@rozie/runtime-{react,vue,svelte,solid,keynav-core}` are version-aligned to 0.5.0 by
|
|
374
|
+
the changesets `fixed` group riding the `runtime-lit` minor above — this is a
|
|
375
|
+
version-alignment release only; none of these five packages has a source or behavior
|
|
376
|
+
change in this wave.
|
|
377
|
+
|
|
378
|
+
`@rozie-ui/date-picker-*` (all six targets) — day and caption labels are now derived
|
|
379
|
+
from `Intl`, with a new `labels` prop for overriding them; range-span selections now
|
|
380
|
+
validate against `disabled` dates; and the calendar header adds drill-in/drill-out
|
|
381
|
+
navigation verbs.
|
|
382
|
+
|
|
383
|
+
`@rozie-ui/embla-*` (all six targets) adopts `$slotted` for its carousel slides. On
|
|
384
|
+
the Lit target specifically, this closes a real projection gap: raw `slot="slide"`
|
|
385
|
+
children are now distributed per-iteration instead of only the first iteration
|
|
386
|
+
claiming them.
|
|
387
|
+
|
|
388
|
+
`@rozie-ui/{combobox,command-palette,data-table,sortable-list,tags,toast}-lit` are
|
|
389
|
+
regenerated against the new Lit emitter output above — each already used a loop-slot
|
|
390
|
+
pattern that now runs through `RozieSlotDistributor` / `$slotted` instead of the prior
|
|
391
|
+
ad hoc approach, with no observable behavior change for existing consumers of these
|
|
392
|
+
specific leaves.
|
|
393
|
+
|
|
394
|
+
- Updated dependencies
|
|
395
|
+
- @rozie/runtime-lit@0.5.0
|
|
396
|
+
|
|
397
|
+
## 0.2.3
|
|
398
|
+
|
|
399
|
+
### Patch Changes
|
|
400
|
+
|
|
401
|
+
- Regenerated against `@rozie/core@0.3.0`. `r-for` loop keys are no longer leaked as literal DOM attributes on emitted elements. No API surface change.
|
|
402
|
+
|
|
403
|
+
## 0.2.2
|
|
404
|
+
|
|
405
|
+
### Patch Changes
|
|
406
|
+
|
|
407
|
+
- Updated dependencies [c279a7e]
|
|
408
|
+
- @rozie/runtime-lit@0.2.1
|
|
409
|
+
|
|
410
|
+
## 0.2.1
|
|
411
|
+
|
|
412
|
+
### Patch Changes
|
|
413
|
+
|
|
414
|
+
- Updated dependencies [364f4c5]
|
|
415
|
+
- @rozie/runtime-lit@0.2.0
|
|
416
|
+
|
|
417
|
+
## 0.2.0
|
|
418
|
+
|
|
419
|
+
### Minor Changes
|
|
420
|
+
|
|
421
|
+
- 1a2e30c: data-table composes `@rozie-ui/popover` via the published-package model (Option A) instead of vendoring its source.
|
|
422
|
+
|
|
423
|
+
The header column `⋯` menu previously vendored the popover primitive's source into each data-table leaf. It now resolves the published `@rozie-ui/popover-<target>` package at compile time (via the schema-versioned manifest), and each data-table leaf declares `@rozie-ui/popover-<target>` (`^0.1.0`) as a required runtime **peerDependency**.
|
|
424
|
+
|
|
425
|
+
**Consumer action:** install the matching popover leaf alongside data-table, e.g. `@rozie-ui/popover-react` for `@rozie-ui/data-table-react`. (`@floating-ui/dom` is no longer a direct data-table peer — it graduates to the popover leaf's own peer.)
|
|
426
|
+
|
|
427
|
+
Also fixes `@rozie-ui/data-table-lit` `sideEffects` (was a css-only allowlist that left the `customElements.define(...)` registrations for `rozie-data-table` / `rozie-column` / `rozie-editor-*` / `rozie-filter-*` unprotected against production tree-shaking).
|
|
428
|
+
|
|
429
|
+
**Release ordering:** `@rozie-ui/popover-<target>` must be published to npm before this release, since it is now a required peer of every data-table leaf.
|
package/README.md
CHANGED
|
@@ -454,23 +454,23 @@ import '@rozie-ui/data-table-lit/themes/shadcn.css'; // or material.css, boot
|
|
|
454
454
|
|
|
455
455
|
| Event | Description |
|
|
456
456
|
| --- | --- |
|
|
457
|
-
| `
|
|
457
|
+
| `activecell-change` | Fired (grid interaction mode only) whenever the active cell moves — by keyboard navigation or a `focusCell` call. One-way notification; payload is `{ rowIndex, colIndex }` integers over the visible model. Fires on every move including arrow keypresses. |
|
|
458
|
+
| `cell-edit-commit` | Fired when an editable cell commits a NEW, validated value — once per committed cell, from the single `commitEdit` call site (a rejected/invalid commit fires nothing). Pairs with the two-way `data` write (one fresh-array replace per commit). Payload is `{ rowId, columnId, oldValue, newValue }`. `cancel`/`Escape` and a validation failure emit nothing. |
|
|
458
459
|
| `expand-change` | Fired when the expanded-row set changes (an expander chevron toggle — click / Enter / Space — or a `toggleRowExpanded`/`expandAll`/`collapseAll` call). Fires exactly once per change (the echo-guarded write funnel dedups the React multi-render re-entry) and REGARDLESS of whether the two-way `expanded` model is bound. Payload is the fresh `ExpandedState` — a `{ [rowId]: true }` object, or the `true` literal after `expandAll` (passed through verbatim — never `Object.keys` it without a `=== true` guard). Named `expand-change` (not `expanded-change`): the model:true `expanded` prop owns the `onExpandedChange` callback on the React/Solid Props interface, so the event stems off a distinct name to avoid a duplicate-identifier collision (the house convention every slice follows). |
|
|
459
|
-
| `group-change` | Fired when the grouping state changes (a consumer-built `#groupBar` apply/clear, mutating the `grouping` model, or an `applyGrouping`/`clearGrouping` call). Fires exactly once per change (the echo-guarded write funnel dedups the React multi-render re-entry) and REGARDLESS of whether the two-way `grouping` model is bound. Payload is the fresh `GroupingState` — an ORDERED `string[]` of column ids (e.g. `["region","category"]` for nested groups), or `[]` when cleared. Named `group-change` (not `grouping-change`): the model:true `grouping` prop owns the `onGroupingChange` callback on the React/Solid Props interface, so the event stems off a distinct name to avoid a duplicate-identifier collision (the house convention every slice follows — sorting→sort-change, expanded→expand-change). |
|
|
460
460
|
| `filter-change` | Fired when a filter changes. Payload is `{ globalFilter }` for the global search box or `{ columnFilters }` (the fresh `ColumnFiltersState` `[{ id, value }]`) for a per-column filter — both surface through this one event. |
|
|
461
|
+
| `group-change` | Fired when the grouping state changes (a consumer-built `#groupBar` apply/clear, mutating the `grouping` model, or an `applyGrouping`/`clearGrouping` call). Fires exactly once per change (the echo-guarded write funnel dedups the React multi-render re-entry) and REGARDLESS of whether the two-way `grouping` model is bound. Payload is the fresh `GroupingState` — an ORDERED `string[]` of column ids (e.g. `["region","category"]` for nested groups), or `[]` when cleared. Named `group-change` (not `grouping-change`): the model:true `grouping` prop owns the `onGroupingChange` callback on the React/Solid Props interface, so the event stems off a distinct name to avoid a duplicate-identifier collision (the house convention every slice follows — sorting→sort-change, expanded→expand-change). |
|
|
462
|
+
| `history-change` | Fired when undo/redo AVAILABILITY changes (one unified grid-wide undo/redo stack, not scoped per-cell or per-row) — EDGE-triggered (only when `canUndo`/`canRedo` actually flip, not on every recorded edit) so a toolbar can enable/disable its buttons without polling. Only meaningful when `undoable` is `true`. Payload is `{ canUndo, canRedo }` booleans. No `history` model prop exists, so there is no model-prop==emit-name duplicate-identifier collision. |
|
|
461
463
|
| `page-change` | Fired when pagination changes (prev/next, a page-size change, or a `setPage`/`setRowsPerPage` call). Payload is the fresh `{ pageIndex, pageSize }` object. |
|
|
462
|
-
| `selection-change` | Fired when the row selection changes (a row/select-all checkbox toggle or a `toggleAllRows`/`clearSelection` call). Payload is the fresh `RowSelectionState` `{ [rowId]: true }` object. |
|
|
463
|
-
| `visibility-change` | Fired when a column is shown/hidden (the column-toggle menu or a `toggleColumnVisibility` call). Payload is the fresh `VisibilityState` `{ [colId]: boolean }` object. |
|
|
464
|
-
| `resize-change` | Fired live during a column resize drag (`columnResizeMode: "onChange"`). Payload is the fresh `ColumnSizingState` `{ [colId]: number }` object. |
|
|
465
|
-
| `reorder-change` | Fired when the column order changes. Column reorder is programmatic only — an `applyColumnOrder` call or a two-way `columnOrder` write; the component ships NO drag or menu affordance for it. Payload is the fresh `ColumnOrderState` `string[]`. |
|
|
466
464
|
| `pin-change` | Fired when a column is pinned/unpinned (the per-header pin buttons or a `pinColumn` call). Payload is the fresh `ColumnPinningState` `{ left: string[], right: string[] }` object. |
|
|
467
|
-
| `history-change` | Fired when undo/redo AVAILABILITY changes (one unified grid-wide undo/redo stack, not scoped per-cell or per-row) — EDGE-triggered (only when `canUndo`/`canRedo` actually flip, not on every recorded edit) so a toolbar can enable/disable its buttons without polling. Only meaningful when `undoable` is `true`. Payload is `{ canUndo, canRedo }` booleans. No `history` model prop exists, so there is no model-prop==emit-name duplicate-identifier collision. |
|
|
468
|
-
| `visible-range-change` | Fired when the RENDERED row window changes under row windowing (`virtual`) — the window the table actually renders, overscan included — and once after mount. Payload `{ start, end }` over the full row space (`end` exclusive; with lazy rows that space is `rowCount` long). Deduped: a scroll that does not move the window emits nothing. Pair with `manual` + `rowCount` and sparse `data` to fetch the rows the viewport needs. |
|
|
469
|
-
| `activecell-change` | Fired (grid interaction mode only) whenever the active cell moves — by keyboard navigation or a `focusCell` call. One-way notification; payload is `{ rowIndex, colIndex }` integers over the visible model. Fires on every move including arrow keypresses. |
|
|
470
|
-
| `row-activate` | Fired when the user opens a row (list-style "open this item" — oinbox's email thread). A plain click on a body data row, outside any control in the clicked cell (the selection checkbox, the expander, links, inputs, drop-in widgets and an open editor do NOT activate), in both interaction modes; not a shift/ctrl/meta click (selection gestures), the end of a drag-select, or a `singleClickEdit` click on an editable grid cell (that opens the editor). In grid mode also Enter on a non-editable cell that has no controls of its own (Enter on an editable cell still begins the edit; Enter on a cell with controls still enters them). Group-header and detail rows never activate. Payload is `{ row, index, trigger }` — `row` the original data object, `index` its position in the rendered model (page-relative; the full model when virtual), `trigger` `'click'` or `'keyboard'`. |
|
|
471
465
|
| `range-change` | Fired whenever the rectangular cell-range selection changes — extended by `Shift+Arrow` / `Shift+Click` from the single `extendRange`/`setRangeFocus` call sites (React multi-emit dedup). One-way notification (the range is NOT a `model:true` slice — the model:true count stays at 12, leaving the Angular multi-model-CVA condition untouched); it is a SEPARATE layer from the row-selection slice and the two never corrupt each other. Payload is `getSelectedRange()` → `{ anchor, focus }` where each corner is a `{ rowIndex, colIndex }` index pair over the visible model (integers only — no row data, no DOM node), or `{ anchor: null, focus: null }` when no range is set. |
|
|
472
|
-
| `
|
|
466
|
+
| `reorder-change` | Fired when the column order changes. Column reorder is programmatic only — an `applyColumnOrder` call or a two-way `columnOrder` write; the component ships NO drag or menu affordance for it. Payload is the fresh `ColumnOrderState` `string[]`. |
|
|
467
|
+
| `resize-change` | Fired live during a column resize drag (`columnResizeMode: "onChange"`). Payload is the fresh `ColumnSizingState` `{ [colId]: number }` object. |
|
|
468
|
+
| `row-activate` | Fired when the user opens a row (list-style "open this item" — oinbox's email thread). A plain click on a body data row, outside any control in the clicked cell (the selection checkbox, the expander, links, inputs, drop-in widgets and an open editor do NOT activate), in both interaction modes; not a shift/ctrl/meta click (selection gestures), the end of a drag-select, or a `singleClickEdit` click on an editable grid cell (that opens the editor). In grid mode also Enter on a non-editable cell that has no controls of its own (Enter on an editable cell still begins the edit; Enter on a cell with controls still enters them). Group-header and detail rows never activate. Payload is `{ row, index, trigger }` — `row` the original data object, `index` its position in the rendered model (page-relative; the full model when virtual), `trigger` `'click'` or `'keyboard'`. |
|
|
473
469
|
| `row-edit-commit` | Fired when a FULL-ROW edit commits — once per row save, from the single `commitRow` call site (React multi-emit dedup). A row enters full-row edit via `Shift+F2` or the `editRow` verb (every editable cell edits at once); one save validates every edited cell (a single failure blocks the whole row and emits nothing) then writes the two-way `data` model ONCE (a single fresh-array row-object replace with all changes applied) and fires this event ONCE. `Escape` reverts the whole row as a unit and emits nothing. Payload is `{ rowId, changes }` where `changes` is `[{ columnId, oldValue, newValue }]` for ONLY the columns whose value actually changed. |
|
|
470
|
+
| `selection-change` | Fired when the row selection changes (a row/select-all checkbox toggle or a `toggleAllRows`/`clearSelection` call). Payload is the fresh `RowSelectionState` `{ [rowId]: true }` object. |
|
|
471
|
+
| `sort-change` | Fired when the sort state changes (header click / shift-click multi-sort / a `sortColumn`/`clearSorting` call). Payload is the fresh `SortingState` array `[{ id, desc }]`. |
|
|
472
|
+
| `visibility-change` | Fired when a column is shown/hidden (the column-toggle menu or a `toggleColumnVisibility` call). Payload is the fresh `VisibilityState` `{ [colId]: boolean }` object. |
|
|
473
|
+
| `visible-range-change` | Fired when the RENDERED row window changes under row windowing (`virtual`) — the window the table actually renders, overscan included — and once after mount. Payload `{ start, end }` over the full row space (`end` exclusive; with lazy rows that space is `rowCount` long). Deduped: a scroll that does not move the window emits nothing. Pair with `manual` + `rowCount` and sparse `data` to fetch the rows the viewport needs. |
|
|
474
474
|
|
|
475
475
|
## Imperative handle
|
|
476
476
|
|