@iyulab/flex-table 0.55.2 → 0.56.1
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 +58 -0
- package/README.md +20 -9
- package/dist/core/editing.d.ts +7 -1
- package/dist/core/row-selection.d.ts +28 -17
- package/dist/events.d.ts +6 -0
- package/dist/{flex-table-D3E9npd9.js → flex-table-DKBcfNA4.js} +294 -183
- package/dist/flex-table.d.ts +63 -15
- package/dist/flex-table.js +1 -1
- package/dist/react.js +1 -1
- package/package.json +1 -1
- package/skills/iyulab-flex-table/SKILL.md +6 -3
- package/skills/iyulab-flex-table/references/api.md +11 -8
- package/skills/iyulab-flex-table/references/react.md +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,63 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.56.1] - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **An edit is written to the row it was started on.** The open editor remembered a screen position and the commit
|
|
8
|
+
wrote whatever row stood there, so a refresh that put a row above the one being edited (or a sort while editing)
|
|
9
|
+
sent the typed value into another row. The editor now follows its row — drawn again in the row's new place with
|
|
10
|
+
what was typed and the focus — and an edit whose row left `data` (or a filter hid) is cancelled.
|
|
11
|
+
- **Undo and redo write the rows they changed** (cell edits, `updateRows`, paste, clear, fill, replace). They wrote
|
|
12
|
+
`data` positions, so after `data` changed in between they changed other rows.
|
|
13
|
+
- **Comments stay on their rows.** They were kept by `data` index, so inserting or deleting a row moved every comment
|
|
14
|
+
below it onto the neighbouring row. A deleted row's comments leave with it (and come back on undo).
|
|
15
|
+
- **Validation marks stay on their cells** when the view reorders while one is shown.
|
|
16
|
+
- `cell-edit-cancel` reports `row` as the data index, like the other edit events (it was the screen position) —
|
|
17
|
+
`-1` when the row is no longer in `data`.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `comment-change` and `getAllComments()` carry the row's `id`.
|
|
22
|
+
|
|
23
|
+
## [0.56.0] - 2026-10-07
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **Row selection stays on its rows.** The checkmarks were kept by screen position, so sorting, filtering, inserting
|
|
28
|
+
or deleting rows moved them onto other rows without a `selection-change` — select a row, sort, and a different row
|
|
29
|
+
was selected (and a bulk action took it). Replacing `data` with other rows of the same length carried the
|
|
30
|
+
checkmarks onto the new rows. Selection is now kept by row id (see `rowKey` below).
|
|
31
|
+
- **`deleteRows()` without indices deletes the checked rows in a `selectable` grid.** It deleted the rows of the
|
|
32
|
+
cell selection (the active cell's row), so a «Delete selected» button removed a row nobody checked. With nothing
|
|
33
|
+
checked it now deletes nothing. A grid without checkboxes keeps deleting the cell selection's rows.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- **`rowKey`** (`row-key`, default `'_id'` — the field `u-rich-table` reads) — the field, or a function of the row,
|
|
38
|
+
that names a row. A keyed row stays selected when `data` is replaced, so a selection spans server pages. A row
|
|
39
|
+
without a key is named by the row object (a session-local id `#n`) and leaves the selection when `data` no longer
|
|
40
|
+
holds it.
|
|
41
|
+
- `getRowId(row)`, the `selectedRowIds` getter and `setSelection(ids)` (does nothing, and fires nothing, when the
|
|
42
|
+
selection already is `ids`).
|
|
43
|
+
- `selection-change` carries `selectedIds` — every selected row, on every page — next to `selectedRows` and
|
|
44
|
+
`selectedIndices` (the selected rows `data` holds). `row-activate` carries the row's `id`.
|
|
45
|
+
|
|
46
|
+
### Changed (breaking)
|
|
47
|
+
|
|
48
|
+
- The header checkbox and `selectAll()` act on the rows in view: rows selected on another page stay selected, and
|
|
49
|
+
unchecking the header deselects only the rows in view. `deselectAll()` still clears every page. A selected row a
|
|
50
|
+
filter hides stays selected (and in `selectedRows`).
|
|
51
|
+
- `RowSelectionState` (exported) keeps ids: `isSelected(id)`, `toggle(id)`, `select(id)`, `selectAll(ids)`,
|
|
52
|
+
`deselectMany(ids)`, `set(ids)`, `retain(keep)`, `selectRange(orderedIds, from, to)`, `isAllSelected(ids)`,
|
|
53
|
+
`isSomeSelected(ids)`, `selectedIds`. `setRowCount` and `selectedIndices` are gone.
|
|
54
|
+
- `clear-selection-on-data-change` keeps its meaning; it is no longer needed to stop a replacement from moving the
|
|
55
|
+
checkmarks.
|
|
56
|
+
|
|
57
|
+
**Migrating**: give rows a stable key (`_id`, or set `rowKey`) when selection should survive a reload or a page change.
|
|
58
|
+
Read `selectedIds` for «what is selected» across pages, `selectedRows` for the rows on hand. A «Delete selected» button
|
|
59
|
+
on a `selectable` grid can keep calling `deleteRows()`.
|
|
60
|
+
|
|
3
61
|
## [0.55.2] - 2026-10-07
|
|
4
62
|
|
|
5
63
|
### Documentation
|
package/README.md
CHANGED
|
@@ -108,6 +108,7 @@ guarantee about a *constrained* host. `height-model.browser.test.ts` pins both s
|
|
|
108
108
|
| `maxRows` | `max-rows` | `number` | `0` | Max row count (0 = unlimited); blocks `addRow()` and paste expansion |
|
|
109
109
|
| `maxUndoSize` | `max-undo-size` | `number` | `100` | Max undo history stack size |
|
|
110
110
|
| `selectable` | `selectable` | `boolean` | `false` | Enable row-level checkbox selection |
|
|
111
|
+
| `rowKey` | `row-key` | `string \| (row) => unknown` | `'_id'` | What names a row — selection is kept by this id (see [Row Selection](#row-selection)) |
|
|
111
112
|
| `selectionMode` | `selection-mode` | `'single' \| 'multi'` | `'multi'` | Row selection mode |
|
|
112
113
|
| `dataMode` | `data-mode` | `'client' \| 'server'` | `'client'` | Client-side or server-side data processing |
|
|
113
114
|
| `footerData` | `footer-data` | `Record<string, string \| TemplateResult> \| null` | `null` | Footer/summary row data (keys match column keys) |
|
|
@@ -235,7 +236,7 @@ Every row keeps its own value. Sorting, filtering, copying, CSV/XLSX export and
|
|
|
235
236
|
| Method | Returns | Description |
|
|
236
237
|
|--------|---------|-------------|
|
|
237
238
|
| `addRow(row?, index?)` | `DataRow \| null` | Add a row. Returns `null` if `maxRows` reached |
|
|
238
|
-
| `deleteRows(indices?)` | `void` | Delete rows by data index
|
|
239
|
+
| `deleteRows(indices?)` | `void` | Delete rows by data index. Without indices: the checked rows when `selectable` (none checked — nothing), otherwise the rows of the cell selection |
|
|
239
240
|
| `updateRows(changes)` | `void` | Batch update cells as single undo action. `changes: Array<{ row, key, value }>` |
|
|
240
241
|
| `refreshData()` | `void` | Force re-render after in-place data mutation |
|
|
241
242
|
|
|
@@ -253,11 +254,21 @@ Every row keeps its own value. Sorting, filtering, copying, CSV/XLSX export and
|
|
|
253
254
|
|
|
254
255
|
| Method | Returns | Description |
|
|
255
256
|
|--------|---------|-------------|
|
|
256
|
-
| `selectAll()` | `void` | Select
|
|
257
|
-
| `deselectAll()` | `void` | Deselect
|
|
258
|
-
| `
|
|
257
|
+
| `selectAll()` | `void` | Select every row in view, after filtering (multi mode only). Rows on other pages stay as they are |
|
|
258
|
+
| `deselectAll()` | `void` | Deselect every row, on every page |
|
|
259
|
+
| `selectWhere(predicate)` | `void` | Add the rows in view for which `predicate(row, dataIndex)` is true |
|
|
260
|
+
| `setSelection(ids)` | `void` | Replace the selection with these row ids. The same set again does nothing and fires nothing |
|
|
261
|
+
| `getSelectedRows()` | `{ selectedIds, selectedIndices, selectedRows }` | `selectedIds`: every selected row (all pages). `selectedRows` / `selectedIndices`: the selected rows `data` holds, and their positions in it |
|
|
262
|
+
| `getRowId(row)` | `string` | The id selection keeps for a row |
|
|
263
|
+
| `selectedRowIds` (getter) | `ReadonlySet<string>` | Ids of the selected rows, all pages (a copy) |
|
|
259
264
|
|
|
260
|
-
Row selection is
|
|
265
|
+
Row selection is kept by **row id**, not by position, so a checkmark stays on its row through sorting, filtering, inserts and deletes. The id is the row's `row-key` field — `_id` by default, the same field `u-rich-table` reads — or the result of a `rowKey` function:
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
table.rowKey = 'orderNo'; // or: table.rowKey = (row) => `${row.site}/${row.no}`
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
A keyed row stays selected when `data` is replaced, so in a server-paged grid the selection spans pages (`selectedIds` names them all; `selectedRows` holds the ones on this page). A row without a key is named by the row object itself (a session-local id `#n`): it follows the row while the same object is in `data` and leaves the selection when it is gone. Set `clear-selection-on-data-change` when every new `data` should start a new selection (a new search):
|
|
261
272
|
|
|
262
273
|
```html
|
|
263
274
|
<flex-table selectable clear-selection-on-data-change></flex-table>
|
|
@@ -295,7 +306,7 @@ type-checks without a cast. The React wrapper's `on*` props carry the same types
|
|
|
295
306
|
| `cell-select` | `{ row, col }` or `null` | Cell focus changed (`null` when no cell is active) |
|
|
296
307
|
| `cell-edit-start` | `{ row, col, key, value }` | Cell editing started |
|
|
297
308
|
| `cell-edit-commit` | `{ row, col, key, oldValue, newValue }` | Cell value committed |
|
|
298
|
-
| `cell-edit-cancel` | `{ row, col }` | Cell edit cancelled (Escape) |
|
|
309
|
+
| `cell-edit-cancel` | `{ row, col }` | Cell edit cancelled (Escape, or its row left `data`). `row` is the data index (`-1` once the row is gone) |
|
|
299
310
|
| `validation-error` | `{ row, col, key, value, error }` | Cell validator rejected value, or a `number` cell got text that is not a number |
|
|
300
311
|
|
|
301
312
|
### Data Events
|
|
@@ -304,13 +315,13 @@ type-checks without a cast. The React wrapper's `on*` props carry the same types
|
|
|
304
315
|
|-------|--------|-------------|
|
|
305
316
|
| `row-add` | `{ row, index }` | Row added |
|
|
306
317
|
| `row-delete` | `{ indices, rows }` | Rows deleted |
|
|
307
|
-
| `row-activate` | `{ row, index, col, key }` | Enter pressed on a non-editable cell — the grid's own contract for "activate this row" (e.g. navigate to a detail view), guaranteed even though the internal Enter handler prevents the keystroke from reliably reaching a listener the host attaches to the same element |
|
|
318
|
+
| `row-activate` | `{ row, id, index, col, key }` | Enter pressed on a non-editable cell — the grid's own contract for "activate this row" (e.g. navigate to a detail view), guaranteed even though the internal Enter handler prevents the keystroke from reliably reaching a listener the host attaches to the same element |
|
|
308
319
|
| `batch-update` | `{ changes: [{ row, key, oldValue, newValue }] }` | Batch update applied |
|
|
309
320
|
| `row-reorder` | `{ from, to }` | Row dragged to a new place (data indices) |
|
|
310
321
|
| `data-import` | `{ count }` | Rows imported from a file |
|
|
311
322
|
| `fill-handle-apply` | `{ sourceRange, targetRange, cells }` | Fill handle wrote `cells` (`{ dataRow, key, oldValue, newValue }`) |
|
|
312
323
|
| `find-replace` | `{ type, cells }` | Replace (`type: 'replace'`) or replace-all from the find panel; `cells` are `{ row, col, oldValue, newValue }` with `col` the column key |
|
|
313
|
-
| `comment-change` | `{ dataIndex, colKey, text }` | Cell comment set, changed or removed (`text: null`) |
|
|
324
|
+
| `comment-change` | `{ dataIndex, id, colKey, text }` | Cell comment set, changed or removed (`text: null`). Comments stay on their rows (`id`) when rows move |
|
|
314
325
|
|
|
315
326
|
### Column Events
|
|
316
327
|
|
|
@@ -336,7 +347,7 @@ type-checks without a cast. The React wrapper's `on*` props carry the same types
|
|
|
336
347
|
|
|
337
348
|
| Event | Detail | Description |
|
|
338
349
|
|-------|--------|-------------|
|
|
339
|
-
| `selection-change` | `{ selectedIndices, selectedRows }` | Row checkbox selection changed |
|
|
350
|
+
| `selection-change` | `{ selectedIds, selectedIndices, selectedRows }` | Row checkbox selection changed — `selectedIds` covers every page, `selectedRows` the rows `data` holds |
|
|
340
351
|
|
|
341
352
|
### Clipboard Events
|
|
342
353
|
|
package/dist/core/editing.d.ts
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { CellPosition } from './selection.js';
|
|
2
|
+
import type { DataRow } from '../models/types.js';
|
|
2
3
|
export interface EditState {
|
|
4
|
+
/** Where the editor is drawn — the visual cell. The grid moves it when the view moves the row. */
|
|
3
5
|
position: CellPosition;
|
|
4
6
|
originalValue: unknown;
|
|
7
|
+
/** The row being edited — the commit writes it, wherever `data` has moved it. */
|
|
8
|
+
row: DataRow;
|
|
9
|
+
/** What the editor held when the row moved and the editor was drawn again — handed to the new one. */
|
|
10
|
+
draft?: unknown;
|
|
5
11
|
}
|
|
6
12
|
/**
|
|
7
13
|
* Manages cell editing state.
|
|
8
14
|
*/
|
|
9
15
|
export declare class EditingState {
|
|
10
16
|
current: EditState | null;
|
|
11
|
-
start(position: CellPosition, originalValue: unknown): void;
|
|
17
|
+
start(position: CellPosition, originalValue: unknown, row: DataRow): void;
|
|
12
18
|
isEditing(row: number, col: number): boolean;
|
|
13
19
|
cancel(): EditState | null;
|
|
14
20
|
commit(): EditState | null;
|
|
@@ -1,30 +1,41 @@
|
|
|
1
1
|
import type { SelectionMode } from '../models/types.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Separate from cell-level SelectionState
|
|
3
|
+
* Row-level (checkbox) selection, kept as a set of row ids — see `FlexTable.getRowId`.
|
|
4
|
+
* Separate from the cell-level `SelectionState`.
|
|
5
|
+
*
|
|
6
|
+
* An id names a row, not a position: sorting, filtering, inserting or deleting rows leaves the checkmarks on the
|
|
7
|
+
* rows they were put on. The header checkbox and "select all" act on the rows currently in view (`ids` arguments);
|
|
8
|
+
* ids of rows that are not in view (another server page) stay selected.
|
|
5
9
|
*/
|
|
6
10
|
export declare class RowSelectionState {
|
|
7
11
|
private _selected;
|
|
8
12
|
private _mode;
|
|
9
|
-
private _rowCount;
|
|
10
13
|
get mode(): SelectionMode;
|
|
11
14
|
set mode(value: SelectionMode);
|
|
12
|
-
|
|
13
|
-
get
|
|
15
|
+
/** Selected ids, in the order they were selected. */
|
|
16
|
+
get selectedIds(): string[];
|
|
14
17
|
get selectedCount(): number;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
isSelected(id: string): boolean;
|
|
19
|
+
/** Every one of `ids` (the rows in view) is selected — false when `ids` is empty. */
|
|
20
|
+
isAllSelected(ids: readonly string[]): boolean;
|
|
21
|
+
/** Some but not all of `ids` (the rows in view) are selected. */
|
|
22
|
+
isSomeSelected(ids: readonly string[]): boolean;
|
|
23
|
+
toggle(id: string): void;
|
|
24
|
+
select(id: string): void;
|
|
25
|
+
deselect(id: string): void;
|
|
26
|
+
/** Adds `ids` to the selection (multi mode only — one slot cannot hold a set). */
|
|
27
|
+
selectAll(ids: readonly string[]): void;
|
|
28
|
+
/** Removes `ids` from the selection — the rest stays. */
|
|
29
|
+
deselectMany(ids: readonly string[]): void;
|
|
22
30
|
deselectAll(): void;
|
|
31
|
+
/** Replaces the selection with `ids` (single mode keeps the last). Returns whether it changed. */
|
|
32
|
+
set(ids: Iterable<string>): boolean;
|
|
33
|
+
/** Keeps only the ids `keep` accepts. Returns whether anything was dropped. */
|
|
34
|
+
retain(keep: (id: string) => boolean): boolean;
|
|
23
35
|
/**
|
|
24
|
-
* Selects
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* "one at a time" semantics — a range doesn't make sense with one slot.
|
|
36
|
+
* Selects `ids` between the positions of `from` and `to` in `ordered` (the rows in view, top to bottom), inclusive,
|
|
37
|
+
* in either direction — shift-click range selection anchored at the last toggled row. Single mode selects only
|
|
38
|
+
* `to`, matching `toggle()`'s one-slot semantics. An anchor that is no longer in view selects only `to`.
|
|
28
39
|
*/
|
|
29
|
-
selectRange(from:
|
|
40
|
+
selectRange(ordered: readonly string[], from: string, to: string): void;
|
|
30
41
|
}
|
package/dist/events.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export interface CellChange {
|
|
|
19
19
|
*/
|
|
20
20
|
export interface FlexTableEventMap {
|
|
21
21
|
/** Row selection changed (`selectable`). Indices are data indices. */
|
|
22
|
+
/** `selectedIds` — every selected row (all pages, see `rowKey`); `selectedRows`/`selectedIndices` — the selected rows `data` holds. */
|
|
22
23
|
'selection-change': CustomEvent<{
|
|
24
|
+
selectedIds: string[];
|
|
23
25
|
selectedIndices: number[];
|
|
24
26
|
selectedRows: DataRow[];
|
|
25
27
|
}>;
|
|
@@ -83,8 +85,10 @@ export interface FlexTableEventMap {
|
|
|
83
85
|
to: number;
|
|
84
86
|
}>;
|
|
85
87
|
/** Enter on a non-editable cell — "activate this row". `index` is the data index, `col` the visible column index. */
|
|
88
|
+
/** `id` — the row's id (`getRowId`); `index` — its position in `data`; `key` — the active column's key. */
|
|
86
89
|
'row-activate': CustomEvent<{
|
|
87
90
|
row: DataRow;
|
|
91
|
+
id: string;
|
|
88
92
|
index: number;
|
|
89
93
|
col: number;
|
|
90
94
|
key: string | undefined;
|
|
@@ -122,8 +126,10 @@ export interface FlexTableEventMap {
|
|
|
122
126
|
value: unknown;
|
|
123
127
|
error: string;
|
|
124
128
|
}>;
|
|
129
|
+
/** `id` — the row's id (comments stay on their rows); `dataIndex` — its position in `data` now (-1 when not loaded). */
|
|
125
130
|
'comment-change': CustomEvent<{
|
|
126
131
|
dataIndex: number;
|
|
132
|
+
id: string;
|
|
127
133
|
colKey: string;
|
|
128
134
|
text: string | null;
|
|
129
135
|
}>;
|