@zakkster/lite-table 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +205 -0
- package/README.md +341 -1
- package/Table.d.ts +134 -1
- package/Table.js +737 -35
- package/llms.txt +121 -2
- package/package.json +11 -7
package/llms.txt
CHANGED
|
@@ -130,10 +130,107 @@ TableCore methods (focus):
|
|
|
130
130
|
rowStart, rowEnd, pageUp, pageDown
|
|
131
131
|
skips hidden columns; follows current sort order
|
|
132
132
|
|
|
133
|
+
TableCore methods (export, M1.1):
|
|
134
|
+
exportCsv(opts?) -> string
|
|
135
|
+
opts.rows: "visible"|"all"|"selected"|Array (default "visible")
|
|
136
|
+
opts.columns: "visible"|"all"|Array<key> (default "visible")
|
|
137
|
+
opts.delimiter: string (default ",")
|
|
138
|
+
opts.quote: string (default '"')
|
|
139
|
+
opts.headers: boolean (default true)
|
|
140
|
+
opts.newline: string (default "\r\n")
|
|
141
|
+
opts.bom: boolean (default false; UTF-8 BOM for Excel-on-Windows)
|
|
142
|
+
opts.formatter: (row, col) => unknown
|
|
143
|
+
RFC 4180 escaping; column accessor honored; numbers stringify naturally;
|
|
144
|
+
null/undefined become empty fields.
|
|
145
|
+
|
|
146
|
+
exportJson(opts?) -> string | object[]
|
|
147
|
+
opts.rows: same as exportCsv
|
|
148
|
+
opts.columns: same as exportCsv
|
|
149
|
+
opts.indent: number (default 0 = compact single-line)
|
|
150
|
+
opts.format: "string"|"array" (default "string"; "array" skips JSON.stringify)
|
|
151
|
+
opts.formatter: (row, col) => unknown
|
|
152
|
+
Fast path: columns:"all"+no formatter returns rows.slice() (shallow).
|
|
153
|
+
|
|
154
|
+
Paginated-getter pitfall: rows:"all" and rows:"selected" resolve against
|
|
155
|
+
rowsGetter(), which for a function-form rows IS the current page. To
|
|
156
|
+
export across the master, pass the master array:
|
|
157
|
+
table.exportCsv({rows: allRows})
|
|
158
|
+
table.exportCsv({rows: table.selectedRows(allRows)})
|
|
159
|
+
|
|
160
|
+
TableCore filtering (M2):
|
|
161
|
+
Column opt-in: { key, filterable: true,
|
|
162
|
+
filter?: (value, query, row) => boolean,
|
|
163
|
+
filterPlaceholder?: string }
|
|
164
|
+
|
|
165
|
+
Default predicate: case-insensitive substring on stringified value.
|
|
166
|
+
Empty / whitespace-only query: filter is treated as inactive (predicate
|
|
167
|
+
not invoked).
|
|
168
|
+
|
|
169
|
+
Reactive surface:
|
|
170
|
+
columnFilters() -> ReadonlyMap<columnKey, queryString>
|
|
171
|
+
filteredRows() -> Computed rows post-filter, pre-sort
|
|
172
|
+
visibleRows() -> Computed rows post-filter AND post-sort
|
|
173
|
+
|
|
174
|
+
Methods:
|
|
175
|
+
setColumnFilter(key, value) value=null/""/whitespace clears that column
|
|
176
|
+
clearColumnFilters() no-op + no notify if already empty
|
|
177
|
+
|
|
178
|
+
Pipeline: rowsGetter -> filteredRows -> visibleRows -> exports/mount
|
|
179
|
+
Multiple active filters apply as AND.
|
|
180
|
+
|
|
181
|
+
DOM (when mounted): a .lt-filter-row is mounted between header + viewport
|
|
182
|
+
if any column is filterable. One <input> per filterable column, two-way
|
|
183
|
+
bound. Escape on input clears that column's filter. Sticky top.
|
|
184
|
+
|
|
185
|
+
TableCore editing (M2):
|
|
186
|
+
Column opt-in: { key, editable: true }
|
|
187
|
+
Config: createTable({ ...,
|
|
188
|
+
onCellEdit: ({row, columnKey, oldValue, newValue}) => ... })
|
|
189
|
+
|
|
190
|
+
lite-table does NOT mutate rows. onCellEdit is the consumer's write hook.
|
|
191
|
+
|
|
192
|
+
Reactive surface:
|
|
193
|
+
editingCell() -> { rowId, columnKey } | null
|
|
194
|
+
editingDraft() -> current in-progress string
|
|
195
|
+
|
|
196
|
+
Methods:
|
|
197
|
+
startEdit(rowId, columnKey) no-op on non-editable; auto-commits
|
|
198
|
+
in-flight on a different cell;
|
|
199
|
+
idempotent on the same cell.
|
|
200
|
+
commitEdit() reads editingDraft
|
|
201
|
+
commitEdit("explicit") commits a specific value
|
|
202
|
+
cancelEdit() discard; no onCellEdit
|
|
203
|
+
isEditing(rowId, columnKey) O(1) predicate
|
|
204
|
+
|
|
205
|
+
Commit-skip: onCellEdit is NOT called when the value is unchanged.
|
|
206
|
+
DOM edits produce string newValue; guard compares
|
|
207
|
+
String(oldValue) !== newValue (so 100 vs "100" is unchanged).
|
|
208
|
+
Explicit commitEdit(explicitValue) falls back to strict ===.
|
|
209
|
+
newValue is ALWAYS a string from the DOM path -- consumers
|
|
210
|
+
coerce in their handler (Number(newValue) / new Date(newValue) /
|
|
211
|
+
newValue === "true" / etc.).
|
|
212
|
+
Handler errors are caught + logged; subsequent edits work.
|
|
213
|
+
|
|
214
|
+
DOM keyboard (when mounted):
|
|
215
|
+
dblclick on editable cell -> startEdit
|
|
216
|
+
F2 / Enter on focused cell with editable column -> startEdit
|
|
217
|
+
Enter -> commit + moveFocus down
|
|
218
|
+
Tab -> commit + moveFocus right (Shift+Tab = left)
|
|
219
|
+
Escape -> cancel + return focus to root
|
|
220
|
+
blur -> commit
|
|
221
|
+
|
|
222
|
+
Edit state survives scroll: keyed on rowId, so contenteditable rebinds
|
|
223
|
+
when the row scrolls back into view. Draft preserved across scroll.
|
|
224
|
+
|
|
133
225
|
TableCore lifecycle:
|
|
134
226
|
dispose() releases all reactive nodes
|
|
135
227
|
cellId(rowId, columnKey) -> "lt_<rowId>__<columnKey>"
|
|
136
228
|
|
|
229
|
+
Note: v1.1.0 fixed a latent leak in dispose() (handles were called,
|
|
230
|
+
not freed). After dispose() every reactive node the table allocated
|
|
231
|
+
returns to the registry pool. 50 createTable+dispose cycles round-trip
|
|
232
|
+
cleanly with activeNodes flat.
|
|
233
|
+
|
|
137
234
|
### mountTable(host, table, options?) -> TableMount
|
|
138
235
|
|
|
139
236
|
options: { injectStyles?=true, initialViewportHeight?=480 }
|
|
@@ -163,15 +260,28 @@ Header: <div class="lt-header" role="row" aria-rowindex="1">
|
|
|
163
260
|
<span class="lt-header-resize"></span> <!-- if resizable -->
|
|
164
261
|
</div> ...
|
|
165
262
|
</div>
|
|
263
|
+
Filters (M2; mounted only when any column has filterable: true):
|
|
264
|
+
<div class="lt-filter-row" role="row" aria-rowindex="2">
|
|
265
|
+
<div class="lt-filter-cell" data-key data-pin
|
|
266
|
+
style="grid-column; left|right (if pinned)">
|
|
267
|
+
<input class="lt-filter-input"
|
|
268
|
+
aria-label="Filter <header>"
|
|
269
|
+
placeholder="Filter…">
|
|
270
|
+
<!-- empty cells for non-filterable columns -->
|
|
271
|
+
</div> ...
|
|
272
|
+
</div>
|
|
166
273
|
Body: <div class="lt-viewport"><div class="lt-inner">
|
|
167
274
|
<div class="lt-row [lt-row-alt] [is-selected]"
|
|
168
275
|
role="row"
|
|
169
276
|
aria-rowindex aria-selected
|
|
170
277
|
style="transform: translateY(<i*rowHeight>px)">
|
|
171
|
-
<div class="lt-cell
|
|
278
|
+
<div class="lt-cell [is-focused] [is-editing]"
|
|
279
|
+
role="gridcell"
|
|
172
280
|
id="lt_<rowId>__<columnKey>"
|
|
173
281
|
aria-colindex
|
|
174
282
|
data-pin
|
|
283
|
+
[data-editable="true"]
|
|
284
|
+
[contenteditable="true"] <!-- only the active edit cell -->
|
|
175
285
|
style="grid-column; left|right (if pinned)">text</div>
|
|
176
286
|
...
|
|
177
287
|
</div> ...
|
|
@@ -185,13 +295,22 @@ Ctrl+Home/End grid corners (0,0) / (last,last)
|
|
|
185
295
|
PageUp/PageDown +/- floor(viewportHeight / rowHeight)
|
|
186
296
|
Space selectRow(focused.rowId, "set")
|
|
187
297
|
Ctrl+A selectAll()
|
|
188
|
-
Escape clearSelection()
|
|
298
|
+
Escape clearSelection() (when not editing)
|
|
299
|
+
F2 / Enter (on editable) startEdit(focused) (M2)
|
|
300
|
+
Dblclick on editable cell startEdit (M2)
|
|
189
301
|
Click cell selectRow(set) + focus
|
|
190
302
|
Shift+click selectRow(range)
|
|
191
303
|
Ctrl/Cmd+click selectRow(toggle)
|
|
192
304
|
Drag header reorder
|
|
193
305
|
Drag right edge of header resize
|
|
194
306
|
Click header toggleSort
|
|
307
|
+
(while editing):
|
|
308
|
+
Enter commitEdit + moveFocus("down")
|
|
309
|
+
Tab / Shift+Tab commitEdit + moveFocus("right"/"left")
|
|
310
|
+
Escape cancelEdit + return focus to root
|
|
311
|
+
blur commitEdit
|
|
312
|
+
(on a filter input):
|
|
313
|
+
Escape setColumnFilter(key, "")
|
|
195
314
|
Shift+click header toggleSort additive
|
|
196
315
|
|
|
197
316
|
## Performance
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zakkster/lite-table",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Headless reactive data tables on @zakkster/lite-signal. CSS Grid (no <table>), pooled slots that never reparent, aria-activedescendant focus model. Zero-GC scroll path on @zakkster/lite-virtual.",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"keywords": [
|
|
@@ -24,8 +24,10 @@
|
|
|
24
24
|
"types": "./Table.d.ts",
|
|
25
25
|
"exports": {
|
|
26
26
|
".": {
|
|
27
|
+
"node": "./Table.js",
|
|
28
|
+
"import": "./Table.js",
|
|
27
29
|
"types": "./Table.d.ts",
|
|
28
|
-
"
|
|
30
|
+
"default": "./Table.js"
|
|
29
31
|
}
|
|
30
32
|
},
|
|
31
33
|
"files": [
|
|
@@ -42,24 +44,26 @@
|
|
|
42
44
|
"bench": "node --expose-gc --max-old-space-size=4096 bench/run.js"
|
|
43
45
|
},
|
|
44
46
|
"peerDependencies": {
|
|
45
|
-
"@zakkster/lite-signal": "^1.2.
|
|
47
|
+
"@zakkster/lite-signal": "^1.2.1",
|
|
46
48
|
"@zakkster/lite-signal-dom": "^1.0.1",
|
|
47
49
|
"@zakkster/lite-virtual": "^1.1.0"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
52
|
+
"@playwright/test": "^1.60.0",
|
|
50
53
|
"clusterize.js": "^1.0.0",
|
|
51
|
-
"happy-dom": "^15.11.7"
|
|
54
|
+
"happy-dom": "^15.11.7",
|
|
55
|
+
"playwright": "^1.60.0"
|
|
52
56
|
},
|
|
53
57
|
"engines": {
|
|
54
58
|
"node": ">=20"
|
|
55
59
|
},
|
|
56
|
-
"homepage": "https://github.com/PeshoVurtoleta/lite-
|
|
60
|
+
"homepage": "https://github.com/PeshoVurtoleta/lite-table#readme",
|
|
57
61
|
"repository": {
|
|
58
62
|
"type": "git",
|
|
59
|
-
"url": "git+https://github.com/PeshoVurtoleta/lite-
|
|
63
|
+
"url": "git+https://github.com/PeshoVurtoleta/lite-table.git"
|
|
60
64
|
},
|
|
61
65
|
"bugs": {
|
|
62
|
-
"url": "https://github.com/PeshoVurtoleta/lite-
|
|
66
|
+
"url": "https://github.com/PeshoVurtoleta/lite-table/issues",
|
|
63
67
|
"email": "shinikchiev@yahoo.com"
|
|
64
68
|
},
|
|
65
69
|
"funding": {
|