@mikestools/usetable 0.0.1 â 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +586 -519
- package/dist/index.d.ts +722 -121
- package/dist/usetable.js +1263 -1078
- package/dist/usetable.umd.cjs +7 -7
- package/package.json +20 -20
- package/showcase/examples/BasicExample.vue +474 -0
package/README.md
CHANGED
|
@@ -1,519 +1,586 @@
|
|
|
1
|
-
# @mikestools/usetable
|
|
2
|
-
|
|
3
|
-
The most comprehensive Vue 3 table composable with 20+ advanced features. Pure DOM wrapper with reactive data layer for complete table manipulation.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- đ¯ **Composable-First** - Pure Vue 3 Composition API, no components
|
|
8
|
-
- đĻ **TypeScript First** - Full type safety with IntelliSense
|
|
9
|
-
- đ **DOM-First Architecture** - HTML table is source of truth
|
|
10
|
-
- ⨠**20+ Advanced Features** - All-in-one solution
|
|
11
|
-
|
|
12
|
-
> **Note:** Data is read from DOM `textContent`, so values are strings. Use `Number()` when working with numeric data.
|
|
13
|
-
|
|
14
|
-
### Complete Feature Set
|
|
15
|
-
|
|
16
|
-
| Feature | Description |
|
|
17
|
-
|---------------------------|-------------------------------------------|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
|
|
|
23
|
-
|
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
37
|
-
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
import {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
table
|
|
61
|
-
table.
|
|
62
|
-
|
|
63
|
-
['
|
|
64
|
-
['
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
| `
|
|
96
|
-
| `
|
|
97
|
-
| `
|
|
98
|
-
| `
|
|
99
|
-
| `
|
|
100
|
-
| `
|
|
101
|
-
| `
|
|
102
|
-
| `
|
|
103
|
-
| `
|
|
104
|
-
| `
|
|
105
|
-
| `
|
|
106
|
-
| `
|
|
107
|
-
| `
|
|
108
|
-
| `
|
|
109
|
-
| `
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
table.
|
|
117
|
-
table.
|
|
118
|
-
table.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
table.
|
|
125
|
-
table.
|
|
126
|
-
table.
|
|
127
|
-
table.
|
|
128
|
-
table.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
table.
|
|
135
|
-
table.
|
|
136
|
-
table.
|
|
137
|
-
table.
|
|
138
|
-
table.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
table.
|
|
145
|
-
table.
|
|
146
|
-
table.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
table.
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (columnIndex ===
|
|
172
|
-
return
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
const
|
|
186
|
-
const
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
table.
|
|
234
|
-
table.
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
table.
|
|
256
|
-
table.
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
table.
|
|
271
|
-
table.
|
|
272
|
-
table.
|
|
273
|
-
table.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
table.
|
|
278
|
-
table.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
table
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
table
|
|
409
|
-
table.
|
|
410
|
-
table.
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
table.
|
|
419
|
-
table.
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
table.
|
|
423
|
-
table.
|
|
424
|
-
table.
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
table.
|
|
433
|
-
table.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
table.
|
|
443
|
-
table.
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
table.
|
|
447
|
-
table.
|
|
448
|
-
table.
|
|
449
|
-
table.
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
table.
|
|
460
|
-
table.
|
|
461
|
-
table.
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
table.
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
table.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
table.
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
1
|
+
# @mikestools/usetable
|
|
2
|
+
|
|
3
|
+
The most comprehensive Vue 3 table composable with 20+ advanced features. Pure DOM wrapper with reactive data layer for complete table manipulation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- đ¯ **Composable-First** - Pure Vue 3 Composition API, no components
|
|
8
|
+
- đĻ **TypeScript First** - Full type safety with IntelliSense
|
|
9
|
+
- đ **DOM-First Architecture** - HTML table is source of truth
|
|
10
|
+
- ⨠**20+ Advanced Features** - All-in-one solution
|
|
11
|
+
|
|
12
|
+
> **Note:** Data is read from DOM `textContent`, so values are strings. Use `Number()` when working with numeric data.
|
|
13
|
+
|
|
14
|
+
### Complete Feature Set
|
|
15
|
+
|
|
16
|
+
| Feature | Description |
|
|
17
|
+
|---------------------------|-------------------------------------------|
|
|
18
|
+
| đī¸ **Record-Based Data** | Work with typed records by ID |
|
|
19
|
+
| đ **Sorting** | Column sorting with state tracking |
|
|
20
|
+
| đ **Filtering** | Row filtering with predicates |
|
|
21
|
+
| âŠī¸ **Undo/Redo** | Full history tracking with rollback |
|
|
22
|
+
| đ¯ **Cell Focus** | Keyboard navigation between cells |
|
|
23
|
+
| đī¸ **Column Visibility** | Show/hide columns dynamically |
|
|
24
|
+
| âī¸ **Reordering** | Move rows and columns programmatically |
|
|
25
|
+
| đ **Clipboard** | Copy/paste with Excel-compatible format |
|
|
26
|
+
| đ **Row State** | Expand/collapse and pin rows |
|
|
27
|
+
| đˇī¸ **Metadata** | Custom data on cells, rows, columns |
|
|
28
|
+
| âī¸ **Selection** | Row and cell selection with range support |
|
|
29
|
+
| đ **Events** | Granular change callbacks |
|
|
30
|
+
| ⥠**Batch Updates** | Efficient bulk operations |
|
|
31
|
+
| âŗ **Async Data** | Loading states and async fetching |
|
|
32
|
+
| đ§ **Frozen Columns** | Pin columns to left/right |
|
|
33
|
+
| đ **Transactions** | Batch operations with automatic rollback |
|
|
34
|
+
| â
**Validation** | Cell, row, column validation |
|
|
35
|
+
| đ **Aggregation** | Sum, average, min, max, count |
|
|
36
|
+
| đ **Pagination** | Client-side pagination |
|
|
37
|
+
| đĨ **Import/Export** | CSV and JSON format support |
|
|
38
|
+
| đ§Š **HTML Elements** | Rich cell content support |
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install @mikestools/usetable vue
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
import { ref, onMounted } from 'vue'
|
|
50
|
+
import { useTable } from '@mikestools/usetable'
|
|
51
|
+
|
|
52
|
+
const tableRef = ref<HTMLTableElement>()
|
|
53
|
+
|
|
54
|
+
onMounted(() => {
|
|
55
|
+
const element = tableRef.value
|
|
56
|
+
if (!element) return
|
|
57
|
+
|
|
58
|
+
const table = useTable(ref(element))
|
|
59
|
+
|
|
60
|
+
// Set up table structure
|
|
61
|
+
table.setHeaders(['Name', 'Department', 'Salary'])
|
|
62
|
+
table.setData([
|
|
63
|
+
['Alice Johnson', 'Engineering', 85000],
|
|
64
|
+
['Bob Smith', 'Marketing', 72000],
|
|
65
|
+
['Carol Williams', 'Sales', 68000]
|
|
66
|
+
])
|
|
67
|
+
|
|
68
|
+
// Add a row
|
|
69
|
+
table.addRow(['David Brown', 'Engineering', 92000])
|
|
70
|
+
|
|
71
|
+
// Update a cell
|
|
72
|
+
table.setCell(0, 2, 90000)
|
|
73
|
+
|
|
74
|
+
// Use aggregation
|
|
75
|
+
const totalSalary = table.sum(2)
|
|
76
|
+
console.log('Total:', totalSalary)
|
|
77
|
+
|
|
78
|
+
// Enable inline editing
|
|
79
|
+
const cleanup = table.enableEditing()
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```vue
|
|
84
|
+
<template>
|
|
85
|
+
<table ref="tableRef" class="table"></table>
|
|
86
|
+
</template>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## API Reference
|
|
90
|
+
|
|
91
|
+
### Core Properties
|
|
92
|
+
|
|
93
|
+
| Property | Type | Description |
|
|
94
|
+
|----------------------|-----------------------------------|-----------------------------------|
|
|
95
|
+
| `element` | `Readonly<Ref<HTMLTableElement>>` | The underlying table element |
|
|
96
|
+
| `data` | `Ref<unknown[][]>` | Reactive 2D array of body data |
|
|
97
|
+
| `headers` | `Ref<string[]>` | Reactive array of headers |
|
|
98
|
+
| `rowCount` | `Readonly<Ref<number>>` | Number of data rows |
|
|
99
|
+
| `columnCount` | `Readonly<Ref<number>>` | Number of columns |
|
|
100
|
+
| `selectedRows` | `Ref<Set<number>>` | Selected row indices |
|
|
101
|
+
| `selectedCells` | `Ref<Set<string>>` | Selected cell keys |
|
|
102
|
+
| `sortState` | `SortState` | Current sort column and direction |
|
|
103
|
+
| `filterState` | `FilterState` | Current filter state |
|
|
104
|
+
| `focusedCell` | `Ref<{row, column} \| null>` | Currently focused cell |
|
|
105
|
+
| `visibleColumnCount` | `Readonly<Ref<number>>` | Count of visible columns |
|
|
106
|
+
| `dirtyState` | `Ref<boolean>` | Whether table has unsaved changes |
|
|
107
|
+
| `expandedRows` | `Ref<Set<number>>` | Expanded row indices |
|
|
108
|
+
| `pinnedTopRows` | `Ref<Set<number>>` | Rows pinned to top |
|
|
109
|
+
| `pinnedBottomRows` | `Ref<Set<number>>` | Rows pinned to bottom |
|
|
110
|
+
| `tableLoading` | `Ref<boolean>` | Loading state |
|
|
111
|
+
|
|
112
|
+
### Data Methods
|
|
113
|
+
|
|
114
|
+
#### Setup
|
|
115
|
+
```typescript
|
|
116
|
+
table.setHeaders(['Name', 'Age', 'Email'])
|
|
117
|
+
table.setData([['Alice', 30, 'alice@email.com'], ...])
|
|
118
|
+
table.setFooter(['Total', '', count])
|
|
119
|
+
table.setCaption('User List')
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
#### Row Operations
|
|
123
|
+
```typescript
|
|
124
|
+
table.addRow(['value1', 'value2', ...], index?)
|
|
125
|
+
table.addRowWithAttributes(['value1', ...], { class: 'highlight' })
|
|
126
|
+
table.removeRow(index) // -1 for last row
|
|
127
|
+
table.updateRow(index, ['newValue1', ...])
|
|
128
|
+
table.getRowData(index) // Returns copy
|
|
129
|
+
table.setRowData(index, ['value1', ...])
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
#### Cell Operations
|
|
133
|
+
```typescript
|
|
134
|
+
table.getCell(rowIndex, columnIndex)
|
|
135
|
+
table.setCell(rowIndex, columnIndex, value)
|
|
136
|
+
table.setCellWithAttributes(rowIndex, columnIndex, value, { class: 'active' })
|
|
137
|
+
table.updateCell(rowIndex, columnIndex, { value, attributes })
|
|
138
|
+
table.getCellRange(rowStart, rowEnd, columnStart, columnEnd)
|
|
139
|
+
table.setCellRange(rowStart, columnStart, [[data]])
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
#### Column Operations
|
|
143
|
+
```typescript
|
|
144
|
+
table.addColumn('Header', ['data1', 'data2', ...], index?)
|
|
145
|
+
table.removeColumn(index)
|
|
146
|
+
table.getColumnData(index)
|
|
147
|
+
table.setColumnData(index, ['value1', 'value2', ...])
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Advanced Features
|
|
151
|
+
|
|
152
|
+
#### Transactions
|
|
153
|
+
```typescript
|
|
154
|
+
// Batch operations with automatic rollback on error
|
|
155
|
+
table.transaction(() => {
|
|
156
|
+
table.addRow(['New', 'Row', 'Data'])
|
|
157
|
+
table.setCell(0, 0, 'Updated')
|
|
158
|
+
// If any operation fails, all changes are reverted
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
// Async transactions
|
|
162
|
+
await table.transaction(async () => {
|
|
163
|
+
const data = await fetchData()
|
|
164
|
+
table.setData(data)
|
|
165
|
+
})
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
#### Validation
|
|
169
|
+
```typescript
|
|
170
|
+
const result = table.validateAll((value, rowIndex, columnIndex) => {
|
|
171
|
+
if (columnIndex === 0 && !value) return 'Name required'
|
|
172
|
+
if (columnIndex === 2 && isNaN(Number(value))) return 'Must be number'
|
|
173
|
+
return null // Valid
|
|
174
|
+
})
|
|
175
|
+
|
|
176
|
+
if (!result.valid) {
|
|
177
|
+
result.errors.forEach(error => {
|
|
178
|
+
console.log(`Row ${error.row}, Column ${error.column}: ${error.message}`)
|
|
179
|
+
})
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Aggregation
|
|
184
|
+
```typescript
|
|
185
|
+
const total = table.sum(colIndex) // Returns number (auto-converts)
|
|
186
|
+
const avg = table.average(colIndex) // Returns number
|
|
187
|
+
const min = table.min(colIndex) // Returns string from DOM
|
|
188
|
+
const max = table.max(colIndex) // Returns string from DOM
|
|
189
|
+
const count = table.count(colIndex, predicate?)
|
|
190
|
+
const custom = table.aggregate(colIndex, { initial, reducer })
|
|
191
|
+
|
|
192
|
+
// Convert min/max when needed
|
|
193
|
+
const maxSalary = Number(table.max(2))
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
#### Transformation
|
|
197
|
+
```typescript
|
|
198
|
+
table.transformColumn(columnIndex, (value, rowIndex) => {
|
|
199
|
+
return Number(value) * 1.1 // 10% increase
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
table.transformRow(rowIndex, (value, columnIndex) => {
|
|
203
|
+
return String(value).toUpperCase()
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
table.transformCells((value, rowIndex, columnIndex) => {
|
|
207
|
+
return columnIndex === 2 ? Number(value).toFixed(2) : value
|
|
208
|
+
})
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### Computed Columns
|
|
212
|
+
```typescript
|
|
213
|
+
table.addComputedColumn({
|
|
214
|
+
label: 'Total',
|
|
215
|
+
computeFunction: (row) => Number(row[1]) * Number(row[2]),
|
|
216
|
+
index: 3 // Optional, defaults to end
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
table.removeComputedColumn(3)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
#### Grouping
|
|
223
|
+
```typescript
|
|
224
|
+
const grouped = table.groupBy(row => row[1]) // Group by column 1
|
|
225
|
+
// Returns: Map<string, number[]> - key to row indices
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### Pagination
|
|
229
|
+
```typescript
|
|
230
|
+
table.paginate({ pageSize: 10, currentPage: 1 })
|
|
231
|
+
|
|
232
|
+
// Navigation
|
|
233
|
+
table.nextPage()
|
|
234
|
+
table.previousPage()
|
|
235
|
+
table.goToPage(5)
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
#### Virtual Scrolling
|
|
239
|
+
```typescript
|
|
240
|
+
table.enableVirtualScrolling({
|
|
241
|
+
rowHeight: 40,
|
|
242
|
+
containerHeight: 400
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
table.disableVirtualScrolling()
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### Import/Export
|
|
249
|
+
```typescript
|
|
250
|
+
// Export
|
|
251
|
+
const csv = table.exportToCSV()
|
|
252
|
+
const json = table.exportToJSON()
|
|
253
|
+
|
|
254
|
+
// Import
|
|
255
|
+
table.importFromCSV(csvString)
|
|
256
|
+
table.importFromJSON(jsonString)
|
|
257
|
+
table.importFromArray([['row1col1', ...], ['row2col1', ...]])
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Search
|
|
261
|
+
```typescript
|
|
262
|
+
const results = table.search('query', { caseSensitive: false })
|
|
263
|
+
// Returns: [{ row, column, value }, ...]
|
|
264
|
+
|
|
265
|
+
const columnResults = table.searchColumn(0, 'query')
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
#### Selection
|
|
269
|
+
```typescript
|
|
270
|
+
table.selectRow(rowIndex)
|
|
271
|
+
table.deselectRow(rowIndex)
|
|
272
|
+
table.toggleRowSelection(rowIndex)
|
|
273
|
+
table.selectCell(rowIndex, columnIndex)
|
|
274
|
+
table.clearSelection()
|
|
275
|
+
|
|
276
|
+
// Check state
|
|
277
|
+
table.isRowSelected(rowIndex)
|
|
278
|
+
table.selectedRows.value // Set<number>
|
|
279
|
+
table.selectedCells.value // Set<string>
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
#### Inline Editing
|
|
283
|
+
```typescript
|
|
284
|
+
const cleanup = table.enableEditing()
|
|
285
|
+
// Double-click to edit, Enter to save, Escape to cancel
|
|
286
|
+
|
|
287
|
+
// Disable when done
|
|
288
|
+
cleanup()
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
#### DOM Sync
|
|
292
|
+
```typescript
|
|
293
|
+
// Sync pre-existing HTML table to data layer
|
|
294
|
+
table.sync()
|
|
295
|
+
|
|
296
|
+
// Now table.headers and table.data contain DOM content
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
#### Data Change Subscription
|
|
300
|
+
```typescript
|
|
301
|
+
const unsubscribe = table.onDataChange((newData) => {
|
|
302
|
+
console.log('Data changed:', newData)
|
|
303
|
+
// Save to server, localStorage, etc.
|
|
304
|
+
})
|
|
305
|
+
|
|
306
|
+
// Stop watching
|
|
307
|
+
unsubscribe()
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Types
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// Cell configuration
|
|
314
|
+
interface CellConfig {
|
|
315
|
+
value: unknown
|
|
316
|
+
attributes?: ElementAttributes
|
|
317
|
+
columnSpan?: number
|
|
318
|
+
rowSpan?: number
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Element attributes
|
|
322
|
+
interface ElementAttributes {
|
|
323
|
+
class?: string | string[] | Record<string, boolean>
|
|
324
|
+
style?: string | Record<string, string | number>
|
|
325
|
+
id?: string
|
|
326
|
+
title?: string
|
|
327
|
+
// ... all HTML attributes, ARIA, data-*, events
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Computed column configuration
|
|
331
|
+
interface ComputedColumnConfig {
|
|
332
|
+
computeFunction: (row: unknown[]) => unknown
|
|
333
|
+
label: string
|
|
334
|
+
index?: number
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// Validation
|
|
338
|
+
interface ValidationResult {
|
|
339
|
+
valid: boolean
|
|
340
|
+
errors: ValidationError[]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
interface ValidationError {
|
|
344
|
+
row: number
|
|
345
|
+
column: number
|
|
346
|
+
message: string
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Search
|
|
350
|
+
interface SearchResult {
|
|
351
|
+
row: number
|
|
352
|
+
column: number
|
|
353
|
+
value: unknown
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Sort state
|
|
357
|
+
interface SortState {
|
|
358
|
+
columnIndex: Readonly<Ref<number | null>>
|
|
359
|
+
ascending: Readonly<Ref<boolean>>
|
|
360
|
+
descending: Readonly<Ref<boolean>>
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Filter state
|
|
364
|
+
interface FilterState {
|
|
365
|
+
isFiltered: Readonly<Ref<boolean>>
|
|
366
|
+
filteredRowCount: Readonly<Ref<number>>
|
|
367
|
+
filteredIndices: Readonly<Ref<number[]>>
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Base record type
|
|
371
|
+
interface BaseRecord {
|
|
372
|
+
readonly id: string
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Column definition for records
|
|
376
|
+
interface RecordColumnDef<T extends BaseRecord> {
|
|
377
|
+
field: string | ((record: T) => unknown)
|
|
378
|
+
header: string
|
|
379
|
+
format?: (value: unknown, record: T) => string
|
|
380
|
+
parse?: (value: string, record: T) => unknown
|
|
381
|
+
editable?: boolean
|
|
382
|
+
width?: string | number
|
|
383
|
+
defaultValue?: unknown
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### Record-Based Data API
|
|
388
|
+
|
|
389
|
+
Work with typed records instead of raw arrays:
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
import type { RecordColumnDef } from '@mikestools/usetable'
|
|
393
|
+
|
|
394
|
+
interface User {
|
|
395
|
+
id: string
|
|
396
|
+
name: string
|
|
397
|
+
email: string
|
|
398
|
+
age: number
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const columns: RecordColumnDef<User>[] = [
|
|
402
|
+
{ field: 'id', header: 'ID' },
|
|
403
|
+
{ field: 'name', header: 'Name' },
|
|
404
|
+
{ field: 'email', header: 'Email' },
|
|
405
|
+
{ field: 'age', header: 'Age' },
|
|
406
|
+
]
|
|
407
|
+
|
|
408
|
+
// Configure table for records
|
|
409
|
+
table.setRecordColumns(columns)
|
|
410
|
+
table.setRecords([
|
|
411
|
+
{ id: 'user-1', name: 'Alice', email: 'alice@example.com', age: 30 },
|
|
412
|
+
{ id: 'user-2', name: 'Bob', email: 'bob@example.com', age: 25 },
|
|
413
|
+
])
|
|
414
|
+
|
|
415
|
+
// CRUD operations
|
|
416
|
+
const newId = table.addRecord({ name: 'Carol', email: 'carol@example.com', age: 28 })
|
|
417
|
+
table.updateRecord<User>('user-1', { age: 31 })
|
|
418
|
+
table.removeRecord('user-2')
|
|
419
|
+
const user = table.getRecordById<User>('user-1')
|
|
420
|
+
|
|
421
|
+
// Selection by ID
|
|
422
|
+
table.selectRecords(['user-1', 'user-3'])
|
|
423
|
+
const selectedIds = table.getSelectedRecordIds()
|
|
424
|
+
const selectedRecords = table.getSelectedRecords<User>()
|
|
425
|
+
|
|
426
|
+
// Change notifications
|
|
427
|
+
table.onRecordChange<User>((id, record, type) => {
|
|
428
|
+
console.log(`${type}: ${id}`) // 'add' | 'update' | 'remove'
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
// Export/Import
|
|
432
|
+
const json = table.exportRecordsToJSON()
|
|
433
|
+
table.importRecordsFromJSON(json)
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
### Sorting API
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
table.sortColumnAscending(columnIndex)
|
|
440
|
+
table.sortColumnDescending(columnIndex)
|
|
441
|
+
table.sortColumnAscendingWith(columnIndex, comparator)
|
|
442
|
+
table.sortColumnDescendingWith(columnIndex, comparator)
|
|
443
|
+
table.clearColumnSort()
|
|
444
|
+
|
|
445
|
+
// Check state
|
|
446
|
+
table.isSorted()
|
|
447
|
+
table.isSortedAscending()
|
|
448
|
+
table.isSortedDescending()
|
|
449
|
+
table.getSortedColumnIndex()
|
|
450
|
+
|
|
451
|
+
// Reactive state
|
|
452
|
+
table.sortState.columnIndex.value
|
|
453
|
+
table.sortState.ascending.value
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Filtering API
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
table.filterRows(predicate)
|
|
460
|
+
table.filterColumn(columnIndex, predicate)
|
|
461
|
+
table.filterColumnByValue(columnIndex, value)
|
|
462
|
+
table.filterColumnByValues(columnIndex, values)
|
|
463
|
+
table.clearFilters()
|
|
464
|
+
|
|
465
|
+
// Check state
|
|
466
|
+
table.getFilteredRowIndices()
|
|
467
|
+
table.filterState.isFiltered.value
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
### Undo/Redo API
|
|
471
|
+
|
|
472
|
+
```typescript
|
|
473
|
+
table.undo()
|
|
474
|
+
table.redo()
|
|
475
|
+
table.canUndo()
|
|
476
|
+
table.canRedo()
|
|
477
|
+
table.clearHistory()
|
|
478
|
+
table.setHistoryLimit(100)
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
### Cell Focus API
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
table.focusCell(rowIndex, columnIndex)
|
|
485
|
+
table.clearCellFocus()
|
|
486
|
+
table.getFocusedCell()
|
|
487
|
+
table.moveFocusUp()
|
|
488
|
+
table.moveFocusDown()
|
|
489
|
+
table.moveFocusLeft()
|
|
490
|
+
table.moveFocusRight()
|
|
491
|
+
table.enableKeyboardNavigation()
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
### Column Visibility API
|
|
495
|
+
|
|
496
|
+
```typescript
|
|
497
|
+
table.hideColumn(columnIndex)
|
|
498
|
+
table.showColumn(columnIndex)
|
|
499
|
+
table.toggleColumnVisibility(columnIndex)
|
|
500
|
+
table.isColumnVisible(columnIndex)
|
|
501
|
+
table.getVisibleColumnIndices()
|
|
502
|
+
table.getHiddenColumnIndices()
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Reordering API
|
|
506
|
+
|
|
507
|
+
```typescript
|
|
508
|
+
table.moveRow(fromIndex, toIndex)
|
|
509
|
+
table.moveRowUp(index)
|
|
510
|
+
table.moveRowDown(index)
|
|
511
|
+
table.moveRowToTop(index)
|
|
512
|
+
table.moveRowToBottom(index)
|
|
513
|
+
table.swapRows(index1, index2)
|
|
514
|
+
table.moveColumn(fromIndex, toIndex)
|
|
515
|
+
table.swapColumns(index1, index2)
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
### Clipboard API
|
|
519
|
+
|
|
520
|
+
```typescript
|
|
521
|
+
table.copyCell(rowIndex, columnIndex)
|
|
522
|
+
table.copyRow(rowIndex)
|
|
523
|
+
table.copyColumn(columnIndex)
|
|
524
|
+
table.copyCellRange(startRow, startCol, endRow, endCol)
|
|
525
|
+
table.copySelectedCells()
|
|
526
|
+
table.copySelectedRows()
|
|
527
|
+
table.pasteAtCell(rowIndex, columnIndex, data)
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Row State API
|
|
531
|
+
|
|
532
|
+
```typescript
|
|
533
|
+
table.expandRow(index)
|
|
534
|
+
table.collapseRow(index)
|
|
535
|
+
table.toggleRowExpansion(index)
|
|
536
|
+
table.pinRowTop(index)
|
|
537
|
+
table.pinRowBottom(index)
|
|
538
|
+
table.unpinRow(index)
|
|
539
|
+
table.unpinAllRows()
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
### Metadata API
|
|
543
|
+
|
|
544
|
+
```typescript
|
|
545
|
+
table.setCellMeta(row, col, key, value)
|
|
546
|
+
table.getCellMeta(row, col, key)
|
|
547
|
+
table.setRowMeta(index, key, value)
|
|
548
|
+
table.getRowMeta(index, key)
|
|
549
|
+
table.setColumnMeta(index, key, value)
|
|
550
|
+
table.getColumnMeta(index, key)
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
## Examples
|
|
554
|
+
|
|
555
|
+
The showcase includes 22 interactive examples:
|
|
556
|
+
|
|
557
|
+
1. **Basic Setup** - Headers, data, caption, row manipulation
|
|
558
|
+
2. **Record-Based Data** - Work with typed records by ID
|
|
559
|
+
3. **Inline Editing** - Double-click to edit with data sync
|
|
560
|
+
4. **Sorting** - Column sorting with state tracking
|
|
561
|
+
5. **Search & Filter** - Search and filter with predicates
|
|
562
|
+
6. **Selection** - Row/cell selection with range support
|
|
563
|
+
7. **Cell Navigation** - Keyboard navigation between cells
|
|
564
|
+
8. **Column Visibility** - Show/hide columns
|
|
565
|
+
9. **Reordering** - Move rows and columns
|
|
566
|
+
10. **Undo/Redo** - History tracking with rollback
|
|
567
|
+
11. **Clipboard** - Copy/paste operations
|
|
568
|
+
12. **Row State** - Expand/collapse and pin rows
|
|
569
|
+
13. **Aggregation** - Sum, average, computed columns
|
|
570
|
+
14. **Validation** - Per-cell validation rules
|
|
571
|
+
15. **Pagination** - Page through large datasets
|
|
572
|
+
16. **Import/Export** - CSV and JSON support
|
|
573
|
+
17. **Styling** - Dynamic classes and attributes
|
|
574
|
+
18. **Transactions** - Batch operations with rollback
|
|
575
|
+
19. **Grouping** - Group rows by values
|
|
576
|
+
20. **Footer & Totals** - Summary rows
|
|
577
|
+
21. **HTML Elements** - Rich content in cells
|
|
578
|
+
22. **Pre-existing Sync** - Connect to existing tables
|
|
579
|
+
|
|
580
|
+
## Browser Support
|
|
581
|
+
|
|
582
|
+
Works in all modern browsers that support ES2020+.
|
|
583
|
+
|
|
584
|
+
## License
|
|
585
|
+
|
|
586
|
+
MIT Š Mike Garcia
|