@zvndev/yable-vanilla 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.
package/README.md ADDED
@@ -0,0 +1,122 @@
1
+ # @zvndev/yable-vanilla
2
+
3
+ Vanilla JavaScript / DOM renderer for the Yable data table engine.
4
+
5
+ `@zvndev/yable-vanilla` takes a `@zvndev/yable-core` table instance and returns an HTML string you can inject into any container. No framework required.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @zvndev/yable-core @zvndev/yable-vanilla
11
+ ```
12
+
13
+ ## Basic Usage
14
+
15
+ ```typescript
16
+ import { createTable, createColumnHelper } from '@zvndev/yable-core'
17
+ import { renderTable, renderPagination } from '@zvndev/yable-vanilla'
18
+ import '@zvndev/yable-themes'
19
+
20
+ interface Product {
21
+ name: string
22
+ price: number
23
+ stock: number
24
+ }
25
+
26
+ const columnHelper = createColumnHelper<Product>()
27
+ const columns = [
28
+ columnHelper.accessor('name', { header: 'Product' }),
29
+ columnHelper.accessor('price', { header: 'Price', enableSorting: true }),
30
+ columnHelper.accessor('stock', { header: 'Stock' }),
31
+ ]
32
+
33
+ const data: Product[] = [
34
+ { name: 'Widget', price: 9.99, stock: 150 },
35
+ { name: 'Gadget', price: 24.99, stock: 75 },
36
+ ]
37
+
38
+ const table = createTable({
39
+ data,
40
+ columns,
41
+ onStateChange: () => render(), // Re-render on state change
42
+ })
43
+
44
+ function render() {
45
+ const container = document.getElementById('table-container')!
46
+ container.innerHTML = renderTable(table, {
47
+ striped: true,
48
+ stickyHeader: true,
49
+ }) + renderPagination(table)
50
+ }
51
+
52
+ render()
53
+ ```
54
+
55
+ ## API
56
+
57
+ ### `renderTable(table, options?): string`
58
+
59
+ Returns the full table HTML string.
60
+
61
+ **Options:**
62
+
63
+ | Option | Type | Default | Description |
64
+ |---|---|---|---|
65
+ | `stickyHeader` | `boolean` | `false` | Pin header row on scroll |
66
+ | `striped` | `boolean` | `false` | Alternate row backgrounds |
67
+ | `bordered` | `boolean` | `false` | Add cell borders |
68
+ | `compact` | `boolean` | `false` | Reduce padding |
69
+ | `theme` | `string` | -- | Theme class name suffix |
70
+ | `clickableRows` | `boolean` | `false` | Add clickable row styling |
71
+ | `footer` | `boolean` | `false` | Render table footer |
72
+ | `emptyMessage` | `string` | `"No data"` | Text when there are no rows |
73
+
74
+ ### `renderPagination(table, options?): string`
75
+
76
+ Returns pagination controls as an HTML string.
77
+
78
+ **Options:**
79
+
80
+ | Option | Type | Default | Description |
81
+ |---|---|---|---|
82
+ | `showPageSize` | `boolean` | `true` | Show page size dropdown |
83
+ | `pageSizes` | `number[]` | `[10, 20, 50, 100]` | Available page sizes |
84
+ | `showInfo` | `boolean` | `true` | Show row count info |
85
+
86
+ ## Event Wiring
87
+
88
+ The rendered HTML uses `data-yable-*` attributes for event delegation. You wire up interactivity by attaching listeners to the container:
89
+
90
+ ```typescript
91
+ container.addEventListener('click', (e) => {
92
+ const target = e.target as HTMLElement
93
+
94
+ // Sort on header click
95
+ const columnId = target.closest('[data-yable-sortable="true"]')
96
+ ?.getAttribute('data-yable-column')
97
+ if (columnId) {
98
+ const column = table.getColumn(columnId)
99
+ column?.toggleSorting()
100
+ render()
101
+ }
102
+
103
+ // Pagination
104
+ const page = target.closest('[data-yable-page]')
105
+ ?.getAttribute('data-yable-page')
106
+ if (page === 'prev') table.previousPage()
107
+ else if (page === 'next') table.nextPage()
108
+ else if (page === 'first') table.firstPage()
109
+ else if (page === 'last') table.lastPage()
110
+ else if (page) table.setPageIndex(Number(page))
111
+
112
+ render()
113
+ })
114
+ ```
115
+
116
+ ## Editing Support
117
+
118
+ The renderer automatically outputs form elements (`<input>`, `<select>`, `<input type="checkbox">`) for cells in edit mode. Use `data-yable-input` and `data-yable-input-row` attributes to wire change events back to the table's `setPendingValue()` method.
119
+
120
+ ## License
121
+
122
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,572 @@
1
+ 'use strict';
2
+
3
+ var yableCore = require('@zvndev/yable-core');
4
+
5
+ // src/index.ts
6
+
7
+ // src/renderer.ts
8
+ function esc(value) {
9
+ const s = String(value ?? "");
10
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
11
+ }
12
+ function escAttr(value) {
13
+ const s = String(value ?? "");
14
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15
+ }
16
+ function renderTable(table, opts = {}) {
17
+ const classes = [
18
+ "yable",
19
+ opts.theme && `yable-theme-${escAttr(opts.theme)}`,
20
+ opts.stickyHeader && "yable--sticky-header",
21
+ opts.striped && "yable--striped",
22
+ opts.bordered && "yable--bordered",
23
+ opts.compact && "yable--compact"
24
+ ].filter(Boolean).join(" ");
25
+ const rows = table.getRowModel().rows;
26
+ let html = `<div class="${classes}" role="grid" aria-rowcount="${rows.length}" aria-colcount="${table.getVisibleLeafColumns().length}">`;
27
+ html += '<table class="yable-table">';
28
+ html += renderHeader(table);
29
+ html += renderBody(table, opts.clickableRows);
30
+ if (opts.footer) {
31
+ html += renderFooter(table);
32
+ }
33
+ html += "</table>";
34
+ if (rows.length === 0) {
35
+ html += `<div class="yable-empty"><div class="yable-empty-message">${esc(opts.emptyMessage ?? "No data")}</div></div>`;
36
+ }
37
+ html += "</div>";
38
+ return html;
39
+ }
40
+ function renderHeader(table) {
41
+ const headerGroups = table.getHeaderGroups();
42
+ let html = '<thead class="yable-thead">';
43
+ for (const headerGroup of headerGroups) {
44
+ html += `<tr class="yable-tr" data-yable-header-group="${escAttr(headerGroup.id)}">`;
45
+ for (const header of headerGroup.headers) {
46
+ html += renderHeaderCell(header);
47
+ }
48
+ html += "</tr>";
49
+ }
50
+ html += "</thead>";
51
+ return html;
52
+ }
53
+ function renderHeaderCell(header) {
54
+ if (header.isPlaceholder) {
55
+ return `<th class="yable-th" colspan="${header.colSpan}"></th>`;
56
+ }
57
+ const column = header.column;
58
+ const sortable = column.getCanSort();
59
+ const sortDir = column.getIsSorted();
60
+ const pinned = column.getIsPinned();
61
+ const classes = [
62
+ "yable-th",
63
+ sortable && "yable-th--sortable",
64
+ sortDir && `yable-th--sorted-${sortDir}`,
65
+ pinned && `yable-th--pinned-${pinned}`
66
+ ].filter(Boolean).join(" ");
67
+ const style = pinned ? getPinStyle(column) : "";
68
+ const ariaSort = sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : sortable ? "none" : void 0;
69
+ const ariaSortAttr = ariaSort ? ` aria-sort="${ariaSort}"` : "";
70
+ const headerDef = column.columnDef.header;
71
+ const content = typeof headerDef === "function" ? String(headerDef(header.getContext())) : String(headerDef ?? column.id);
72
+ let sortIndicator = "";
73
+ if (sortable) {
74
+ if (sortDir) {
75
+ sortIndicator = `<span class="yable-sort-indicator" data-active="true" data-direction="${sortDir}" aria-hidden="true">${sortDir === "asc" ? "&#9650;" : "&#9660;"}</span>`;
76
+ } else {
77
+ sortIndicator = `<span class="yable-sort-indicator" aria-hidden="true">&#9650;&#9660;</span>`;
78
+ }
79
+ }
80
+ let resizeHandle = "";
81
+ if (column.getCanResize()) {
82
+ resizeHandle = `<div class="yable-resize-handle" data-yable-resize="${escAttr(column.id)}"></div>`;
83
+ }
84
+ return `<th class="${classes}"${ariaSortAttr}${style ? ` style="${style}"` : ""} data-yable-column="${escAttr(column.id)}" data-yable-sortable="${sortable}" colspan="${header.colSpan}">${esc(content)}${sortIndicator}${resizeHandle}</th>`;
85
+ }
86
+ function renderBody(table, clickableRows) {
87
+ const rows = table.getRowModel().rows;
88
+ let html = '<tbody class="yable-tbody">';
89
+ for (const row of rows) {
90
+ html += renderRow(table, row, clickableRows);
91
+ }
92
+ html += "</tbody>";
93
+ return html;
94
+ }
95
+ function renderRow(table, row, clickable) {
96
+ const isSelected = row.getIsSelected();
97
+ const isExpanded = row.getIsExpanded();
98
+ const classes = [
99
+ "yable-tr",
100
+ clickable && "yable-tr--clickable",
101
+ isSelected && "yable-tr--selected"
102
+ ].filter(Boolean).join(" ");
103
+ let html = `<tr class="${classes}" data-yable-row="${escAttr(row.id)}"${isSelected ? ' aria-selected="true"' : ""}>`;
104
+ const cells = row.getVisibleCells();
105
+ for (const cell of cells) {
106
+ html += renderCell(table, cell);
107
+ }
108
+ html += "</tr>";
109
+ if (isExpanded) {
110
+ const colSpan = table.getVisibleLeafColumns().length;
111
+ html += `<tr class="yable-tr yable-tr--detail" data-yable-detail="${escAttr(row.id)}"><td class="yable-td yable-td--detail" colspan="${colSpan}"><div class="yable-detail-content" data-yable-detail-content="${escAttr(row.id)}"></div></td></tr>`;
112
+ }
113
+ return html;
114
+ }
115
+ function renderCell(table, cell) {
116
+ const column = cell.column;
117
+ const pinned = column.getIsPinned();
118
+ const isEditing = cell.getIsEditing();
119
+ const isAlwaysEditable = cell.getIsAlwaysEditable();
120
+ const classes = [
121
+ "yable-td",
122
+ pinned && `yable-td--pinned-${pinned}`,
123
+ (isEditing || isAlwaysEditable) && "yable-td--editing"
124
+ ].filter(Boolean).join(" ");
125
+ const style = pinned ? getPinStyle(column) : "";
126
+ const editConfig = column.columnDef.editConfig;
127
+ if (isEditing || isAlwaysEditable) {
128
+ if (editConfig) {
129
+ const value2 = table.getPendingValue(cell.row.id, column.id) ?? cell.getValue();
130
+ return `<td class="${classes}"${style ? ` style="${style}"` : ""} data-yable-cell="${escAttr(column.id)}" data-yable-row="${escAttr(cell.row.id)}">${renderFormElement(editConfig, value2, cell.row.id, column.id)}</td>`;
131
+ }
132
+ }
133
+ const value = cell.renderValue();
134
+ return `<td class="${classes}"${style ? ` style="${style}"` : ""} data-yable-cell="${escAttr(column.id)}" data-yable-row="${escAttr(cell.row.id)}">${esc(value)}</td>`;
135
+ }
136
+ function renderFooter(table) {
137
+ const footerGroups = table.getFooterGroups();
138
+ if (!footerGroups.length) return "";
139
+ let html = '<tfoot class="yable-tfoot">';
140
+ for (const footerGroup of footerGroups) {
141
+ html += '<tr class="yable-tr">';
142
+ for (const header of footerGroup.headers) {
143
+ const footerDef = header.column.columnDef.footer;
144
+ const content = header.isPlaceholder ? "" : typeof footerDef === "function" ? String(footerDef(header.getContext())) : String(footerDef ?? "");
145
+ html += `<td class="yable-td" colspan="${header.colSpan}">${esc(content)}</td>`;
146
+ }
147
+ html += "</tr>";
148
+ }
149
+ html += "</tfoot>";
150
+ return html;
151
+ }
152
+ function renderFormElement(editConfig, value, rowId, columnId) {
153
+ const type = editConfig.type ?? "text";
154
+ switch (type) {
155
+ case "select":
156
+ return renderSelect(editConfig.options ?? [], value, rowId, columnId, editConfig.placeholder);
157
+ case "checkbox":
158
+ return `<input type="checkbox" class="yable-checkbox" data-yable-input="${escAttr(columnId)}" data-yable-input-row="${escAttr(rowId)}"${value ? " checked" : ""} />`;
159
+ case "toggle":
160
+ return `<input type="checkbox" role="switch" class="yable-toggle" data-yable-input="${escAttr(columnId)}" data-yable-input-row="${escAttr(rowId)}"${value ? " checked" : ""} aria-checked="${Boolean(value)}" />`;
161
+ case "date":
162
+ case "datetime-local":
163
+ case "time":
164
+ return `<input type="${esc(type)}" class="yable-input" data-yable-input="${escAttr(columnId)}" data-yable-input-row="${escAttr(rowId)}" value="${esc(formatDateValue(value, type))}" />`;
165
+ default:
166
+ return `<input type="${esc(type)}" class="yable-input" data-yable-input="${escAttr(columnId)}" data-yable-input-row="${escAttr(rowId)}" value="${esc(String(value ?? ""))}"${editConfig.placeholder ? ` placeholder="${esc(editConfig.placeholder)}"` : ""} />`;
167
+ }
168
+ }
169
+ function renderSelect(options, value, rowId, columnId, placeholder) {
170
+ let html = `<select class="yable-select" data-yable-input="${escAttr(columnId)}" data-yable-input-row="${escAttr(rowId)}">`;
171
+ if (placeholder) {
172
+ html += `<option value="" disabled>${esc(placeholder)}</option>`;
173
+ }
174
+ for (const opt of options) {
175
+ const selected = String(opt.value) === String(value) ? " selected" : "";
176
+ html += `<option value="${esc(opt.value)}"${selected}>${esc(opt.label)}</option>`;
177
+ }
178
+ html += "</select>";
179
+ return html;
180
+ }
181
+ function formatDateValue(value, type) {
182
+ if (!value) return "";
183
+ if (typeof value === "string") return value;
184
+ if (value instanceof Date) {
185
+ if (type === "date") return value.toISOString().split("T")[0];
186
+ if (type === "datetime-local") return value.toISOString().slice(0, 16);
187
+ if (type === "time") return value.toISOString().slice(11, 16);
188
+ }
189
+ return String(value);
190
+ }
191
+ function getPinStyle(column) {
192
+ const pinned = column.getIsPinned();
193
+ if (!pinned) return "";
194
+ const offset = column.getPinnedIndex() * 150;
195
+ return `position: sticky; ${pinned}: ${offset}px; z-index: 1;`;
196
+ }
197
+ function renderPagination(table, opts = {}) {
198
+ const { showPageSize = true, pageSizes = [10, 20, 50, 100], showInfo = true } = opts;
199
+ const { pageIndex, pageSize } = table.getState().pagination;
200
+ const pageCount = table.getPageCount();
201
+ const totalRows = table.getPrePaginationRowModel().rows.length;
202
+ const from = pageIndex * pageSize + 1;
203
+ const to = Math.min((pageIndex + 1) * pageSize, totalRows);
204
+ let html = '<div class="yable-pagination">';
205
+ if (showInfo) {
206
+ html += '<div class="yable-pagination-info">';
207
+ html += totalRows > 0 ? `<span>${from}\u2013${to} of ${totalRows}</span>` : "<span>No results</span>";
208
+ if (showPageSize) {
209
+ html += '<select class="yable-pagination-select" data-yable-pagesize>';
210
+ for (const size of pageSizes) {
211
+ html += `<option value="${size}"${size === pageSize ? " selected" : ""}>${size} rows</option>`;
212
+ }
213
+ html += "</select>";
214
+ }
215
+ html += "</div>";
216
+ }
217
+ html += '<div class="yable-pagination-pages">';
218
+ html += `<button class="yable-pagination-btn" data-yable-page="first"${!table.getCanPreviousPage() ? " disabled" : ""} aria-label="First page">&#171;</button>`;
219
+ html += `<button class="yable-pagination-btn" data-yable-page="prev"${!table.getCanPreviousPage() ? " disabled" : ""} aria-label="Previous page">&#8249;</button>`;
220
+ const pages = getPageNumbers(pageIndex, pageCount);
221
+ for (let i = 0; i < pages.length; i++) {
222
+ const page = pages[i];
223
+ if (page === -1) {
224
+ html += '<span class="yable-pagination-btn" style="cursor:default;opacity:0.5">...</span>';
225
+ } else {
226
+ html += `<button class="yable-pagination-btn" data-yable-page="${page}"${page === pageIndex ? ' data-active="true" aria-current="page"' : ""} aria-label="Page ${page + 1}">${page + 1}</button>`;
227
+ }
228
+ }
229
+ html += `<button class="yable-pagination-btn" data-yable-page="next"${!table.getCanNextPage() ? " disabled" : ""} aria-label="Next page">&#8250;</button>`;
230
+ html += `<button class="yable-pagination-btn" data-yable-page="last"${!table.getCanNextPage() ? " disabled" : ""} aria-label="Last page">&#187;</button>`;
231
+ html += "</div></div>";
232
+ return html;
233
+ }
234
+ function getPageNumbers(current, total) {
235
+ if (total <= 7) return Array.from({ length: total }, (_, i) => i);
236
+ const pages = [0];
237
+ if (current > 3) pages.push(-1);
238
+ const start = Math.max(1, current - 1);
239
+ const end = Math.min(total - 2, current + 1);
240
+ for (let i = start; i <= end; i++) pages.push(i);
241
+ if (current < total - 4) pages.push(-1);
242
+ pages.push(total - 1);
243
+ return pages;
244
+ }
245
+
246
+ // src/events.ts
247
+ function attachEventDelegation(root, table, handlers, onUpdate) {
248
+ const abortController = new AbortController();
249
+ const signal = abortController.signal;
250
+ root.addEventListener(
251
+ "click",
252
+ (e) => {
253
+ const target = e.target;
254
+ const pageBtn = target.closest("[data-yable-page]");
255
+ if (pageBtn) {
256
+ const page = pageBtn.dataset.yablePage;
257
+ if (page === "first") table.setPageIndex(0);
258
+ else if (page === "last") table.setPageIndex(table.getPageCount() - 1);
259
+ else if (page === "prev") table.previousPage();
260
+ else if (page === "next") table.nextPage();
261
+ else table.setPageIndex(Number(page));
262
+ onUpdate();
263
+ return;
264
+ }
265
+ const th = target.closest('[data-yable-sortable="true"]');
266
+ if (th) {
267
+ const columnId = th.dataset.yableColumn;
268
+ const column = table.getColumn(columnId);
269
+ if (column) {
270
+ column.toggleSorting();
271
+ onUpdate();
272
+ }
273
+ handlers["header:click"]?.({
274
+ type: "header:click",
275
+ columnId,
276
+ originalEvent: e
277
+ });
278
+ return;
279
+ }
280
+ const td = target.closest("[data-yable-cell]");
281
+ if (td) {
282
+ const columnId = td.dataset.yableCell;
283
+ const rowId = td.dataset.yableRow;
284
+ handlers["cell:click"]?.({
285
+ type: "cell:click",
286
+ rowId,
287
+ columnId,
288
+ originalEvent: e
289
+ });
290
+ }
291
+ const tr = target.closest("[data-yable-row]");
292
+ if (tr && !target.closest("input, select, button")) {
293
+ const rowId = tr.dataset.yableRow;
294
+ handlers["row:click"]?.({
295
+ type: "row:click",
296
+ rowId,
297
+ originalEvent: e
298
+ });
299
+ }
300
+ },
301
+ { signal }
302
+ );
303
+ root.addEventListener(
304
+ "dblclick",
305
+ (e) => {
306
+ const target = e.target;
307
+ const td = target.closest("[data-yable-cell]");
308
+ if (td) {
309
+ const columnId = td.dataset.yableCell;
310
+ const rowId = td.dataset.yableRow;
311
+ handlers["cell:dblclick"]?.({
312
+ type: "cell:dblclick",
313
+ rowId,
314
+ columnId,
315
+ originalEvent: e
316
+ });
317
+ }
318
+ const tr = target.closest("[data-yable-row]");
319
+ if (tr) {
320
+ const rowId = tr.dataset.yableRow;
321
+ handlers["row:dblclick"]?.({
322
+ type: "row:dblclick",
323
+ rowId,
324
+ originalEvent: e
325
+ });
326
+ }
327
+ },
328
+ { signal }
329
+ );
330
+ root.addEventListener(
331
+ "change",
332
+ (e) => {
333
+ const target = e.target;
334
+ const columnId = target.dataset.yableInput;
335
+ const rowId = target.dataset.yableInputRow;
336
+ if (!columnId || !rowId) return;
337
+ let value;
338
+ if (target instanceof HTMLInputElement && target.type === "checkbox") {
339
+ value = target.checked;
340
+ } else if (target instanceof HTMLInputElement && target.type === "number") {
341
+ value = Number(target.value);
342
+ } else {
343
+ value = target.value;
344
+ }
345
+ table.setPendingValue(rowId, columnId, value);
346
+ onUpdate();
347
+ },
348
+ { signal }
349
+ );
350
+ root.addEventListener(
351
+ "blur",
352
+ (e) => {
353
+ const target = e.target;
354
+ if (target.dataset.yableInput && target.dataset.yableInputRow) {
355
+ const editing = table.getState().editing;
356
+ if (editing.activeCell) {
357
+ table.commitEdit();
358
+ onUpdate();
359
+ }
360
+ }
361
+ },
362
+ { signal, capture: true }
363
+ );
364
+ root.addEventListener(
365
+ "change",
366
+ (e) => {
367
+ const target = e.target;
368
+ if (target.dataset.yablePagesize !== void 0) {
369
+ table.setPageSize(Number(target.value));
370
+ onUpdate();
371
+ }
372
+ },
373
+ { signal }
374
+ );
375
+ root.addEventListener(
376
+ "mousedown",
377
+ (e) => {
378
+ const target = e.target;
379
+ if (target.classList.contains("yable-resize-handle")) {
380
+ const columnId = target.dataset.yableResize;
381
+ for (const headerGroup of table.getHeaderGroups()) {
382
+ for (const header of headerGroup.headers) {
383
+ if (header.column.id === columnId) {
384
+ const handler = header.getResizeHandler();
385
+ if (handler) {
386
+ handler(e);
387
+ onUpdate();
388
+ }
389
+ break;
390
+ }
391
+ }
392
+ }
393
+ }
394
+ },
395
+ { signal }
396
+ );
397
+ return () => abortController.abort();
398
+ }
399
+
400
+ // src/createTableDOM.ts
401
+ function createTableDOM(options) {
402
+ const { element, initialState, ...tableOpts } = options;
403
+ const {
404
+ stickyHeader,
405
+ striped,
406
+ bordered,
407
+ compact,
408
+ theme,
409
+ clickableRows,
410
+ footer,
411
+ emptyMessage,
412
+ pagination: paginationOpts,
413
+ ...coreOptions
414
+ } = tableOpts;
415
+ let state = {
416
+ sorting: [],
417
+ columnFilters: [],
418
+ globalFilter: "",
419
+ pagination: { pageIndex: 0, pageSize: 10 },
420
+ rowSelection: {},
421
+ columnVisibility: {},
422
+ columnOrder: [],
423
+ columnPinning: { left: [], right: [] },
424
+ columnSizing: {},
425
+ columnSizingInfo: {
426
+ startOffset: null,
427
+ startSize: null,
428
+ deltaOffset: null,
429
+ deltaPercentage: null,
430
+ isResizingColumn: false,
431
+ columnSizingStart: []
432
+ },
433
+ expanded: {},
434
+ rowPinning: { top: [], bottom: [] },
435
+ grouping: [],
436
+ editing: { activeCell: void 0, pendingValues: {} },
437
+ commits: { cells: {}, nextOpId: 1 },
438
+ keyboardNavigation: { focusedCell: null },
439
+ undoRedo: { undoStack: [], redoStack: [], maxSize: 50 },
440
+ fillHandle: { isDragging: false },
441
+ formulas: { enabled: false, formulas: {}, computedValues: {}, errors: {} },
442
+ rowDrag: { draggingRowId: null, overRowId: null, dropPosition: null },
443
+ pivot: {
444
+ enabled: false,
445
+ config: { rowFields: [], columnFields: [], valueFields: [] },
446
+ expandedRowGroups: {},
447
+ expandedColumnGroups: {}
448
+ },
449
+ ...initialState
450
+ };
451
+ let displayOpts = {
452
+ stickyHeader,
453
+ striped,
454
+ bordered,
455
+ compact,
456
+ theme,
457
+ clickableRows,
458
+ footer,
459
+ emptyMessage
460
+ };
461
+ let paginationDisplayOpts = typeof paginationOpts === "object" ? paginationOpts : paginationOpts ? {} : void 0;
462
+ const handlers = {};
463
+ const coreTable = yableCore.createTable({
464
+ ...coreOptions,
465
+ state,
466
+ onStateChange: (updater) => {
467
+ state = yableCore.functionalUpdate(updater, state);
468
+ render();
469
+ }
470
+ });
471
+ let cleanupEvents = null;
472
+ function render() {
473
+ coreTable.setOptions((prev) => ({
474
+ ...prev,
475
+ ...coreOptions,
476
+ state,
477
+ onStateChange: (updater) => {
478
+ state = yableCore.functionalUpdate(updater, state);
479
+ render();
480
+ }
481
+ }));
482
+ cleanupEvents?.();
483
+ let html = renderTable(coreTable, displayOpts);
484
+ if (paginationDisplayOpts !== void 0) {
485
+ html += renderPagination(coreTable, paginationDisplayOpts);
486
+ }
487
+ element.innerHTML = html;
488
+ cleanupEvents = attachEventDelegation(element, coreTable, handlers, render);
489
+ }
490
+ render();
491
+ const instance = {
492
+ table: coreTable,
493
+ render,
494
+ setData(data) {
495
+ coreTable.setOptions((prev) => ({
496
+ ...prev,
497
+ data,
498
+ state,
499
+ onStateChange: (updater) => {
500
+ state = yableCore.functionalUpdate(updater, state);
501
+ render();
502
+ }
503
+ }));
504
+ render();
505
+ },
506
+ getState() {
507
+ return state;
508
+ },
509
+ setOptions(opts) {
510
+ const { element: _el, initialState: _is, ...rest } = opts;
511
+ const {
512
+ stickyHeader: sh,
513
+ striped: st,
514
+ bordered: bd,
515
+ compact: cp,
516
+ theme: th,
517
+ clickableRows: cr,
518
+ footer: ft,
519
+ emptyMessage: em,
520
+ pagination: pg,
521
+ ...core
522
+ } = rest;
523
+ if (sh !== void 0 || st !== void 0 || bd !== void 0 || cp !== void 0 || th !== void 0 || cr !== void 0 || ft !== void 0 || em !== void 0) {
524
+ displayOpts = { ...displayOpts, stickyHeader: sh ?? displayOpts.stickyHeader, striped: st ?? displayOpts.striped, bordered: bd ?? displayOpts.bordered, compact: cp ?? displayOpts.compact, theme: th ?? displayOpts.theme, clickableRows: cr ?? displayOpts.clickableRows, footer: ft ?? displayOpts.footer, emptyMessage: em ?? displayOpts.emptyMessage };
525
+ }
526
+ if (pg !== void 0) {
527
+ paginationDisplayOpts = typeof pg === "object" ? pg : pg ? {} : void 0;
528
+ }
529
+ if (Object.keys(core).length > 0) {
530
+ Object.assign(coreOptions, core);
531
+ }
532
+ render();
533
+ },
534
+ on(event, handler) {
535
+ handlers[event] = handler;
536
+ },
537
+ off(event) {
538
+ delete handlers[event];
539
+ },
540
+ destroy() {
541
+ cleanupEvents?.();
542
+ element.innerHTML = "";
543
+ }
544
+ };
545
+ return instance;
546
+ }
547
+
548
+ Object.defineProperty(exports, "aggregationFns", {
549
+ enumerable: true,
550
+ get: function () { return yableCore.aggregationFns; }
551
+ });
552
+ Object.defineProperty(exports, "createColumnHelper", {
553
+ enumerable: true,
554
+ get: function () { return yableCore.createColumnHelper; }
555
+ });
556
+ Object.defineProperty(exports, "filterFns", {
557
+ enumerable: true,
558
+ get: function () { return yableCore.filterFns; }
559
+ });
560
+ Object.defineProperty(exports, "functionalUpdate", {
561
+ enumerable: true,
562
+ get: function () { return yableCore.functionalUpdate; }
563
+ });
564
+ Object.defineProperty(exports, "sortingFns", {
565
+ enumerable: true,
566
+ get: function () { return yableCore.sortingFns; }
567
+ });
568
+ exports.createTableDOM = createTableDOM;
569
+ exports.renderPagination = renderPagination;
570
+ exports.renderTable = renderTable;
571
+ //# sourceMappingURL=index.cjs.map
572
+ //# sourceMappingURL=index.cjs.map