@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/Table.d.ts
CHANGED
|
@@ -36,6 +36,17 @@ export interface ColumnDef<Row = any, Value = unknown> {
|
|
|
36
36
|
pinnable?: boolean;
|
|
37
37
|
hideable?: boolean;
|
|
38
38
|
reorderable?: boolean;
|
|
39
|
+
/** M2: enable double-click / F2 cell editing for this column. Default false. */
|
|
40
|
+
editable?: boolean;
|
|
41
|
+
/** M2: enable the per-column filter input above this column. Default false. */
|
|
42
|
+
filterable?: boolean;
|
|
43
|
+
/** M2: custom filter predicate. Receives the column's value (post-accessor),
|
|
44
|
+
* the current (trimmed) filter query string, and the full row. Return
|
|
45
|
+
* true to keep the row in the result. Defaults to case-insensitive
|
|
46
|
+
* substring match on the stringified value. */
|
|
47
|
+
filter?: (value: Value, query: string, row: Row) => boolean;
|
|
48
|
+
/** M2: placeholder text for the filter input. Default "Filter…". */
|
|
49
|
+
filterPlaceholder?: string;
|
|
39
50
|
/** Computed value override; if absent, `row[key]` is used. */
|
|
40
51
|
accessor?: (row: Row) => Value;
|
|
41
52
|
/** Sort comparator; default is null-safe number-or-string compare. */
|
|
@@ -53,6 +64,11 @@ export interface ColumnState<Row = any> {
|
|
|
53
64
|
readonly pinnable: boolean;
|
|
54
65
|
readonly hideable: boolean;
|
|
55
66
|
readonly reorderable: boolean;
|
|
67
|
+
/** M2 */
|
|
68
|
+
readonly editable: boolean;
|
|
69
|
+
readonly filterable: boolean;
|
|
70
|
+
readonly filter: ((value: unknown, query: string, row: Row) => boolean) | null;
|
|
71
|
+
readonly filterPlaceholder: string | null;
|
|
56
72
|
readonly minWidth: number;
|
|
57
73
|
readonly maxWidth: number;
|
|
58
74
|
readonly width: Signal<number>;
|
|
@@ -68,6 +84,34 @@ export interface FocusedCell {
|
|
|
68
84
|
columnKey: string;
|
|
69
85
|
}
|
|
70
86
|
|
|
87
|
+
/** M2: cell currently being edited. Keyed on row identity so the edit
|
|
88
|
+
* state survives slot recycling / scroll / sort / filter -- same model
|
|
89
|
+
* as `FocusedCell`. */
|
|
90
|
+
export interface EditingCell {
|
|
91
|
+
rowId: string | number;
|
|
92
|
+
columnKey: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** M2: payload passed to `onCellEdit` on commit.
|
|
96
|
+
*
|
|
97
|
+
* `newValue` is `string` when the commit came from the DOM editing path
|
|
98
|
+
* (double-click / F2 → contenteditable → Enter / Tab / blur). The
|
|
99
|
+
* consumer is responsible for coercion when editing typed columns. When
|
|
100
|
+
* the consumer calls `commitEdit(explicitValue)` with a non-string,
|
|
101
|
+
* `newValue` carries that value through unchanged (typed as unknown).
|
|
102
|
+
* `oldValue` is whatever the cell holds pre-edit (post-accessor).
|
|
103
|
+
*
|
|
104
|
+
* The unchanged-guard compares `String(oldValue) !== newValue` for string
|
|
105
|
+
* `newValue`s -- so a no-op Enter on a numeric column doesn't fire this
|
|
106
|
+
* handler. For non-string explicit values it falls back to strict
|
|
107
|
+
* equality. */
|
|
108
|
+
export interface CellEditPayload<Row = any> {
|
|
109
|
+
row: Row;
|
|
110
|
+
columnKey: string;
|
|
111
|
+
oldValue: unknown;
|
|
112
|
+
newValue: string | unknown;
|
|
113
|
+
}
|
|
114
|
+
|
|
71
115
|
export interface SortEntry {
|
|
72
116
|
key: string;
|
|
73
117
|
dir: SortDir;
|
|
@@ -102,6 +146,54 @@ export interface CreateTableConfig<Row = any> {
|
|
|
102
146
|
overscan?: number;
|
|
103
147
|
initialFocus?: FocusedCell | null;
|
|
104
148
|
initialSort?: readonly SortEntry[];
|
|
149
|
+
/** M2: commit hook for cell edits. Receives the row + column key + old +
|
|
150
|
+
* new value. The table does NOT mutate rows itself; consumer is
|
|
151
|
+
* responsible for the data write (in-memory mutation, backend POST,
|
|
152
|
+
* optimistic update, etc.). Skipped when newValue === oldValue.
|
|
153
|
+
* Errors thrown by the handler are caught + logged. */
|
|
154
|
+
onCellEdit?: (payload: CellEditPayload<Row>) => void | Promise<unknown>;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// --- Export option types ----------------------------------------------------
|
|
158
|
+
|
|
159
|
+
/** Row source for export.
|
|
160
|
+
*
|
|
161
|
+
* - `"visible"` (default): the current post-sort view that the user sees.
|
|
162
|
+
* - `"all"`: the original master `rows` array, ignoring sort.
|
|
163
|
+
* - `"selected"`: the current selection materialized against the master
|
|
164
|
+
* array (so the export order matches insertion order, not the sorted
|
|
165
|
+
* order the user is looking at). Pass an explicit array if you want
|
|
166
|
+
* a different source.
|
|
167
|
+
* - Array: an explicit row array (e.g. a paginated page snapshot).
|
|
168
|
+
*/
|
|
169
|
+
export type ExportRowsSource<Row = any> = "visible" | "all" | "selected" | readonly Row[];
|
|
170
|
+
|
|
171
|
+
/** Column projection for export.
|
|
172
|
+
*
|
|
173
|
+
* - `"visible"` (default): column order matches the current visible order
|
|
174
|
+
* and skips hidden columns.
|
|
175
|
+
* - `"all"`: all declared columns in declaration order, including hidden.
|
|
176
|
+
* - Array: explicit list of column keys (projection + ordering).
|
|
177
|
+
*/
|
|
178
|
+
export type ExportColumnsSelector = "visible" | "all" | readonly string[];
|
|
179
|
+
|
|
180
|
+
export interface ExportCsvOptions<Row = any> {
|
|
181
|
+
rows?: ExportRowsSource<Row>;
|
|
182
|
+
columns?: ExportColumnsSelector;
|
|
183
|
+
delimiter?: string;
|
|
184
|
+
quote?: string;
|
|
185
|
+
headers?: boolean;
|
|
186
|
+
newline?: string;
|
|
187
|
+
bom?: boolean;
|
|
188
|
+
formatter?: (row: Row, col: ColumnState<Row>) => unknown;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export interface ExportJsonOptions<Row = any> {
|
|
192
|
+
rows?: ExportRowsSource<Row>;
|
|
193
|
+
columns?: ExportColumnsSelector;
|
|
194
|
+
indent?: number;
|
|
195
|
+
format?: "string" | "array";
|
|
196
|
+
formatter?: (row: Row, col: ColumnState<Row>) => unknown;
|
|
105
197
|
}
|
|
106
198
|
|
|
107
199
|
// --- Headless core ----------------------------------------------------------
|
|
@@ -116,6 +208,10 @@ export interface TableCore<Row = any> {
|
|
|
116
208
|
|
|
117
209
|
// ---- Reactive: data ----
|
|
118
210
|
rowsGetter(): readonly Row[];
|
|
211
|
+
/** M2: filtered rows BEFORE sort. `visibleRows` reads from this; consumers
|
|
212
|
+
* can read it directly when they want filter-aware row materializers
|
|
213
|
+
* (e.g. count of pre-sort matches, export of filtered-but-not-sorted). */
|
|
214
|
+
readonly filteredRows: Computed<readonly Row[]>;
|
|
119
215
|
readonly visibleRows: Computed<readonly Row[]>;
|
|
120
216
|
readonly rowCount: Computed<number>;
|
|
121
217
|
|
|
@@ -133,8 +229,12 @@ export interface TableCore<Row = any> {
|
|
|
133
229
|
readonly leftOffsets: Computed<Map<string, number>>;
|
|
134
230
|
readonly rightOffsets: Computed<Map<string, number>>;
|
|
135
231
|
|
|
136
|
-
// ---- Reactive: sort / focus / selection ----
|
|
232
|
+
// ---- Reactive: sort / filter / focus / selection / editing ----
|
|
137
233
|
readonly sortChain: Signal<readonly SortEntry[]>;
|
|
234
|
+
/** M2: per-column filter state. Map<columnKey, queryString>. Mutated
|
|
235
|
+
* via `setColumnFilter` -- direct .set on the signal works but always
|
|
236
|
+
* pass a fresh Map (Object.is-equal mutations would not notify). */
|
|
237
|
+
readonly columnFilters: Signal<ReadonlyMap<string, string>>;
|
|
138
238
|
readonly focusedCell: Signal<FocusedCell | null>;
|
|
139
239
|
/** Predicate-based selection state. Reading `.set.has(id)` directly is
|
|
140
240
|
* wrong in all-mode -- use `isSelected(id)` for membership. */
|
|
@@ -143,6 +243,14 @@ export interface TableCore<Row = any> {
|
|
|
143
243
|
/** Reactive O(1) selected count. In all-mode this is
|
|
144
244
|
* `rowCount() - blacklist.size`; otherwise `whitelist.size`. */
|
|
145
245
|
readonly selectedCount: Computed<number>;
|
|
246
|
+
/** M2: current cell being edited, or null. Keyed on row identity so it
|
|
247
|
+
* survives scroll / sort / filter / slot recycling. */
|
|
248
|
+
readonly editingCell: Signal<EditingCell | null>;
|
|
249
|
+
/** M2: in-progress edit value (the contenteditable's textContent).
|
|
250
|
+
* mountTable writes to this from `input` events; consumers can also
|
|
251
|
+
* set it programmatically to drive an external input. `commitEdit`
|
|
252
|
+
* without an explicit value reads this. */
|
|
253
|
+
readonly editingDraft: Signal<string>;
|
|
146
254
|
|
|
147
255
|
// ---- Methods: sort ----
|
|
148
256
|
setSort(key: string, dir: SortDir | null): void;
|
|
@@ -178,6 +286,31 @@ export interface TableCore<Row = any> {
|
|
|
178
286
|
setColumnOrder(keys: readonly string[]): void;
|
|
179
287
|
moveColumn(fromKey: string, toKey: string, opts?: { before?: boolean }): void;
|
|
180
288
|
|
|
289
|
+
// ---- Methods: filters (M2) ----
|
|
290
|
+
/** Set or clear a column's filter. Pass `null`/`undefined`/`""` to clear.
|
|
291
|
+
* No-op on non-filterable or unknown columns. */
|
|
292
|
+
setColumnFilter(key: string, value: string | null | undefined): void;
|
|
293
|
+
/** Clear all filters. No-op + no notify if the filter map is already empty. */
|
|
294
|
+
clearColumnFilters(): void;
|
|
295
|
+
|
|
296
|
+
// ---- Methods: editing (M2) ----
|
|
297
|
+
/** Start editing the (rowId, columnKey) cell. No-op on non-editable
|
|
298
|
+
* columns. Auto-commits any in-flight edit of a different cell. Idempotent
|
|
299
|
+
* on the same cell (does not re-seed the draft). */
|
|
300
|
+
startEdit(rowId: string | number, columnKey: string): void;
|
|
301
|
+
/** Commit the in-flight edit. With no argument, reads from `editingDraft`.
|
|
302
|
+
* Fires `onCellEdit` only when `newValue !== oldValue`. Clears edit state. */
|
|
303
|
+
commitEdit(value?: string): void;
|
|
304
|
+
/** Discard the in-flight edit without firing `onCellEdit`. */
|
|
305
|
+
cancelEdit(): void;
|
|
306
|
+
/** O(1) predicate. */
|
|
307
|
+
isEditing(rowId: string | number, columnKey: string): boolean;
|
|
308
|
+
|
|
309
|
+
// ---- Methods: export ----
|
|
310
|
+
exportCsv(opts?: ExportCsvOptions<Row>): string;
|
|
311
|
+
exportJson(opts?: ExportJsonOptions<Row> & { format?: "string" }): string;
|
|
312
|
+
exportJson<R = Row>(opts: ExportJsonOptions<Row> & { format: "array" }): object[];
|
|
313
|
+
|
|
181
314
|
// ---- Methods: focus ----
|
|
182
315
|
moveFocus(direction: FocusDirection, opts?: { pageSize?: number }): void;
|
|
183
316
|
|