wandertable 0.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.
Files changed (104) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +505 -0
  3. package/dist/style.css +1 -0
  4. package/dist/types/WanderTable.d.ts +112 -0
  5. package/dist/types/WanderTable.d.ts.map +1 -0
  6. package/dist/types/__tests__/ColumnModel.test.d.ts +2 -0
  7. package/dist/types/__tests__/ColumnModel.test.d.ts.map +1 -0
  8. package/dist/types/__tests__/CommandBus.test.d.ts +2 -0
  9. package/dist/types/__tests__/CommandBus.test.d.ts.map +1 -0
  10. package/dist/types/__tests__/DataModel.test.d.ts +2 -0
  11. package/dist/types/__tests__/DataModel.test.d.ts.map +1 -0
  12. package/dist/types/__tests__/Export.test.d.ts +2 -0
  13. package/dist/types/__tests__/Export.test.d.ts.map +1 -0
  14. package/dist/types/__tests__/GroupedHeaders.test.d.ts +2 -0
  15. package/dist/types/__tests__/GroupedHeaders.test.d.ts.map +1 -0
  16. package/dist/types/__tests__/MergeModel.test.d.ts +2 -0
  17. package/dist/types/__tests__/MergeModel.test.d.ts.map +1 -0
  18. package/dist/types/__tests__/SelectionModel.test.d.ts +2 -0
  19. package/dist/types/__tests__/SelectionModel.test.d.ts.map +1 -0
  20. package/dist/types/__tests__/ViewMapping.test.d.ts +2 -0
  21. package/dist/types/__tests__/ViewMapping.test.d.ts.map +1 -0
  22. package/dist/types/columns/ColumnModel.d.ts +25 -0
  23. package/dist/types/columns/ColumnModel.d.ts.map +1 -0
  24. package/dist/types/columns/GroupedHeaders.d.ts +20 -0
  25. package/dist/types/columns/GroupedHeaders.d.ts.map +1 -0
  26. package/dist/types/core/CommandBus.d.ts +63 -0
  27. package/dist/types/core/CommandBus.d.ts.map +1 -0
  28. package/dist/types/core/DataModel.d.ts +27 -0
  29. package/dist/types/core/DataModel.d.ts.map +1 -0
  30. package/dist/types/core/EventEmitter.d.ts +10 -0
  31. package/dist/types/core/EventEmitter.d.ts.map +1 -0
  32. package/dist/types/core/MergeModel.d.ts +35 -0
  33. package/dist/types/core/MergeModel.d.ts.map +1 -0
  34. package/dist/types/core/PaginationModel.d.ts +24 -0
  35. package/dist/types/core/PaginationModel.d.ts.map +1 -0
  36. package/dist/types/core/RowGroupModel.d.ts +36 -0
  37. package/dist/types/core/RowGroupModel.d.ts.map +1 -0
  38. package/dist/types/core/SelectionModel.d.ts +47 -0
  39. package/dist/types/core/SelectionModel.d.ts.map +1 -0
  40. package/dist/types/core/ViewMapping.d.ts +37 -0
  41. package/dist/types/core/ViewMapping.d.ts.map +1 -0
  42. package/dist/types/core/ViewportModel.d.ts +9 -0
  43. package/dist/types/core/ViewportModel.d.ts.map +1 -0
  44. package/dist/types/editors/BlankEditor.d.ts +20 -0
  45. package/dist/types/editors/BlankEditor.d.ts.map +1 -0
  46. package/dist/types/editors/DropdownEditor.d.ts +23 -0
  47. package/dist/types/editors/DropdownEditor.d.ts.map +1 -0
  48. package/dist/types/editors/EditorManager.d.ts +22 -0
  49. package/dist/types/editors/EditorManager.d.ts.map +1 -0
  50. package/dist/types/index.d.ts +35 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/interaction/ClipboardManager.d.ts +19 -0
  53. package/dist/types/interaction/ClipboardManager.d.ts.map +1 -0
  54. package/dist/types/interaction/ColumnReorder.d.ts +20 -0
  55. package/dist/types/interaction/ColumnReorder.d.ts.map +1 -0
  56. package/dist/types/interaction/ColumnResize.d.ts +15 -0
  57. package/dist/types/interaction/ColumnResize.d.ts.map +1 -0
  58. package/dist/types/interaction/FillHandle.d.ts +39 -0
  59. package/dist/types/interaction/FillHandle.d.ts.map +1 -0
  60. package/dist/types/interaction/KeyboardNav.d.ts +19 -0
  61. package/dist/types/interaction/KeyboardNav.d.ts.map +1 -0
  62. package/dist/types/interaction/RowResize.d.ts +15 -0
  63. package/dist/types/interaction/RowResize.d.ts.map +1 -0
  64. package/dist/types/plugins/ContextMenu.d.ts +44 -0
  65. package/dist/types/plugins/ContextMenu.d.ts.map +1 -0
  66. package/dist/types/plugins/Export.d.ts +16 -0
  67. package/dist/types/plugins/Export.d.ts.map +1 -0
  68. package/dist/types/render/AutoFit.d.ts +23 -0
  69. package/dist/types/render/AutoFit.d.ts.map +1 -0
  70. package/dist/types/render/DOMRenderer.d.ts +91 -0
  71. package/dist/types/render/DOMRenderer.d.ts.map +1 -0
  72. package/dist/types/render/LoadingOverlay.d.ts +9 -0
  73. package/dist/types/render/LoadingOverlay.d.ts.map +1 -0
  74. package/dist/types/render/PaginationBar.d.ts +20 -0
  75. package/dist/types/render/PaginationBar.d.ts.map +1 -0
  76. package/dist/types/renderers/BadgeRenderer.d.ts +5 -0
  77. package/dist/types/renderers/BadgeRenderer.d.ts.map +1 -0
  78. package/dist/types/renderers/BlankRenderer.d.ts +9 -0
  79. package/dist/types/renderers/BlankRenderer.d.ts.map +1 -0
  80. package/dist/types/renderers/CheckboxRenderer.d.ts +5 -0
  81. package/dist/types/renderers/CheckboxRenderer.d.ts.map +1 -0
  82. package/dist/types/renderers/NumberRenderer.d.ts +7 -0
  83. package/dist/types/renderers/NumberRenderer.d.ts.map +1 -0
  84. package/dist/types/renderers/ProgressRenderer.d.ts +5 -0
  85. package/dist/types/renderers/ProgressRenderer.d.ts.map +1 -0
  86. package/dist/types/renderers/RendererRegistry.d.ts +10 -0
  87. package/dist/types/renderers/RendererRegistry.d.ts.map +1 -0
  88. package/dist/types/renderers/TextRenderer.d.ts +5 -0
  89. package/dist/types/renderers/TextRenderer.d.ts.map +1 -0
  90. package/dist/types/rows/RowModel.d.ts +20 -0
  91. package/dist/types/rows/RowModel.d.ts.map +1 -0
  92. package/dist/types/themes.d.ts +5 -0
  93. package/dist/types/themes.d.ts.map +1 -0
  94. package/dist/types/types/index.d.ts +348 -0
  95. package/dist/types/types/index.d.ts.map +1 -0
  96. package/dist/wandertable.cjs +14 -0
  97. package/dist/wandertable.cjs.map +1 -0
  98. package/dist/wandertable.global.js +14 -0
  99. package/dist/wandertable.global.js.map +1 -0
  100. package/dist/wandertable.js +2757 -0
  101. package/dist/wandertable.js.map +1 -0
  102. package/dist/wandertable.umd.js +14 -0
  103. package/dist/wandertable.umd.js.map +1 -0
  104. package/package.json +36 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Wilson Glasser
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,505 @@
1
+ # WanderTable
2
+
3
+ [![CI](https://github.com/wilsonglasser/wandertable/actions/workflows/ci.yml/badge.svg)](https://github.com/wilsonglasser/wandertable/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/wandertable)](https://www.npmjs.com/package/wandertable)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+ [![Ko-fi](https://img.shields.io/badge/Ko--fi-wilsonglasser-ff5e5b?logo=ko-fi&logoColor=white)](https://ko-fi.com/wilsonglasser)
7
+ [![Buy Me a Coffee](https://img.shields.io/badge/Buy%20Me%20a%20Coffee-wilsonglasser-ffdd00?logo=buy-me-a-coffee&logoColor=black)](https://buymeacoffee.com/wilsonglasser)
8
+
9
+ High-performance grid/spreadsheet library for the web. Zero dependencies, TypeScript, DOM-virtualized.
10
+
11
+ ## Features
12
+
13
+ - **Two modes**: Spreadsheet (independent cells) and Table (column-driven rows)
14
+ - **Two layouts**: `scroll` (fixed height, internal scroll, virtualized) and `auto` (grows with content, page scroll)
15
+ - **DOM virtualized**: Only visible cells exist in the DOM (~750 nodes max). Recycled on scroll.
16
+ - **Rich cell content**: Each cell is a real `<div>` - any HTML, CSS, hover effects, transitions
17
+ - **Renderers**: text, number, checkbox, badge, progress, blank - or register your own
18
+ - **Editors**: text, dropdown (async, searchable, multi-select), blank - or register your own
19
+ - **Cell-level types**: Each cell can have its own renderer/editor independent of the column
20
+ - **Selection**: Single, range (Shift), multi-range (Ctrl), full row/column, select all
21
+ - **Keyboard navigation**: Arrows, Tab, Enter, Escape, Home/End, Ctrl+arrows, typing to edit
22
+ - **Clipboard**: Copy/paste/cut as TSV (compatible with Excel/Sheets). Single-cell paste fills selection.
23
+ - **Undo/Redo**: Command pattern with configurable stack limit
24
+ - **Sorting**: Per-column, asc/desc, custom comparator, external callback for server-side
25
+ - **Filtering**: Per-column, custom filter functions, external callback for server-side
26
+ - **Merge cells**: Merge/unmerge arbitrary ranges
27
+ - **Frozen panes**: Sticky rows and columns that stay visible on scroll
28
+ - **Grouped headers**: Multi-level column headers (e.g. "Months / Jan Feb Mar")
29
+ - **Pagination**: Built-in pagination bar with page size, total rows, navigation
30
+ - **Context menu**: Dynamic items with `visible`/`disabled` as callbacks, per-target (cell/column/row header)
31
+ - **Resize**: Column and row resize by dragging header edges (individually toggleable)
32
+ - **Auto-fit**: Columns and rows auto-size to content
33
+ - **Auto row height**: Rows automatically grow to fit cell content
34
+ - **Export**: CSV and JSON with download, configurable columns/headers
35
+ - **Themes**: Light, Dark, Minimal presets - or define your own via CSS variables
36
+ - **Loading overlay**: Show/hide spinner overlay for async operations
37
+ - **Events**: Typed event system for cell changes, selection, scroll, commands, editor lifecycle
38
+ - **Command events**: `command:execute`, `command:undo`, `command:redo` for syncing with external databases
39
+ - **Fill handle**: Drag to fill cells in any direction (including diagonal), with custom callback
40
+ - **Column reorder**: Drag headers to reorder columns
41
+ - **Row grouping**: Expand/collapse groups of rows
42
+ - **Infinite mode**: Grid auto-expands on navigate/scroll beyond bounds
43
+ - **Lazy loading**: `onLoadMore` callback when scroll reaches the edge
44
+ - **Sparse data model**: Map-based, only cells with data consume memory. Patches for partial updates.
45
+
46
+ ## Install
47
+
48
+ ```bash
49
+ npm install wandertable
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ### Spreadsheet Mode
55
+
56
+ ```typescript
57
+ import { WanderTable } from 'wandertable';
58
+ import 'wandertable/style.css';
59
+
60
+ const table = new WanderTable(document.getElementById('grid'), {
61
+ mode: 'spreadsheet',
62
+ rowCount: 1000,
63
+ colCount: 26,
64
+ showRowHeaders: true,
65
+ showColHeaders: true,
66
+ });
67
+
68
+ // Set individual cells
69
+ table.setCellValue(0, 0, 'Hello');
70
+ table.setCellValue(0, 1, 42);
71
+
72
+ // Bulk load
73
+ table.applyPatch([
74
+ { row: 1, col: 0, data: { value: 'World', style: { fontWeight: 'bold' } } },
75
+ { row: 1, col: 1, data: { value: true, type: 'checkbox' } },
76
+ { row: 2, col: 0, data: { value: 75, type: 'progress' } },
77
+ { row: 2, col: 1, data: { value: 'active', type: 'badge' } },
78
+ ]);
79
+ ```
80
+
81
+ ### Table Mode
82
+
83
+ ```typescript
84
+ const table = new WanderTable(document.getElementById('grid'), {
85
+ mode: 'table',
86
+ layout: 'auto', // grows with content, scroll on page
87
+ autoRowHeight: true,
88
+ columns: [
89
+ { key: 'name', label: 'Name', width: 180 },
90
+ { key: 'email', label: 'Email', width: 200 },
91
+ { key: 'status', label: 'Status', type: 'badge', width: 100 },
92
+ { key: 'active', label: 'Active', type: 'checkbox', width: 70 },
93
+ ],
94
+ tableData: [
95
+ { name: 'Alice', email: 'alice@test.com', status: 'active', active: true },
96
+ { name: 'Bob', email: 'bob@test.com', status: 'pending', active: false },
97
+ ],
98
+ });
99
+ ```
100
+
101
+ ### Grouped Column Headers
102
+
103
+ ```typescript
104
+ const table = new WanderTable(el, {
105
+ mode: 'table',
106
+ columns: [
107
+ { key: 'name', label: 'Name', width: 180 },
108
+ {
109
+ label: 'Q4 Sales',
110
+ children: [
111
+ { key: 'oct', label: 'Oct', type: 'number', width: 80 },
112
+ { key: 'nov', label: 'Nov', type: 'number', width: 80 },
113
+ { key: 'dec', label: 'Dec', type: 'number', width: 80 },
114
+ ],
115
+ },
116
+ {
117
+ label: 'Metrics',
118
+ children: [
119
+ { key: 'score', label: 'Score', type: 'progress', width: 120 },
120
+ { key: 'active', label: 'Active', type: 'checkbox', width: 70 },
121
+ ],
122
+ },
123
+ ],
124
+ tableData: [...],
125
+ });
126
+ ```
127
+
128
+ ## Options
129
+
130
+ ```typescript
131
+ new WanderTable(container, {
132
+ // ── Mode & Layout ──
133
+ mode: 'spreadsheet' | 'table', // default: 'spreadsheet'
134
+ layout: 'scroll' | 'auto', // default: 'scroll'
135
+
136
+ // ── Data ──
137
+ data: CellPatch[], // initial cell patches (spreadsheet mode)
138
+ tableData: Record<string, unknown>[], // initial row objects (table mode)
139
+ columns: ColumnConfig[], // column definitions
140
+ rowCount: number, // default: 100
141
+ colCount: number, // default: 26
142
+
143
+ // ── Dimensions ──
144
+ defaultRowHeight: number, // default: 28
145
+ defaultColWidth: number, // default: 100
146
+ autoRowHeight: boolean, // auto-fit row height to content. default: false
147
+
148
+ // ── Display ──
149
+ showRowHeaders: boolean, // show row numbers. default: true
150
+ showColHeaders: boolean, // show column letters/labels. default: true
151
+ showGridLines: boolean, // show cell borders. default: true
152
+ showBorder: boolean, // show outer border. default: true
153
+ loading: boolean, // show loading overlay. default: false
154
+
155
+ // ── Frozen Panes ──
156
+ frozenRows: number, // sticky rows at top. default: 0
157
+ frozenCols: number, // sticky columns at left. default: 0
158
+
159
+ // ── Permissions ──
160
+ readOnly: boolean, // no editing at all. default: false
161
+ allowEditing: boolean, // allow cell editing. default: true
162
+ allowSelection: boolean, // allow cell selection. default: true
163
+ allowRangeSelection: boolean, // allow multi-cell selection. default: true
164
+ allowColResize: boolean, // column resize by drag. default: true
165
+ allowRowResize: boolean, // row resize by drag. default: true
166
+ allowClipboard: boolean, // copy/paste/cut. default: true
167
+ allowContextMenu: boolean, // right-click menu. default: true
168
+ allowKeyboardNav: boolean, // keyboard navigation. default: true
169
+ allowUndoRedo: boolean, // undo/redo. default: true
170
+
171
+ // ── Cell Defaults ──
172
+ defaultCellType: string, // renderer for cells without type. default: 'text'
173
+ undoLimit: number, // max undo stack size. default: 100
174
+
175
+ // ── Merge & Sort & Filter ──
176
+ merges: MergeRange[], // initial merged ranges
177
+ sort: { col, direction }, // initial sort
178
+ filters: Record<number, FilterFn>, // initial filters
179
+ pagination: PaginationConfig, // pagination settings
180
+
181
+ // ── Theme ──
182
+ theme: Partial<ThemeConfig>, // or use THEME_DARK, THEME_MINIMAL
183
+
184
+ // ── Callbacks ──
185
+ onCellChange: (row, col, oldValue, newValue) => void,
186
+ onSelectionChange: (ranges) => void,
187
+ onScroll: (viewport) => void,
188
+ onContextMenu: (row, col, event) => void,
189
+ onSort: (col, direction, key?) => boolean | void, // return true for external sort
190
+ onFilter: (col, key, active) => boolean | void, // return true for external filter
191
+ onPageChange: (page, pageSize) => void,
192
+ });
193
+ ```
194
+
195
+ ## API
196
+
197
+ ### Data
198
+
199
+ ```typescript
200
+ table.setCell(row, col, cellData) // set full cell data
201
+ table.getCell(row, col) // get cell data
202
+ table.setCellValue(row, col, value) // set just the value (with undo)
203
+ table.deleteCell(row, col) // remove cell
204
+ table.applyPatch(patches) // bulk update (with undo)
205
+ table.transaction(() => { ... }) // batch changes, single re-render
206
+ table.setTableData(rows) // replace all data (table mode)
207
+ ```
208
+
209
+ ### Selection
210
+
211
+ ```typescript
212
+ table.getSelection() // get selected ranges
213
+ table.getSelectionInfo() // rich info: focus, bounds, cellCount, isFullRow, etc.
214
+ table.setSelection(ranges) // set selection programmatically
215
+ table.scrollTo(row, col) // scroll to make cell visible
216
+ ```
217
+
218
+ ### Undo / Redo
219
+
220
+ ```typescript
221
+ table.undo()
222
+ table.redo()
223
+ table.commands.canUndo // boolean
224
+ table.commands.canRedo // boolean
225
+ table.commands.clear() // clear history
226
+ ```
227
+
228
+ ### Sort & Filter
229
+
230
+ ```typescript
231
+ table.sort(col, 'asc' | 'desc' | null, compareFn?)
232
+ table.clearSort()
233
+ table.getSortState()
234
+
235
+ table.setFilter(col, (row, data) => boolean)
236
+ table.removeFilter(col)
237
+ table.clearFilters()
238
+ table.getActiveFilters() // column indices with active filters
239
+ ```
240
+
241
+ ### Merge
242
+
243
+ ```typescript
244
+ table.mergeCells(row, col, rowSpan, colSpan)
245
+ table.unmergeCells(row, col)
246
+ table.getMerge(row, col) // get merge at cell
247
+ table.clearMerges()
248
+ ```
249
+
250
+ ### Layout
251
+
252
+ ```typescript
253
+ table.setColumnWidth(col, width)
254
+ table.setRowHeight(row, height)
255
+ table.autoFitColumn(col) // auto-size to content
256
+ table.autoFitRow(row) // auto-size to content
257
+ table.insertRow(at)
258
+ table.insertColumn(at)
259
+ table.refresh() // force re-render
260
+ ```
261
+
262
+ ### Export
263
+
264
+ ```typescript
265
+ table.exportCSV(options?) // returns CSV string
266
+ table.exportJSON(options?) // returns object[]
267
+ table.downloadCSV('file.csv', options?)
268
+ table.downloadJSON('file.json', options?)
269
+ ```
270
+
271
+ Options: `{ visibleOnly, columns, includeHeaders, headers }`
272
+
273
+ ### Pagination
274
+
275
+ ```typescript
276
+ table.setPage(page) // 0-based
277
+ table.setPageSize(size)
278
+ table.getPaginationState() // { page, pageSize, totalRows, totalPages, startRow, endRow }
279
+ table.pagination.nextPage()
280
+ table.pagination.prevPage()
281
+ table.pagination.firstPage()
282
+ table.pagination.lastPage()
283
+ ```
284
+
285
+ ### Loading
286
+
287
+ ```typescript
288
+ table.setLoading(true) // show spinner overlay
289
+ table.setLoading(false) // hide
290
+ table.isLoading // boolean
291
+ ```
292
+
293
+ ### Renderers & Editors
294
+
295
+ ```typescript
296
+ // Register custom renderer
297
+ table.registerRenderer('my-type', {
298
+ render(cell: HTMLElement, data: CellData | undefined, state: CellState) {
299
+ cell.innerHTML = `<div class="custom">${data?.value ?? ''}</div>`;
300
+ }
301
+ });
302
+
303
+ // Register custom editor
304
+ table.registerEditor('my-editor', () => ({
305
+ createElement(container) { ... },
306
+ open(value, bounds, cellData) { ... },
307
+ getValue() { ... },
308
+ close() { ... },
309
+ }));
310
+
311
+ // Use per cell
312
+ table.setCell(0, 0, { value: 'hello', type: 'my-type' });
313
+
314
+ // Or per column
315
+ columns: [{ key: 'status', type: 'my-type', editor: 'my-editor' }]
316
+ ```
317
+
318
+ Built-in renderers: `text`, `number`, `checkbox`, `badge`, `progress`, `blank`
319
+
320
+ ### Context Menu
321
+
322
+ ```typescript
323
+ // Override menu items
324
+ table.contextMenuItems = [
325
+ {
326
+ label: 'My Action',
327
+ shortcut: 'Ctrl+M',
328
+ visible: (ctx) => ctx.target === 'cell', // show only for cells
329
+ disabled: (ctx) => ctx.readOnly, // disable when readOnly
330
+ action: (ctx) => console.log(ctx.row, ctx.col),
331
+ },
332
+ { label: '', separator: true },
333
+ {
334
+ label: 'Column Action',
335
+ visible: (ctx) => ctx.target === 'column-header',
336
+ action: (ctx) => console.log('col', ctx.col),
337
+ },
338
+ ];
339
+ ```
340
+
341
+ `MenuContext` fields: `target`, `row`, `col`, `selectedCount`, `isMultiSelect`, `isFullRow`, `isFullCol`, `readOnly`, `canUndo`, `canRedo`
342
+
343
+ ### Events
344
+
345
+ ```typescript
346
+ table.on('cell:change', ({ row, col, oldValue, newValue }) => { ... })
347
+ table.on('cell:click', ({ row, col, event }) => { ... })
348
+ table.on('cell:dblclick', ({ row, col, event }) => { ... })
349
+ table.on('cell:contextmenu', ({ row, col, event }) => { ... })
350
+ table.on('selection:change', ({ ranges }) => { ... })
351
+ table.on('editor:open', ({ row, col }) => { ... })
352
+ table.on('editor:close', ({ row, col, value, cancelled }) => { ... })
353
+ table.on('scroll', ({ viewport }) => { ... })
354
+ table.on('column:resize', ({ col, width }) => { ... })
355
+ table.on('row:resize', ({ row, height }) => { ... })
356
+ table.on('data:patch', ({ patches }) => { ... })
357
+ table.on('command:execute', ({ changes }) => { ... }) // sync with DB
358
+ table.on('command:undo', ({ changes }) => { ... })
359
+ table.on('command:redo', ({ changes }) => { ... })
360
+
361
+ // Unsubscribe
362
+ const unsub = table.on('cell:change', handler);
363
+ unsub();
364
+ ```
365
+
366
+ ### Themes
367
+
368
+ ```typescript
369
+ import { THEME_LIGHT, THEME_DARK, THEME_MINIMAL } from 'wandertable';
370
+
371
+ new WanderTable(el, { theme: THEME_DARK });
372
+ ```
373
+
374
+ Or define your own via CSS variables:
375
+
376
+ ```css
377
+ .wt-root {
378
+ --wt-bg: #0f172a;
379
+ --wt-cell-bg: #1e293b;
380
+ --wt-cell-text: #e2e8f0;
381
+ --wt-header-bg: #0f172a;
382
+ --wt-header-text: #94a3b8;
383
+ --wt-grid-line: #334155;
384
+ --wt-sel-bg: rgba(59, 130, 246, 0.15);
385
+ --wt-sel-border: #3b82f6;
386
+ --wt-focus-border: #60a5fa;
387
+ --wt-font: 'Inter', sans-serif;
388
+ --wt-font-size: 13px;
389
+ --wt-header-font-size: 12px;
390
+ --wt-cell-padding: 8px;
391
+ --wt-header-height: 28px;
392
+ --wt-row-header-width: 50px;
393
+ }
394
+ ```
395
+
396
+ ### Fill Handle
397
+
398
+ Drag the small square at the bottom-right corner of a selection to fill cells.
399
+
400
+ ```typescript
401
+ new WanderTable(el, {
402
+ allowFillHandle: true, // default: true
403
+
404
+ // Optional: override default fill behavior
405
+ onFill: (source, fill, direction) => {
406
+ // direction: 'down' | 'up' | 'right' | 'left' | 'diagonal'
407
+
408
+ // Return CellPatch[] for custom values
409
+ // Return true to cancel fill
410
+ // Return void to use default (repeat pattern)
411
+ },
412
+ });
413
+ ```
414
+
415
+ ### Column Reorder
416
+
417
+ ```typescript
418
+ new WanderTable(el, {
419
+ allowColReorder: true, // drag column headers to reorder
420
+ });
421
+
422
+ table.reorderColumn(fromCol, toCol); // programmatic
423
+ ```
424
+
425
+ ### Row Grouping
426
+
427
+ ```typescript
428
+ new WanderTable(el, {
429
+ rowGroups: [
430
+ { startRow: 0, count: 5, label: 'Group 1', expanded: true },
431
+ { startRow: 6, count: 3, label: 'Group 2', expanded: false },
432
+ ],
433
+ });
434
+
435
+ table.addRowGroup(startRow, count, label);
436
+ table.toggleRowGroup(startRow);
437
+ table.expandAllGroups();
438
+ table.collapseAllGroups();
439
+ ```
440
+
441
+ ### Lazy Loading
442
+
443
+ ```typescript
444
+ new WanderTable(el, {
445
+ onLoadMore: (direction) => {
446
+ // direction: 'down' | 'right'
447
+ fetchMoreData().then(rows => {
448
+ table.applyPatch(rows);
449
+ });
450
+ },
451
+ });
452
+ ```
453
+
454
+ ### Infinite Mode
455
+
456
+ ```typescript
457
+ new WanderTable(el, {
458
+ infinite: true, // grid expands as you navigate/scroll beyond bounds
459
+ rowCount: 50, // initial size
460
+ colCount: 26,
461
+ });
462
+ ```
463
+
464
+ ### Utilities
465
+
466
+ ```typescript
467
+ import { columnLabel } from 'wandertable';
468
+
469
+ columnLabel(0) // 'A'
470
+ columnLabel(25) // 'Z'
471
+ columnLabel(26) // 'AA'
472
+ columnLabel(701) // 'ZZ'
473
+ columnLabel(702) // 'AAA'
474
+ ```
475
+
476
+ ## Build Output
477
+
478
+ | File | Size | Description |
479
+ |------|------|-------------|
480
+ | `dist/wandertable.js` | ~112 KB | ESM module |
481
+ | `dist/wandertable.cjs` | ~85 KB | CommonJS module |
482
+ | `dist/wandertable.umd.js` | ~85 KB | UMD module |
483
+ | `dist/wandertable.global.js` | ~85 KB | IIFE standalone (`window.WanderTable`) |
484
+ | `dist/style.css` | ~8 KB | Styles |
485
+ | `dist/types/` | | TypeScript declarations |
486
+
487
+ ## Development
488
+
489
+ ```bash
490
+ npm install
491
+ npm run dev # dev server at http://localhost:5173
492
+ npm run build # build library (ESM + CJS + types + CSS)
493
+ npm run typecheck # type check
494
+ npm run test # run 47 unit tests
495
+ npm run test:watch # tests in watch mode
496
+ ```
497
+
498
+ ## CI/CD
499
+
500
+ - **CI** (`.github/workflows/ci.yml`): Runs on push/PR to main. Typecheck + tests + build on Node 20 and 22.
501
+ - **Publish** (`.github/workflows/publish.yml`): Publishes to npm on GitHub release. Requires `NPM_TOKEN` secret.
502
+
503
+ ## License
504
+
505
+ MIT
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .wt-root{position:relative;overflow:hidden;background:var(--wt-bg, #ffffff);font-family:var(--wt-font, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif);font-size:var(--wt-font-size, 13px);color:var(--wt-cell-text, #1e293b);outline:none;border:1px solid var(--wt-grid-line, #e2e8f0);box-sizing:border-box;user-select:none;-webkit-user-select:none}.wt-root--no-border{border:none}.wt-root--no-gridlines .wt-cell{border-right:none;border-bottom:none}.wt-root--no-gridlines .wt-header-cell,.wt-root--no-gridlines .wt-corner{border-right:none;border-bottom:1px solid var(--wt-grid-line, #e2e8f0)}.wt-root--auto{overflow:visible;height:auto!important}.wt-root *,.wt-root *:before,.wt-root *:after{box-sizing:border-box}.wt-scroll-container{position:absolute;overflow:auto;will-change:scroll-position}.wt-scroll-sentinel{position:absolute;top:0;left:0;pointer-events:none;visibility:hidden}.wt-viewport{position:absolute;top:0;left:0;width:100%;pointer-events:auto}.wt-row{position:absolute;top:0;left:0;width:100%;contain:layout style}.wt-cell{position:absolute;top:0;overflow:hidden;contain:layout style paint;display:flex;align-items:center;padding:0 var(--wt-cell-padding, 8px);background:var(--wt-cell-bg, #ffffff);border-right:1px solid var(--wt-grid-line, #e2e8f0);border-bottom:1px solid var(--wt-grid-line, #e2e8f0);white-space:nowrap;text-overflow:ellipsis;line-height:1.4;cursor:default}.wt-cell--selected{background:var(--wt-sel-bg, rgba(59, 130, 246, .08))}.wt-cell--focused{box-shadow:inset 0 0 0 2px var(--wt-focus-border, #3b82f6);z-index:1}.wt-corner{position:absolute;top:0;left:0;z-index:3;background:var(--wt-header-bg, #f8fafc);border-right:1px solid var(--wt-grid-line, #e2e8f0);border-bottom:1px solid var(--wt-grid-line, #e2e8f0);cursor:pointer}.wt-corner:hover{background:#e2e8f0}.wt-col-headers{position:absolute;top:0;overflow:hidden;z-index:2}.wt-col-headers-inner{position:relative;height:100%}.wt-row-headers{position:absolute;left:0;overflow:hidden;z-index:2}.wt-row-headers-inner{position:relative;width:100%}.wt-header-cell{position:absolute;display:flex;align-items:center;justify-content:center;font-size:var(--wt-header-font-size, 12px);font-weight:500;color:var(--wt-header-text, #475569);background:var(--wt-header-bg, #f8fafc);border-right:1px solid var(--wt-grid-line, #e2e8f0);border-bottom:1px solid var(--wt-grid-line, #e2e8f0);-webkit-user-select:none;user-select:none;cursor:pointer}.wt-header-cell:hover{background:#e2e8f0}.wt-col-header-cell{top:0;cursor:pointer}.wt-col-header-cell:after{content:"";position:absolute;right:0;top:0;width:5px;height:100%;cursor:col-resize}.wt-row-header-cell{left:0}.wt-editor-overlay{position:absolute;z-index:10;pointer-events:auto}.wt-editor-input{width:100%;height:100%;border:2px solid var(--wt-focus-border, #3b82f6);outline:none;font-family:inherit;font-size:inherit;padding:0 var(--wt-cell-padding, 8px);box-sizing:border-box;background:#fff}.wt-dropdown{position:absolute;top:100%;left:0;min-width:100%;max-height:240px;background:#fff;border:1px solid var(--wt-grid-line, #e2e8f0);border-radius:6px;box-shadow:0 4px 16px #0000001f;overflow:hidden;z-index:20}.wt-dropdown-search{width:100%;padding:8px 12px;border:none;border-bottom:1px solid var(--wt-grid-line, #e2e8f0);outline:none;font-family:inherit;font-size:inherit}.wt-dropdown-list{max-height:200px;overflow-y:auto}.wt-dropdown-item{padding:6px 12px;cursor:pointer;display:flex;align-items:center;gap:8px}.wt-dropdown-item:hover,.wt-dropdown-item--highlight{background:var(--wt-sel-bg, rgba(59, 130, 246, .08))}.wt-dropdown-item--selected{background:#3b82f61f;font-weight:500}.wt-dropdown-item--disabled{opacity:.5;cursor:not-allowed}.wt-dropdown-icon{flex-shrink:0}.wt-scroll-container::-webkit-scrollbar{width:8px;height:8px}.wt-scroll-container::-webkit-scrollbar-track{background:transparent}.wt-scroll-container::-webkit-scrollbar-thumb{background:#cbd5e1;border-radius:4px}.wt-scroll-container::-webkit-scrollbar-thumb:hover{background:#94a3b8}.wt-scroll-container::-webkit-scrollbar-corner{background:transparent}.wt-checkbox{display:flex;align-items:center;justify-content:center;width:100%;cursor:pointer}.wt-checkbox-box{width:16px;height:16px;border:1.5px solid #94a3b8;border-radius:3px;display:flex;align-items:center;justify-content:center;transition:all .15s}.wt-checkbox-box--checked{background:#3b82f6;border-color:#3b82f6;color:#fff}.wt-badge{display:inline-block;padding:2px 8px;border-radius:9999px;font-size:11px;font-weight:500;line-height:1.4;white-space:nowrap}.wt-progress{display:flex;align-items:center;gap:8px;width:100%;position:relative}.wt-progress-bar{height:6px;border-radius:3px;flex:1;overflow:hidden;background:#e2e8f0}.wt-progress-label{font-size:11px;color:#64748b;min-width:32px;text-align:right}.wt-context-menu{position:fixed;z-index:50;min-width:180px;background:#fff;border:1px solid #e2e8f0;border-radius:8px;box-shadow:0 4px 24px #00000026;padding:4px 0;font-size:13px}.wt-context-menu-item{display:flex;align-items:center;gap:8px;padding:6px 12px;cursor:pointer;color:#334155}.wt-context-menu-item:hover{background:#f1f5f9}.wt-context-menu-item--disabled{opacity:.4;cursor:not-allowed}.wt-context-menu-item--disabled:hover{background:none}.wt-context-menu-shortcut{margin-left:auto;font-size:11px;color:#94a3b8}.wt-context-menu-separator{height:1px;background:#e2e8f0;margin:4px 0}.wt-fill-handle{position:absolute;width:8px;height:8px;background:var(--wt-focus-border, #3b82f6);border:1px solid #fff;border-radius:1px;cursor:crosshair;z-index:5;pointer-events:auto}.wt-fill-handle:hover{transform:scale(1.3)}.wt-fill-shadow{position:absolute;border:2px dashed var(--wt-focus-border, #3b82f6);background:#3b82f60f;pointer-events:none;z-index:4}.wt-reorder-ghost{position:fixed;padding:4px 12px;background:var(--wt-header-bg, #f8fafc);border:1px solid var(--wt-grid-line, #e2e8f0);border-radius:4px;box-shadow:0 4px 12px #00000026;font-size:var(--wt-header-font-size, 12px);font-weight:500;color:var(--wt-header-text, #475569);pointer-events:none;z-index:100;transform:translate(-50%);opacity:.9}.wt-reorder-indicator{position:absolute;top:0;width:2px;height:100%;background:var(--wt-focus-border, #3b82f6);z-index:10;pointer-events:none}.wt-group-toggle{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:1px solid #cbd5e1;border-radius:3px;background:#fff;cursor:pointer;font-size:10px;color:#64748b;margin-right:4px;flex-shrink:0}.wt-group-toggle:hover{background:#f1f5f9;border-color:#94a3b8}.wt-row-header-cell:after{content:"";position:absolute;bottom:0;left:0;width:100%;height:4px;cursor:row-resize}.wt-col-header-group{position:absolute;display:flex;align-items:center;justify-content:center;font-size:var(--wt-header-font-size, 12px);font-weight:600;color:var(--wt-header-text, #475569);background:var(--wt-header-bg, #f8fafc);border-right:1px solid var(--wt-grid-line, #e2e8f0);border-bottom:1px solid var(--wt-grid-line, #e2e8f0);-webkit-user-select:none;user-select:none}.wt-pagination{display:flex;align-items:center;gap:8px;padding:6px 12px;background:var(--wt-header-bg, #f8fafc);border-top:1px solid var(--wt-grid-line, #e2e8f0);font-size:12px;color:var(--wt-header-text, #475569);-webkit-user-select:none;user-select:none}.wt-pagination-info{margin-right:auto}.wt-pagination-btn{width:28px;height:28px;display:flex;align-items:center;justify-content:center;border:1px solid var(--wt-grid-line, #e2e8f0);border-radius:4px;background:var(--wt-cell-bg, #fff);color:var(--wt-cell-text, #1e293b);font-size:14px;cursor:pointer;padding:0}.wt-pagination-btn:hover:not(:disabled){background:#e2e8f0}.wt-pagination-btn:disabled{opacity:.3;cursor:not-allowed}.wt-pagination-input{width:48px;height:28px;text-align:center;border:1px solid var(--wt-grid-line, #e2e8f0);border-radius:4px;font-size:12px;outline:none;-moz-appearance:textfield}.wt-pagination-input::-webkit-inner-spin-button,.wt-pagination-input::-webkit-outer-spin-button{-webkit-appearance:none}.wt-loading-overlay{position:absolute;top:0;right:0;bottom:0;left:0;z-index:50;background:#ffffffb3;display:flex;align-items:center;justify-content:center;pointer-events:all}.wt-loading-spinner{width:32px;height:32px;border:3px solid #e2e8f0;border-top-color:#3b82f6;border-radius:50%;animation:wt-spin .6s linear infinite}@keyframes wt-spin{to{transform:rotate(360deg)}}
@@ -0,0 +1,112 @@
1
+ import type { CellData, CellEditor, CellPatch, CellRenderer, SelectionRange, Viewport, WanderTableEvents, WanderTableOptions } from './types/index.js';
2
+ import { DataModel } from './core/DataModel.js';
3
+ import { EventEmitter } from './core/EventEmitter.js';
4
+ import { SelectionModel } from './core/SelectionModel.js';
5
+ import { CommandBus } from './core/CommandBus.js';
6
+ import { ColumnModel } from './columns/ColumnModel.js';
7
+ import { RowModel } from './rows/RowModel.js';
8
+ import { type MenuItemConfig } from './plugins/ContextMenu.js';
9
+ import { RowGroupModel } from './core/RowGroupModel.js';
10
+ import { ViewMapping, type SortDirection, type FilterFn } from './core/ViewMapping.js';
11
+ import { MergeModel, type MergeRange } from './core/MergeModel.js';
12
+ import { PaginationModel } from './core/PaginationModel.js';
13
+ import { type ExportOptions } from './plugins/Export.js';
14
+ export declare class WanderTable extends EventEmitter<WanderTableEvents> {
15
+ readonly data: DataModel;
16
+ readonly selection: SelectionModel;
17
+ readonly columns: ColumnModel;
18
+ readonly rows: RowModel;
19
+ readonly commands: CommandBus;
20
+ readonly viewMapping: ViewMapping;
21
+ readonly merges: MergeModel;
22
+ readonly pagination: PaginationModel;
23
+ readonly rowGroups: RowGroupModel;
24
+ private renderer;
25
+ private rendererRegistry;
26
+ private editorManager;
27
+ private keyboard;
28
+ private clipboard;
29
+ private columnResize;
30
+ private rowResize;
31
+ private contextMenu;
32
+ private autoFit;
33
+ private paginationBar;
34
+ private loadingOverlay;
35
+ private fillHandle;
36
+ private columnReorder;
37
+ private headerRows;
38
+ private theme;
39
+ private options;
40
+ private isDragging;
41
+ private _infiniteScrollHandler;
42
+ private _lazyLoadHandler;
43
+ private _keydownHandler;
44
+ constructor(container: HTMLElement, options?: WanderTableOptions);
45
+ private wireCallbacks;
46
+ /** Override this to customize context menu items */
47
+ contextMenuItems: MenuItemConfig[];
48
+ private buildMenuContext;
49
+ private openContextMenu;
50
+ setCell(row: number, col: number, data: CellData): void;
51
+ getCell(row: number, col: number): CellData | undefined;
52
+ setCellValue(row: number, col: number, value: unknown): void;
53
+ deleteCell(row: number, col: number): void;
54
+ applyPatch(patches: CellPatch[]): void;
55
+ transaction(fn: () => void): void;
56
+ undo(): void;
57
+ redo(): void;
58
+ getSelection(): SelectionRange[];
59
+ getSelectionInfo(): import("./index.js").SelectionInfo;
60
+ setSelection(ranges: SelectionRange[]): void;
61
+ scrollTo(row: number, col: number): void;
62
+ registerRenderer(name: string, renderer: CellRenderer): void;
63
+ registerEditor(name: string, factory: () => CellEditor): void;
64
+ setColumnWidth(col: number, width: number): void;
65
+ setRowHeight(row: number, height: number): void;
66
+ autoFitColumn(col: number): void;
67
+ autoFitRow(row: number): void;
68
+ insertRow(at: number): void;
69
+ insertColumn(at: number): void;
70
+ sort(col: number, direction: SortDirection, compareFn?: (a: unknown, b: unknown) => number): void;
71
+ clearSort(): void;
72
+ getSortState(): import("./index.js").SortState | null;
73
+ setFilter(col: number, fn: FilterFn): void;
74
+ removeFilter(col: number): void;
75
+ clearFilters(): void;
76
+ getActiveFilters(): number[];
77
+ mergeCells(row: number, col: number, rowSpan: number, colSpan: number): void;
78
+ unmergeCells(row: number, col: number): void;
79
+ getMerge(row: number, col: number): MergeRange | null;
80
+ clearMerges(): void;
81
+ addRowGroup(startRow: number, count: number, label?: string, expanded?: boolean): void;
82
+ removeRowGroup(startRow: number): void;
83
+ toggleRowGroup(startRow: number): void;
84
+ expandAllGroups(): void;
85
+ collapseAllGroups(): void;
86
+ exportCSV(options?: ExportOptions): string;
87
+ exportJSON(options?: ExportOptions): object[];
88
+ downloadCSV(filename?: string, options?: ExportOptions): void;
89
+ downloadJSON(filename?: string, options?: ExportOptions): void;
90
+ getViewport(): Viewport;
91
+ refresh(): void;
92
+ destroy(): void;
93
+ private startEditing;
94
+ private deleteSelected;
95
+ private pasteWithUndo;
96
+ private cutWithUndo;
97
+ private execCommand;
98
+ /** Move a column from one index to another, shifting data */
99
+ reorderColumn(fromCol: number, toCol: number): void;
100
+ private expandIfNeeded;
101
+ private isFullRowOrColSelected;
102
+ /** Load row objects into the sparse DataModel using column keys */
103
+ private loadTableData;
104
+ /** Replace all table data (table mode) */
105
+ setTableData(rows: Record<string, unknown>[]): void;
106
+ setLoading(loading: boolean): void;
107
+ get isLoading(): boolean;
108
+ setPage(page: number): void;
109
+ setPageSize(size: number): void;
110
+ getPaginationState(): import("./index.js").PaginationState;
111
+ }
112
+ //# sourceMappingURL=WanderTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WanderTable.d.ts","sourceRoot":"","sources":["../../src/WanderTable.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ColumnModel.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ColumnModel.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/ColumnModel.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CommandBus.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandBus.test.d.ts","sourceRoot":"","sources":["../../../src/__tests__/CommandBus.test.ts"],"names":[],"mappings":""}