@svelte-lean/table 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +6 -1
- package/dist/style.css +214 -9
- package/dist/table/data-table.svelte +944 -319
- package/dist/table/navigation.js +20 -2
- package/dist/table.svelte.js +34 -1
- package/dist/types.d.ts +17 -5
- package/package.json +5 -2
package/dist/table/navigation.js
CHANGED
|
@@ -13,11 +13,29 @@ export function horizontalCell(cell, delta) {
|
|
|
13
13
|
const cells = dataCells(row);
|
|
14
14
|
return cells[cells.indexOf(cell) + delta];
|
|
15
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* The row after or before `row` in the table body, crossing from one `<tbody>` to the next (grid
|
|
18
|
+
* layout renders the body in sections).
|
|
19
|
+
*/
|
|
20
|
+
function adjacentRow(row, delta) {
|
|
21
|
+
const sibling = delta > 0 ? 'nextElementSibling' : 'previousElementSibling';
|
|
22
|
+
const next = row[sibling];
|
|
23
|
+
if (next)
|
|
24
|
+
return next;
|
|
25
|
+
for (let section = row.parentElement?.[sibling]; section; section = section[sibling]) {
|
|
26
|
+
if (section.tagName !== 'TBODY')
|
|
27
|
+
continue;
|
|
28
|
+
const edge = delta > 0 ? section.firstElementChild : section.lastElementChild;
|
|
29
|
+
if (edge)
|
|
30
|
+
return edge;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
16
34
|
/** The nearest cell with the same `data-column` in a following or preceding row. */
|
|
17
35
|
export function verticalCell(cell, delta) {
|
|
18
36
|
const column = cell.dataset.column;
|
|
19
|
-
const
|
|
20
|
-
for (let row =
|
|
37
|
+
const start = cell.parentElement;
|
|
38
|
+
for (let row = start && adjacentRow(start, delta); row; row = adjacentRow(row, delta)) {
|
|
21
39
|
const target = dataCells(row).find((candidate) => candidate.dataset.column === column);
|
|
22
40
|
if (target)
|
|
23
41
|
return target;
|
package/dist/table.svelte.js
CHANGED
|
@@ -30,8 +30,41 @@ export function createTable(options) {
|
|
|
30
30
|
searchText: options.searchText,
|
|
31
31
|
locale: resolve(options.locale)
|
|
32
32
|
};
|
|
33
|
-
return (options.getRowModel ?? createRowModel)(context);
|
|
33
|
+
return stabilize((options.getRowModel ?? createRowModel)(context));
|
|
34
34
|
});
|
|
35
|
+
/**
|
|
36
|
+
* Row node identity across models. Every state change builds a new model; a node whose row,
|
|
37
|
+
* depth and children are unchanged is handed back as the same object, so a renderer keyed by
|
|
38
|
+
* row id sees nothing to redraw for it after a sort, a selection or an expansion. Only
|
|
39
|
+
* bookkeeping: the previous nodes by id, replaced by each model.
|
|
40
|
+
*/
|
|
41
|
+
let previous = new Map();
|
|
42
|
+
function same(a, b) {
|
|
43
|
+
return (a.kind === 'row' &&
|
|
44
|
+
a.data === b.data &&
|
|
45
|
+
a.depth === b.depth &&
|
|
46
|
+
a.children.length === b.children.length &&
|
|
47
|
+
a.children.every((child, i) => child === b.children[i]));
|
|
48
|
+
}
|
|
49
|
+
function stabilize(next) {
|
|
50
|
+
const kept = new Map();
|
|
51
|
+
const reuse = (node) => {
|
|
52
|
+
const known = kept.get(node.id);
|
|
53
|
+
if (known)
|
|
54
|
+
return known;
|
|
55
|
+
// Children first: a parent is the same only when its children are the same objects.
|
|
56
|
+
const children = node.children.length ? node.children.map(reuse) : node.children;
|
|
57
|
+
const candidate = children === node.children ? node : { ...node, children };
|
|
58
|
+
const old = previous.get(node.id);
|
|
59
|
+
const result = old && same(old, candidate) ? old : candidate;
|
|
60
|
+
kept.set(node.id, result);
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
const rows = next.rows.map(reuse);
|
|
64
|
+
const all = next.all.map(reuse);
|
|
65
|
+
previous = kept;
|
|
66
|
+
return { ...next, rows, all };
|
|
67
|
+
}
|
|
35
68
|
const selected = $derived(new Set(state.selection));
|
|
36
69
|
const expanded = $derived(new Set(state.expanded));
|
|
37
70
|
const mode = $derived(resolve(options.selection) ?? 'multiple');
|
package/dist/types.d.ts
CHANGED
|
@@ -236,6 +236,15 @@ export interface TableProps<T> {
|
|
|
236
236
|
title?: Snippet<[RowModel<T>]>;
|
|
237
237
|
showHeader?: boolean;
|
|
238
238
|
tableLayout?: 'auto' | 'fixed';
|
|
239
|
+
/**
|
|
240
|
+
* `table` (default): native table layout. `grid`: the same elements laid out as CSS grid rows
|
|
241
|
+
* with explicit ARIA roles; body rows stand in sections the browser skips while off screen
|
|
242
|
+
* (`content-visibility: auto`), and column widths are measured from the content once and kept.
|
|
243
|
+
* For long pages (hundreds of rows) where layout and paint of every row would dominate. In grid
|
|
244
|
+
* layout `width` is a column's minimum, `maxWidth` caps a measured width (default 480), `span`
|
|
245
|
+
* is not applied, and `tableLayout` has no effect.
|
|
246
|
+
*/
|
|
247
|
+
layout?: 'table' | 'grid';
|
|
239
248
|
searchable?: boolean;
|
|
240
249
|
/** Milliseconds between the last keystroke in the search box or a filter input and the state change. */
|
|
241
250
|
searchDelay?: number;
|
|
@@ -273,15 +282,18 @@ export interface TableProps<T> {
|
|
|
273
282
|
/**
|
|
274
283
|
* Keeps the header row in view. With `height` it sticks inside the scroll region; without, it
|
|
275
284
|
* sticks to the page while the table fits its width (`--slean-table-sticky-top` offsets it below
|
|
276
|
-
* a fixed application bar)
|
|
285
|
+
* a fixed application bar). A table wider than its container scrolls sideways in the region,
|
|
286
|
+
* which captures sticky positioning: the header then scrolls with the rows, unless the region
|
|
287
|
+
* also scrolls down (a `height`, or a `max-height` from CSS).
|
|
277
288
|
*/
|
|
278
289
|
stickyHeader?: boolean;
|
|
279
290
|
height?: string;
|
|
280
291
|
/**
|
|
281
|
-
* Progressive mounting for large pages. When at least
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
292
|
+
* Progressive mounting for large pages. When at least a first batch of rows the table has not
|
|
293
|
+
* drawn arrives (the first render in the browser, a fetch, a page change), that batch renders at
|
|
294
|
+
* once and the rest follow in slices sized from the measured cost of a row, one macrotask apart,
|
|
295
|
+
* so the main thread never blocks on the whole set. `true` makes the first batch one screenful; a
|
|
296
|
+
* number sets it. Server output and its hydration are always complete. Default `false`.
|
|
285
297
|
*/
|
|
286
298
|
progressive?: boolean | number;
|
|
287
299
|
pagination?: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svelte-lean/table",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A Svelte-native table and data-grid engine that ships only the features you use.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Ömer Say",
|
|
@@ -89,5 +89,8 @@
|
|
|
89
89
|
"pagination",
|
|
90
90
|
"typescript",
|
|
91
91
|
"headless"
|
|
92
|
-
]
|
|
92
|
+
],
|
|
93
|
+
"dependencies": {
|
|
94
|
+
"esm-env": "^1.2.2"
|
|
95
|
+
}
|
|
93
96
|
}
|