@humanspeak/svelte-headless-table 6.0.2 → 6.0.4
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 +3 -2
- package/dist/bodyCells.d.ts +116 -0
- package/dist/bodyCells.js +80 -0
- package/dist/bodyRows.d.ts +92 -0
- package/dist/bodyRows.js +55 -0
- package/dist/columns.d.ts +204 -0
- package/dist/columns.js +120 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +4 -0
- package/dist/createTable.d.ts +107 -0
- package/dist/createTable.js +92 -0
- package/dist/createViewModel.d.ts +105 -2
- package/dist/createViewModel.js +55 -1
- package/dist/headerCells.d.ts +210 -0
- package/dist/headerCells.js +151 -0
- package/dist/headerRows.d.ts +72 -0
- package/dist/headerRows.js +60 -0
- package/dist/plugins/addColumnFilters.d.ts +101 -0
- package/dist/plugins/addColumnFilters.js +55 -0
- package/dist/plugins/addColumnOrder.d.ts +30 -0
- package/dist/plugins/addColumnOrder.js +21 -0
- package/dist/plugins/addDataExport.d.ts +51 -0
- package/dist/plugins/addDataExport.js +30 -0
- package/dist/plugins/addExpandedRows.d.ts +41 -2
- package/dist/plugins/addExpandedRows.js +24 -0
- package/dist/plugins/addFlatten.d.ts +48 -3
- package/dist/plugins/addFlatten.js +28 -0
- package/dist/plugins/addGridLayout.d.ts +13 -0
- package/dist/plugins/addGridLayout.js +13 -0
- package/dist/plugins/addGroupBy.d.ts +80 -4
- package/dist/plugins/addGroupBy.js +48 -4
- package/dist/plugins/addHiddenColumns.d.ts +27 -0
- package/dist/plugins/addHiddenColumns.js +19 -0
- package/dist/plugins/addPagination.d.ts +54 -0
- package/dist/plugins/addPagination.js +29 -0
- package/dist/plugins/addResizedColumns.d.ts +58 -4
- package/dist/plugins/addResizedColumns.js +33 -0
- package/dist/plugins/addSelectedRows.d.ts +58 -2
- package/dist/plugins/addSelectedRows.js +42 -0
- package/dist/plugins/addSortBy.d.ts +83 -6
- package/dist/plugins/addSortBy.js +65 -24
- package/dist/plugins/addSubRows.d.ts +39 -1
- package/dist/plugins/addSubRows.js +25 -0
- package/dist/plugins/addTableFilter.d.ts +87 -3
- package/dist/plugins/addTableFilter.js +90 -40
- package/dist/plugins/addVirtualScroll.d.ts +37 -0
- package/dist/plugins/addVirtualScroll.js +302 -0
- package/dist/plugins/addVirtualScroll.types.d.ts +139 -0
- package/dist/plugins/addVirtualScroll.types.js +1 -0
- package/dist/plugins/index.d.ts +1 -0
- package/dist/plugins/index.js +1 -0
- package/dist/tableComponent.d.ts +41 -0
- package/dist/tableComponent.js +37 -0
- package/dist/types/Action.d.ts +16 -2
- package/dist/types/Entries.d.ts +6 -0
- package/dist/types/KeyPath.d.ts +20 -0
- package/dist/types/Label.d.ts +26 -3
- package/dist/types/Matrix.d.ts +6 -0
- package/dist/types/TablePlugin.d.ts +140 -10
- package/dist/utils/HeightManager.d.ts +107 -0
- package/dist/utils/HeightManager.js +204 -0
- package/dist/utils/array.d.ts +24 -0
- package/dist/utils/array.js +24 -0
- package/dist/utils/attributes.d.ts +31 -0
- package/dist/utils/attributes.js +31 -0
- package/dist/utils/clone.d.ts +19 -0
- package/dist/utils/clone.js +13 -0
- package/dist/utils/compare.d.ts +29 -0
- package/dist/utils/compare.js +29 -0
- package/dist/utils/counter.d.ts +12 -0
- package/dist/utils/counter.js +12 -0
- package/dist/utils/css.d.ts +11 -0
- package/dist/utils/css.js +11 -0
- package/dist/utils/event.d.ts +14 -0
- package/dist/utils/event.js +14 -0
- package/dist/utils/filter.d.ts +47 -0
- package/dist/utils/filter.js +47 -0
- package/dist/utils/math.d.ts +22 -0
- package/dist/utils/math.js +22 -0
- package/dist/utils/matrix.d.ts +24 -0
- package/dist/utils/matrix.js +24 -0
- package/dist/utils/store.d.ts +116 -9
- package/dist/utils/store.js +63 -0
- package/package.json +23 -23
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { derived, get, readable, writable } from 'svelte/store';
|
|
2
|
+
import { HeightManager } from '../utils/HeightManager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Default configuration values for virtual scroll.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULTS = {
|
|
7
|
+
estimatedRowHeight: 40,
|
|
8
|
+
bufferSize: 10,
|
|
9
|
+
loadMoreThreshold: 200
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Creates a virtual scroll plugin that enables virtualized table rendering.
|
|
13
|
+
* Only renders rows that are visible in the viewport plus a buffer, dramatically
|
|
14
|
+
* improving performance for large datasets.
|
|
15
|
+
*
|
|
16
|
+
* @template Item - The type of data items in the table.
|
|
17
|
+
* @param config - Configuration options for virtual scrolling.
|
|
18
|
+
* @returns A TablePlugin that provides virtualization functionality.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const table = createTable(data, {
|
|
23
|
+
* virtualScroll: addVirtualScroll({
|
|
24
|
+
* estimatedRowHeight: 48,
|
|
25
|
+
* bufferSize: 5,
|
|
26
|
+
* onLoadMore: async () => {
|
|
27
|
+
* const more = await fetchMoreItems()
|
|
28
|
+
* data.update(d => [...d, ...more])
|
|
29
|
+
* },
|
|
30
|
+
* hasMore: hasMoreStore
|
|
31
|
+
* })
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* const {
|
|
35
|
+
* virtualScroll,
|
|
36
|
+
* topSpacerHeight,
|
|
37
|
+
* bottomSpacerHeight,
|
|
38
|
+
* visibleRange
|
|
39
|
+
* } = table.pluginStates.virtualScroll
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export const addVirtualScroll = ({ onLoadMore, hasMore: hasMoreConfig, loadMoreThreshold = DEFAULTS.loadMoreThreshold, estimatedRowHeight = DEFAULTS.estimatedRowHeight, bufferSize = DEFAULTS.bufferSize, getRowHeight } = {}) => () => {
|
|
43
|
+
// Height management
|
|
44
|
+
const heightManager = new HeightManager(estimatedRowHeight);
|
|
45
|
+
// Scroll state
|
|
46
|
+
const scrollTop = writable(0);
|
|
47
|
+
const viewportHeight = writable(0);
|
|
48
|
+
// Row IDs array (set by derivePageRows, used for calculations)
|
|
49
|
+
// This is a simple array, not derived from rows to avoid circular deps
|
|
50
|
+
const rowIds = writable([]);
|
|
51
|
+
// Loading state
|
|
52
|
+
const isLoading = writable(false);
|
|
53
|
+
const hasMoreStore = typeof hasMoreConfig === 'object' && hasMoreConfig !== null
|
|
54
|
+
? hasMoreConfig
|
|
55
|
+
: writable(hasMoreConfig ?? false);
|
|
56
|
+
// Track whether we've already triggered a load to prevent duplicates
|
|
57
|
+
let loadMorePending = false;
|
|
58
|
+
// Scroll container reference (set by the action)
|
|
59
|
+
let scrollContainer = null;
|
|
60
|
+
// Cache for row lookup (set by derivePageRows)
|
|
61
|
+
let allRowsCache = [];
|
|
62
|
+
// Visible range calculation
|
|
63
|
+
const visibleRange = derived([rowIds, scrollTop, viewportHeight], ([$rowIds, $scrollTop, $viewportHeight]) => {
|
|
64
|
+
return heightManager.getVisibleRange($rowIds, $scrollTop, $viewportHeight, bufferSize);
|
|
65
|
+
});
|
|
66
|
+
// Total height of all rows
|
|
67
|
+
const totalHeight = derived(rowIds, ($rowIds) => {
|
|
68
|
+
return heightManager.getTotalHeight($rowIds);
|
|
69
|
+
});
|
|
70
|
+
// Spacer heights
|
|
71
|
+
const topSpacerHeight = derived([rowIds, visibleRange], ([$rowIds, $range]) => {
|
|
72
|
+
return heightManager.getOffsetForIndex($rowIds, $range.start);
|
|
73
|
+
});
|
|
74
|
+
const bottomSpacerHeight = derived([rowIds, visibleRange, totalHeight], ([$rowIds, $range, $total]) => {
|
|
75
|
+
const endOffset = heightManager.getOffsetForIndex($rowIds, $range.end);
|
|
76
|
+
return Math.max(0, $total - endOffset);
|
|
77
|
+
});
|
|
78
|
+
// Total and rendered row counts
|
|
79
|
+
const totalRows = derived(rowIds, ($rowIds) => $rowIds.length);
|
|
80
|
+
const renderedRows = derived(visibleRange, ($range) => $range.end - $range.start);
|
|
81
|
+
/**
|
|
82
|
+
* Check if we should load more data and trigger the callback.
|
|
83
|
+
*/
|
|
84
|
+
const checkLoadMore = () => {
|
|
85
|
+
if (!onLoadMore || loadMorePending || !get(hasMoreStore)) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const $scrollTop = get(scrollTop);
|
|
89
|
+
const $viewportHeight = get(viewportHeight);
|
|
90
|
+
const $totalHeight = get(totalHeight);
|
|
91
|
+
const distanceFromBottom = $totalHeight - ($scrollTop + $viewportHeight);
|
|
92
|
+
if (distanceFromBottom <= loadMoreThreshold) {
|
|
93
|
+
loadMorePending = true;
|
|
94
|
+
isLoading.set(true);
|
|
95
|
+
const result = onLoadMore();
|
|
96
|
+
if (result instanceof Promise) {
|
|
97
|
+
result.finally(() => {
|
|
98
|
+
loadMorePending = false;
|
|
99
|
+
isLoading.set(false);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
loadMorePending = false;
|
|
104
|
+
isLoading.set(false);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Handle scroll events from the container.
|
|
110
|
+
*/
|
|
111
|
+
const handleScroll = (event) => {
|
|
112
|
+
const target = event.target;
|
|
113
|
+
const newScrollTop = target.scrollTop;
|
|
114
|
+
scrollTop.set(newScrollTop);
|
|
115
|
+
checkLoadMore();
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Svelte action to attach to the scroll container.
|
|
119
|
+
*/
|
|
120
|
+
const virtualScroll = (node) => {
|
|
121
|
+
scrollContainer = node;
|
|
122
|
+
// Set initial viewport height
|
|
123
|
+
const initialHeight = node.clientHeight;
|
|
124
|
+
viewportHeight.set(initialHeight);
|
|
125
|
+
// Create ResizeObserver to track viewport size changes
|
|
126
|
+
const resizeObserver = new ResizeObserver((entries) => {
|
|
127
|
+
for (const entry of entries) {
|
|
128
|
+
viewportHeight.set(entry.contentRect.height);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
resizeObserver.observe(node);
|
|
132
|
+
// Attach scroll listener
|
|
133
|
+
node.addEventListener('scroll', handleScroll, { passive: true });
|
|
134
|
+
// Check if we need to load more initially
|
|
135
|
+
checkLoadMore();
|
|
136
|
+
return {
|
|
137
|
+
destroy() {
|
|
138
|
+
scrollContainer = null;
|
|
139
|
+
node.removeEventListener('scroll', handleScroll);
|
|
140
|
+
resizeObserver.disconnect();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
/**
|
|
145
|
+
* Scroll to a specific row index.
|
|
146
|
+
*/
|
|
147
|
+
const scrollToIndex = (index, options = {}) => {
|
|
148
|
+
if (!scrollContainer) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const { align = 'start', behavior = 'auto' } = options;
|
|
152
|
+
const $rowIds = get(rowIds);
|
|
153
|
+
if (index < 0 || index >= $rowIds.length) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const targetOffset = heightManager.getOffsetForIndex($rowIds, index);
|
|
157
|
+
const rowHeight = heightManager.getHeight($rowIds[index]);
|
|
158
|
+
const $viewportHeight = get(viewportHeight);
|
|
159
|
+
let scrollPosition;
|
|
160
|
+
switch (align) {
|
|
161
|
+
case 'center':
|
|
162
|
+
scrollPosition = targetOffset - ($viewportHeight - rowHeight) / 2;
|
|
163
|
+
break;
|
|
164
|
+
case 'end':
|
|
165
|
+
scrollPosition = targetOffset - $viewportHeight + rowHeight;
|
|
166
|
+
break;
|
|
167
|
+
case 'auto': {
|
|
168
|
+
// Check if already visible
|
|
169
|
+
const $scrollTop = get(scrollTop);
|
|
170
|
+
const visibleStart = $scrollTop;
|
|
171
|
+
const visibleEnd = $scrollTop + $viewportHeight;
|
|
172
|
+
const rowStart = targetOffset;
|
|
173
|
+
const rowEnd = targetOffset + rowHeight;
|
|
174
|
+
if (rowStart >= visibleStart && rowEnd <= visibleEnd) {
|
|
175
|
+
// Already fully visible
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
else if (rowStart < visibleStart) {
|
|
179
|
+
scrollPosition = rowStart;
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
scrollPosition = rowEnd - $viewportHeight;
|
|
183
|
+
}
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
case 'start':
|
|
187
|
+
default:
|
|
188
|
+
scrollPosition = targetOffset;
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
scrollContainer.scrollTo({
|
|
192
|
+
top: Math.max(0, scrollPosition),
|
|
193
|
+
behavior
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* Notify the plugin that a row has been measured.
|
|
198
|
+
*/
|
|
199
|
+
const measureRow = (rowId, height) => {
|
|
200
|
+
// If getRowHeight is provided, prefer that
|
|
201
|
+
if (getRowHeight) {
|
|
202
|
+
const row = allRowsCache.find((r) => r.id === rowId);
|
|
203
|
+
if (row?.isData() && row.original) {
|
|
204
|
+
const specifiedHeight = getRowHeight(row.original);
|
|
205
|
+
if (specifiedHeight !== height) {
|
|
206
|
+
height = specifiedHeight;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const changed = heightManager.setHeight(rowId, height);
|
|
211
|
+
if (changed) {
|
|
212
|
+
// Force recalculation of derived stores by updating rowIds
|
|
213
|
+
// (touching it with the same value)
|
|
214
|
+
rowIds.update((v) => v);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
/**
|
|
218
|
+
* Svelte action to automatically measure row height.
|
|
219
|
+
* Attach to each <tr> element: <tr use:measureRowAction={row.id}>
|
|
220
|
+
*/
|
|
221
|
+
const measureRowAction = (node, rowId) => {
|
|
222
|
+
// Measure initial height
|
|
223
|
+
const measure = () => {
|
|
224
|
+
const height = node.getBoundingClientRect().height;
|
|
225
|
+
if (height > 0) {
|
|
226
|
+
measureRow(rowId, height);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
// Measure on mount
|
|
230
|
+
measure();
|
|
231
|
+
// Use ResizeObserver to track height changes
|
|
232
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
233
|
+
measure();
|
|
234
|
+
});
|
|
235
|
+
resizeObserver.observe(node);
|
|
236
|
+
return {
|
|
237
|
+
update(newRowId) {
|
|
238
|
+
rowId = newRowId;
|
|
239
|
+
measure();
|
|
240
|
+
},
|
|
241
|
+
destroy() {
|
|
242
|
+
resizeObserver.disconnect();
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
// Plugin state
|
|
247
|
+
const pluginState = {
|
|
248
|
+
scrollTop: { subscribe: scrollTop.subscribe },
|
|
249
|
+
viewportHeight: { subscribe: viewportHeight.subscribe },
|
|
250
|
+
visibleRange,
|
|
251
|
+
totalHeight,
|
|
252
|
+
topSpacerHeight,
|
|
253
|
+
bottomSpacerHeight,
|
|
254
|
+
isLoading: { subscribe: isLoading.subscribe },
|
|
255
|
+
hasMore: { subscribe: hasMoreStore.subscribe },
|
|
256
|
+
virtualScroll,
|
|
257
|
+
scrollToIndex,
|
|
258
|
+
measureRow,
|
|
259
|
+
measureRowAction,
|
|
260
|
+
totalRows,
|
|
261
|
+
renderedRows
|
|
262
|
+
};
|
|
263
|
+
/**
|
|
264
|
+
* Derive visible rows from all page rows.
|
|
265
|
+
* Re-runs when rows, scroll position, or viewport height changes.
|
|
266
|
+
*/
|
|
267
|
+
const derivePageRows = (rows) => {
|
|
268
|
+
return derived([rows, scrollTop, viewportHeight], ([$rows, $scrollTop, $viewportHeight], set) => {
|
|
269
|
+
// Cache rows for lookup in measureRow and hooks
|
|
270
|
+
allRowsCache = $rows;
|
|
271
|
+
// Extract row IDs and update the store (only if changed)
|
|
272
|
+
const ids = $rows.map((r) => r.id);
|
|
273
|
+
const currentIds = get(rowIds);
|
|
274
|
+
if (ids.length !== currentIds.length ||
|
|
275
|
+
ids.some((id, i) => id !== currentIds[i])) {
|
|
276
|
+
rowIds.set(ids);
|
|
277
|
+
}
|
|
278
|
+
// Calculate visible range
|
|
279
|
+
const range = heightManager.getVisibleRange(ids, $scrollTop, $viewportHeight, bufferSize);
|
|
280
|
+
// Return only the visible subset
|
|
281
|
+
const visibleRows = $rows.slice(range.start, range.end);
|
|
282
|
+
set(visibleRows);
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
// Hooks to add virtual index props to rows
|
|
286
|
+
const hooks = {
|
|
287
|
+
'tbody.tr': (row) => {
|
|
288
|
+
const virtualIndex = allRowsCache.findIndex((r) => r.id === row.id);
|
|
289
|
+
return {
|
|
290
|
+
props: readable({
|
|
291
|
+
virtualIndex: virtualIndex >= 0 ? virtualIndex : 0,
|
|
292
|
+
isVirtual: true
|
|
293
|
+
})
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
return {
|
|
298
|
+
pluginState,
|
|
299
|
+
derivePageRows,
|
|
300
|
+
hooks
|
|
301
|
+
};
|
|
302
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import type { Action } from 'svelte/action';
|
|
2
|
+
import type { Readable, Writable } from 'svelte/store';
|
|
3
|
+
/**
|
|
4
|
+
* Configuration options for the addVirtualScroll plugin.
|
|
5
|
+
*
|
|
6
|
+
* @template Item - The type of data items in the table.
|
|
7
|
+
*/
|
|
8
|
+
export interface VirtualScrollConfig<Item> {
|
|
9
|
+
/**
|
|
10
|
+
* Callback fired when more data should be loaded (infinite scroll).
|
|
11
|
+
* Return a promise to indicate when loading is complete.
|
|
12
|
+
*/
|
|
13
|
+
onLoadMore?: () => void | Promise<void>;
|
|
14
|
+
/**
|
|
15
|
+
* Whether there is more data available to load.
|
|
16
|
+
* Can be a boolean or a Writable store.
|
|
17
|
+
*/
|
|
18
|
+
hasMore?: Writable<boolean> | boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Number of pixels from the bottom to trigger onLoadMore.
|
|
21
|
+
* @default 200
|
|
22
|
+
*/
|
|
23
|
+
loadMoreThreshold?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Estimated height of each row in pixels.
|
|
26
|
+
* Used for initial calculations before rows are measured.
|
|
27
|
+
* @default 40
|
|
28
|
+
*/
|
|
29
|
+
estimatedRowHeight?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Number of rows to render above and below the visible area.
|
|
32
|
+
* Higher values reduce flicker during fast scrolling but render more DOM nodes.
|
|
33
|
+
* @default 10
|
|
34
|
+
*/
|
|
35
|
+
bufferSize?: number;
|
|
36
|
+
/**
|
|
37
|
+
* Optional function to get the height of a specific row.
|
|
38
|
+
* If provided, enables variable row heights.
|
|
39
|
+
*/
|
|
40
|
+
getRowHeight?: (_item: Item) => number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Visible range of rows.
|
|
44
|
+
*/
|
|
45
|
+
export interface VisibleRange {
|
|
46
|
+
/** Index of the first visible row (0-based). */
|
|
47
|
+
start: number;
|
|
48
|
+
/** Index of the last visible row (exclusive). */
|
|
49
|
+
end: number;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Options for scrollToIndex method.
|
|
53
|
+
*/
|
|
54
|
+
export interface ScrollToIndexOptions {
|
|
55
|
+
/** Alignment of the target row within the viewport. */
|
|
56
|
+
align?: 'start' | 'center' | 'end' | 'auto';
|
|
57
|
+
/** Scroll behavior. */
|
|
58
|
+
behavior?: ScrollBehavior;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* State exposed by the addVirtualScroll plugin.
|
|
62
|
+
*
|
|
63
|
+
* @template Item - The type of data items in the table.
|
|
64
|
+
*/
|
|
65
|
+
export interface VirtualScrollState<Item> {
|
|
66
|
+
/**
|
|
67
|
+
* Current scroll position of the container.
|
|
68
|
+
*/
|
|
69
|
+
scrollTop: Readable<number>;
|
|
70
|
+
/**
|
|
71
|
+
* Height of the scroll container viewport.
|
|
72
|
+
*/
|
|
73
|
+
viewportHeight: Readable<number>;
|
|
74
|
+
/**
|
|
75
|
+
* Range of currently visible row indices.
|
|
76
|
+
*/
|
|
77
|
+
visibleRange: Readable<VisibleRange>;
|
|
78
|
+
/**
|
|
79
|
+
* Total height of all rows (for scroll container sizing).
|
|
80
|
+
*/
|
|
81
|
+
totalHeight: Readable<number>;
|
|
82
|
+
/**
|
|
83
|
+
* Height of the top spacer element.
|
|
84
|
+
*/
|
|
85
|
+
topSpacerHeight: Readable<number>;
|
|
86
|
+
/**
|
|
87
|
+
* Height of the bottom spacer element.
|
|
88
|
+
*/
|
|
89
|
+
bottomSpacerHeight: Readable<number>;
|
|
90
|
+
/**
|
|
91
|
+
* Whether more data is currently being loaded.
|
|
92
|
+
*/
|
|
93
|
+
isLoading: Readable<boolean>;
|
|
94
|
+
/**
|
|
95
|
+
* Whether there is more data available to load.
|
|
96
|
+
*/
|
|
97
|
+
hasMore: Readable<boolean>;
|
|
98
|
+
/**
|
|
99
|
+
* Svelte action to attach to the scroll container.
|
|
100
|
+
* Handles scroll event listeners and viewport tracking.
|
|
101
|
+
*/
|
|
102
|
+
virtualScroll: Action<HTMLElement>;
|
|
103
|
+
/**
|
|
104
|
+
* Scroll to a specific row index.
|
|
105
|
+
*/
|
|
106
|
+
scrollToIndex: (_index: number, _options?: ScrollToIndexOptions) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Notify the plugin that a row has been measured.
|
|
109
|
+
* Called automatically when rows are rendered.
|
|
110
|
+
* @internal
|
|
111
|
+
*/
|
|
112
|
+
measureRow: (_rowId: string, _height: number) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Svelte action to attach to each table row for automatic height measurement.
|
|
115
|
+
* Usage: <tr use:measureRowAction={row.id}>
|
|
116
|
+
*/
|
|
117
|
+
measureRowAction: Action<HTMLElement, string>;
|
|
118
|
+
/**
|
|
119
|
+
* Total number of rows (before virtualization).
|
|
120
|
+
*/
|
|
121
|
+
totalRows: Readable<number>;
|
|
122
|
+
/**
|
|
123
|
+
* Number of rows currently rendered in the DOM.
|
|
124
|
+
*/
|
|
125
|
+
renderedRows: Readable<number>;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Props added to body rows by the virtual scroll plugin.
|
|
129
|
+
*/
|
|
130
|
+
export interface VirtualScrollRowProps {
|
|
131
|
+
/**
|
|
132
|
+
* Index of this row in the full dataset.
|
|
133
|
+
*/
|
|
134
|
+
virtualIndex: number;
|
|
135
|
+
/**
|
|
136
|
+
* Whether this row is currently in the visible range.
|
|
137
|
+
*/
|
|
138
|
+
isVirtual: boolean;
|
|
139
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/plugins/index.d.ts
CHANGED
package/dist/plugins/index.js
CHANGED
package/dist/tableComponent.d.ts
CHANGED
|
@@ -2,18 +2,59 @@ import type { TableState } from './createViewModel.js';
|
|
|
2
2
|
import type { AnyPlugins, ComponentKeys, ElementHook, PluginTablePropSet } from './types/TablePlugin.js';
|
|
3
3
|
import type { Clonable } from './utils/clone.js';
|
|
4
4
|
import { type Readable } from 'svelte/store';
|
|
5
|
+
/**
|
|
6
|
+
* Initialization options for a TableComponent.
|
|
7
|
+
*/
|
|
5
8
|
export interface TableComponentInit {
|
|
9
|
+
/** Unique identifier for the component. */
|
|
6
10
|
id: string;
|
|
7
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* Abstract base class for all table components (rows, cells, etc.).
|
|
14
|
+
* Provides common functionality for state injection, hook application, and attribute merging.
|
|
15
|
+
*
|
|
16
|
+
* @template Item - The type of data items in the table.
|
|
17
|
+
* @template Plugins - The plugins used by the table.
|
|
18
|
+
* @template Key - The component key type (e.g., 'tbody.tr', 'tbody.tr.td').
|
|
19
|
+
*/
|
|
8
20
|
export declare abstract class TableComponent<Item, Plugins extends AnyPlugins, Key extends ComponentKeys> implements Clonable<TableComponent<Item, Plugins, Key>> {
|
|
21
|
+
/** Unique identifier for the component. */
|
|
9
22
|
id: string;
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new TableComponent.
|
|
25
|
+
*
|
|
26
|
+
* @param init - Initialization options.
|
|
27
|
+
*/
|
|
10
28
|
constructor({ id }: TableComponentInit);
|
|
11
29
|
private attrsForName;
|
|
30
|
+
/**
|
|
31
|
+
* Gets the merged HTML attributes from all applied plugins.
|
|
32
|
+
*
|
|
33
|
+
* @returns A readable store of merged attributes.
|
|
34
|
+
*/
|
|
12
35
|
attrs(): Readable<Record<string, unknown>>;
|
|
13
36
|
private propsForName;
|
|
37
|
+
/**
|
|
38
|
+
* Gets the merged props from all applied plugins.
|
|
39
|
+
*
|
|
40
|
+
* @returns A readable store of plugin props keyed by plugin name.
|
|
41
|
+
*/
|
|
14
42
|
props(): Readable<PluginTablePropSet<Plugins>[Key]>;
|
|
43
|
+
/** Reference to the table state, injected after creation. */
|
|
15
44
|
state?: TableState<Item, Plugins>;
|
|
45
|
+
/**
|
|
46
|
+
* Injects the table state reference into this component.
|
|
47
|
+
*
|
|
48
|
+
* @param state - The table state to inject.
|
|
49
|
+
*/
|
|
16
50
|
injectState(state: TableState<Item, Plugins>): void;
|
|
51
|
+
/**
|
|
52
|
+
* Applies a plugin hook to this component.
|
|
53
|
+
* Hooks can provide both props and attributes.
|
|
54
|
+
*
|
|
55
|
+
* @param pluginName - The name of the plugin.
|
|
56
|
+
* @param hook - The element hook containing props and/or attrs.
|
|
57
|
+
*/
|
|
17
58
|
applyHook(pluginName: string, hook: ElementHook<Record<string, unknown>, Record<string, unknown>>): void;
|
|
18
59
|
abstract clone(): TableComponent<Item, Plugins, Key>;
|
|
19
60
|
}
|
package/dist/tableComponent.js
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
1
|
import { finalizeAttributes, mergeAttributes } from './utils/attributes.js';
|
|
2
2
|
import { derivedKeys } from '@humanspeak/svelte-subscribe';
|
|
3
3
|
import { derived } from 'svelte/store';
|
|
4
|
+
/**
|
|
5
|
+
* Abstract base class for all table components (rows, cells, etc.).
|
|
6
|
+
* Provides common functionality for state injection, hook application, and attribute merging.
|
|
7
|
+
*
|
|
8
|
+
* @template Item - The type of data items in the table.
|
|
9
|
+
* @template Plugins - The plugins used by the table.
|
|
10
|
+
* @template Key - The component key type (e.g., 'tbody.tr', 'tbody.tr.td').
|
|
11
|
+
*/
|
|
4
12
|
export class TableComponent {
|
|
13
|
+
/** Unique identifier for the component. */
|
|
5
14
|
id;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new TableComponent.
|
|
17
|
+
*
|
|
18
|
+
* @param init - Initialization options.
|
|
19
|
+
*/
|
|
6
20
|
constructor({ id }) {
|
|
7
21
|
this.id = id;
|
|
8
22
|
}
|
|
9
23
|
attrsForName = {};
|
|
24
|
+
/**
|
|
25
|
+
* Gets the merged HTML attributes from all applied plugins.
|
|
26
|
+
*
|
|
27
|
+
* @returns A readable store of merged attributes.
|
|
28
|
+
*/
|
|
10
29
|
attrs() {
|
|
11
30
|
return derived(Object.values(this.attrsForName), ($attrsArray) => {
|
|
12
31
|
let $mergedAttrs = {};
|
|
@@ -17,13 +36,31 @@ export class TableComponent {
|
|
|
17
36
|
});
|
|
18
37
|
}
|
|
19
38
|
propsForName = {};
|
|
39
|
+
/**
|
|
40
|
+
* Gets the merged props from all applied plugins.
|
|
41
|
+
*
|
|
42
|
+
* @returns A readable store of plugin props keyed by plugin name.
|
|
43
|
+
*/
|
|
20
44
|
props() {
|
|
21
45
|
return derivedKeys(this.propsForName);
|
|
22
46
|
}
|
|
47
|
+
/** Reference to the table state, injected after creation. */
|
|
23
48
|
state;
|
|
49
|
+
/**
|
|
50
|
+
* Injects the table state reference into this component.
|
|
51
|
+
*
|
|
52
|
+
* @param state - The table state to inject.
|
|
53
|
+
*/
|
|
24
54
|
injectState(state) {
|
|
25
55
|
this.state = state;
|
|
26
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* Applies a plugin hook to this component.
|
|
59
|
+
* Hooks can provide both props and attributes.
|
|
60
|
+
*
|
|
61
|
+
* @param pluginName - The name of the plugin.
|
|
62
|
+
* @param hook - The element hook containing props and/or attrs.
|
|
63
|
+
*/
|
|
27
64
|
applyHook(pluginName, hook) {
|
|
28
65
|
if (hook.props !== undefined) {
|
|
29
66
|
this.propsForName[pluginName] = hook.props;
|
package/dist/types/Action.d.ts
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Return type for Svelte actions.
|
|
3
|
+
* Contains optional lifecycle methods for updating and destroying the action.
|
|
4
|
+
*
|
|
5
|
+
* @template Props - The type of props passed to the action.
|
|
6
|
+
*/
|
|
1
7
|
export type ActionReturnType<Props = any> = {
|
|
2
|
-
|
|
8
|
+
/** Called when props change. */
|
|
9
|
+
update?: (_newProps: Props) => void;
|
|
10
|
+
/** Called when the element is removed from the DOM. */
|
|
3
11
|
destroy?: () => void;
|
|
4
12
|
};
|
|
5
|
-
|
|
13
|
+
/**
|
|
14
|
+
* A Svelte action function that can be applied to DOM elements.
|
|
15
|
+
* Actions receive an element and optional props, returning lifecycle methods.
|
|
16
|
+
*
|
|
17
|
+
* @template Props - The type of props passed to the action.
|
|
18
|
+
*/
|
|
19
|
+
export type Action<Props> = (_node: Element, _props?: Props) => ActionReturnType<Props> | void;
|
package/dist/types/Entries.d.ts
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts an object type to its entries type (array of [key, value] tuples).
|
|
3
|
+
* Similar to the return type of Object.entries() but with stronger typing.
|
|
4
|
+
*
|
|
5
|
+
* @template Obj - The object type to convert.
|
|
6
|
+
*/
|
|
1
7
|
export type Entries<Obj> = NonNullable<{
|
|
2
8
|
[K in keyof Obj]: [K, NonNullable<Obj[K]>];
|
|
3
9
|
}[keyof Obj]>[];
|
package/dist/types/KeyPath.d.ts
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a union of all valid dot-notation key paths for an object type.
|
|
3
|
+
* Useful for type-safe property access with nested objects.
|
|
4
|
+
*
|
|
5
|
+
* @template T - The object type to extract paths from.
|
|
6
|
+
* @template D - Maximum depth to traverse (default 3 to avoid infinite recursion).
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* type Person = { name: string; address: { city: string } }
|
|
10
|
+
* type Paths = KeyPath<Person> // 'name' | 'address' | 'address.city'
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
1
13
|
export type KeyPath<T, D extends number = 3> = KeyPath_<T, D, []>;
|
|
14
|
+
/**
|
|
15
|
+
* Internal recursive helper for KeyPath.
|
|
16
|
+
* @internal
|
|
17
|
+
*/
|
|
2
18
|
type KeyPath_<T, D extends number, S extends unknown[]> = D extends S['length'] ? never : T extends object ? {
|
|
3
19
|
[K in keyof T]-?: K extends string ? `${K}` | Join<K, KeyPath_<T[K], D, [never, ...S]>> : K extends number ? `[${K}]` | Join<`[${K}]`, KeyPath_<T[K], D, [never, ...S]>> : never;
|
|
4
20
|
}[keyof T] : '';
|
|
21
|
+
/**
|
|
22
|
+
* Internal helper for joining path segments with dots.
|
|
23
|
+
* @internal
|
|
24
|
+
*/
|
|
5
25
|
type Join<K, P> = K extends string | number ? P extends string | number ? P extends `[${string}` ? `${K}${P}` : `${K}${'' extends P ? '' : '.'}${P}` : never : never;
|
|
6
26
|
export {};
|
package/dist/types/Label.d.ts
CHANGED
|
@@ -3,6 +3,29 @@ import type { DataBodyCell, DisplayBodyCell } from '../bodyCells.js';
|
|
|
3
3
|
import type { TableState } from '../createViewModel.js';
|
|
4
4
|
import type { HeaderCell } from '../headerCells.js';
|
|
5
5
|
import type { AnyPlugins } from './TablePlugin.js';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A render function for data body cells.
|
|
8
|
+
* Receives the cell and table state, returns content to render.
|
|
9
|
+
*
|
|
10
|
+
* @template Item - The type of data items in the table.
|
|
11
|
+
* @template Plugins - The plugins used by the table.
|
|
12
|
+
* @template Value - The type of the cell value.
|
|
13
|
+
*/
|
|
14
|
+
export type DataLabel<Item, Plugins extends AnyPlugins = AnyPlugins, Value = any> = (_cell: DataBodyCell<Item, AnyPlugins, Value>, _state: TableState<Item, Plugins>) => RenderConfig;
|
|
15
|
+
/**
|
|
16
|
+
* A render function for display body cells.
|
|
17
|
+
* Receives the cell and table state, returns content to render.
|
|
18
|
+
*
|
|
19
|
+
* @template Item - The type of data items in the table.
|
|
20
|
+
* @template Plugins - The plugins used by the table.
|
|
21
|
+
*/
|
|
22
|
+
export type DisplayLabel<Item, Plugins extends AnyPlugins = AnyPlugins> = (_cell: DisplayBodyCell<Item>, _state: TableState<Item, Plugins>) => RenderConfig;
|
|
23
|
+
/**
|
|
24
|
+
* A label for header cells. Can be static content or a render function.
|
|
25
|
+
* If the function type is removed from the union, generics will not be
|
|
26
|
+
* inferred for subtypes.
|
|
27
|
+
*
|
|
28
|
+
* @template Item - The type of data items in the table.
|
|
29
|
+
* @template Plugins - The plugins used by the table.
|
|
30
|
+
*/
|
|
31
|
+
export type HeaderLabel<Item, Plugins extends AnyPlugins = AnyPlugins> = RenderConfig | ((_cell: HeaderCell<Item, Plugins>, _state: TableState<Item, Plugins>) => RenderConfig);
|