@human-kit/svelte-components 1.0.0-alpha.16 → 1.0.0-alpha.18
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/dist/checkbox/root/checkbox-root.svelte +9 -4
- package/dist/checkbox/root/checkbox-root.svelte.d.ts +4 -1
- package/dist/table/PLAN.md +6 -6
- package/dist/table/README.md +4 -2
- package/dist/table/body/table-body.svelte +5 -0
- package/dist/table/cell/table-cell.svelte +18 -1
- package/dist/table/checkbox/README.md +1 -1
- package/dist/table/checkbox/table-checkbox.svelte +5 -48
- package/dist/table/checkbox-indicator/README.md +1 -1
- package/dist/table/column/README.md +11 -11
- package/dist/table/column-header-cell/table-column-header-cell.svelte +20 -17
- package/dist/table/column-resizer/table-column-resizer-fixed-width-test.svelte +57 -0
- package/dist/table/column-resizer/table-column-resizer-fixed-width-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-freeze-layout-test.svelte +2 -1
- package/dist/table/column-resizer/table-column-resizer-overflow-test.svelte +64 -0
- package/dist/table/column-resizer/table-column-resizer-overflow-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-padded-container-test.svelte +67 -0
- package/dist/table/column-resizer/table-column-resizer-padded-container-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-sandbox-overflow-test.svelte +87 -0
- package/dist/table/column-resizer/table-column-resizer-sandbox-overflow-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer-selection-column-test.svelte +2 -1
- package/dist/table/column-resizer/table-column-resizer-test.svelte +3 -3
- package/dist/table/column-resizer/table-column-resizer-three-column-relative-test.svelte +64 -0
- package/dist/table/column-resizer/table-column-resizer-three-column-relative-test.svelte.d.ts +3 -0
- package/dist/table/column-resizer/table-column-resizer.svelte +47 -54
- package/dist/table/root/README.md +12 -12
- package/dist/table/root/context.d.ts +24 -7
- package/dist/table/root/context.js +506 -43
- package/dist/table/root/table-root.svelte +140 -9
- package/dist/table/row/table-row.svelte +20 -60
- package/dist/table/types.d.ts +4 -4
- package/package.json +1 -1
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
import {
|
|
22
22
|
createTableContext,
|
|
23
23
|
setTableContext,
|
|
24
|
+
type TableColumnWidth,
|
|
24
25
|
type TableSelectionKey,
|
|
25
26
|
type TableSortDescriptor
|
|
26
27
|
} from './context';
|
|
@@ -60,7 +61,7 @@
|
|
|
60
61
|
let focusVisible = $state(false);
|
|
61
62
|
let pendingControlledHiddenColumns = $state<string[] | null>(null);
|
|
62
63
|
let pendingControlledSelection = $state<Set<TableSelectionKey> | null>(null);
|
|
63
|
-
let pendingControlledColumnWidths = $state<Map<string,
|
|
64
|
+
let pendingControlledColumnWidths = $state<Map<string, TableColumnWidth> | null>(null);
|
|
64
65
|
let sortAnnouncement = $state('');
|
|
65
66
|
let hasObservedSortState = $state(false);
|
|
66
67
|
let hasInitializedSortSync = $state(false);
|
|
@@ -137,7 +138,10 @@
|
|
|
137
138
|
return true;
|
|
138
139
|
}
|
|
139
140
|
|
|
140
|
-
function hasSameColumnWidths(
|
|
141
|
+
function hasSameColumnWidths(
|
|
142
|
+
left: Map<string, TableColumnWidth>,
|
|
143
|
+
right: Map<string, TableColumnWidth>
|
|
144
|
+
) {
|
|
141
145
|
if (left.size !== right.size) return false;
|
|
142
146
|
for (const [key, value] of left) {
|
|
143
147
|
if (right.get(key) !== value) return false;
|
|
@@ -164,7 +168,52 @@
|
|
|
164
168
|
return ctx.hasResizableColumns();
|
|
165
169
|
});
|
|
166
170
|
|
|
171
|
+
const hasDefinedColumnWidths = $derived.by(() => {
|
|
172
|
+
void $layoutVersion;
|
|
173
|
+
for (let index = 0; index < ctx.getColumnCount(); index += 1) {
|
|
174
|
+
const column = ctx.getColumnAt(index);
|
|
175
|
+
if (!column || ctx.isColumnHidden(column.id)) continue;
|
|
176
|
+
if (column.width !== undefined || column.defaultWidth !== undefined) {
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return false;
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const layoutColumns = $derived.by(() => {
|
|
184
|
+
void $layoutVersion;
|
|
185
|
+
void $widthVersion;
|
|
186
|
+
|
|
187
|
+
const columns: Array<{
|
|
188
|
+
id: string;
|
|
189
|
+
width: number | undefined;
|
|
190
|
+
widthStyle: string | undefined;
|
|
191
|
+
implicitWidth: boolean;
|
|
192
|
+
minWidth: number | undefined;
|
|
193
|
+
maxWidth: number | undefined;
|
|
194
|
+
}> = [];
|
|
195
|
+
|
|
196
|
+
for (let index = 0; index < ctx.getColumnCount(); index += 1) {
|
|
197
|
+
const column = ctx.getColumnAt(index);
|
|
198
|
+
if (!column || ctx.isColumnHidden(column.id)) continue;
|
|
199
|
+
|
|
200
|
+
columns.push({
|
|
201
|
+
id: column.id,
|
|
202
|
+
width: ctx.getColumnWidth(column.id),
|
|
203
|
+
widthStyle: ctx.getColumnWidthStyle(column.id),
|
|
204
|
+
implicitWidth: !ctx.hasAuthoredColumnWidthSpec(column.id),
|
|
205
|
+
minWidth: ctx.getColumnMinWidth(column.id),
|
|
206
|
+
maxWidth: ctx.getColumnMaxWidth(column.id)
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return columns;
|
|
211
|
+
});
|
|
212
|
+
|
|
167
213
|
const explicitManagedTableWidth = $derived.by(() => {
|
|
214
|
+
void $layoutVersion;
|
|
215
|
+
if (ctx.hasRelativeVisibleColumnWidths()) return undefined;
|
|
216
|
+
|
|
168
217
|
const widths = columnWidths ?? defaultColumnWidths;
|
|
169
218
|
if (!widths) return undefined;
|
|
170
219
|
|
|
@@ -172,8 +221,10 @@
|
|
|
172
221
|
let hasAnyWidth = false;
|
|
173
222
|
for (const [columnId, width] of widths) {
|
|
174
223
|
if (ctx.isColumnHidden(columnId)) continue;
|
|
175
|
-
if (!
|
|
176
|
-
|
|
224
|
+
if (typeof width === 'string' && !width.trim().endsWith('px')) return undefined;
|
|
225
|
+
const numericWidth = typeof width === 'number' ? width : Number.parseFloat(width);
|
|
226
|
+
if (!Number.isFinite(numericWidth)) return undefined;
|
|
227
|
+
total += numericWidth;
|
|
177
228
|
hasAnyWidth = true;
|
|
178
229
|
}
|
|
179
230
|
|
|
@@ -183,16 +234,37 @@
|
|
|
183
234
|
const managedTableWidth = $derived.by(() => {
|
|
184
235
|
void $widthVersion;
|
|
185
236
|
void $layoutVersion;
|
|
186
|
-
|
|
187
|
-
const
|
|
237
|
+
const widths = ctx.getResolvedVisibleColumnWidths();
|
|
238
|
+
const columnCount = ctx.getVisibleColumnCount();
|
|
239
|
+
if (widths.size === 0 || widths.size < columnCount) return undefined;
|
|
240
|
+
if (ctx.hasRelativeVisibleColumnWidths()) return undefined;
|
|
241
|
+
let total = 0;
|
|
242
|
+
for (const w of widths.values()) total += w;
|
|
243
|
+
return total;
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
const relativeResolvedTableWidth = $derived.by(() => {
|
|
247
|
+
void $widthVersion;
|
|
248
|
+
void $layoutVersion;
|
|
249
|
+
if (!ctx.hasRelativeVisibleColumnWidths()) return undefined;
|
|
250
|
+
|
|
251
|
+
const widths = ctx.getResolvedVisibleColumnWidths();
|
|
188
252
|
const columnCount = ctx.getVisibleColumnCount();
|
|
189
253
|
if (widths.size === 0 || widths.size < columnCount) return undefined;
|
|
254
|
+
|
|
190
255
|
let total = 0;
|
|
191
256
|
for (const w of widths.values()) total += w;
|
|
192
257
|
return total;
|
|
193
258
|
});
|
|
194
259
|
|
|
195
|
-
const
|
|
260
|
+
const fallbackRelativeTableWidth = $derived.by(() => {
|
|
261
|
+
void $layoutVersion;
|
|
262
|
+
return ctx.hasRelativeVisibleColumnWidths() ? '100%' : undefined;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const resolvedTableWidth = $derived(
|
|
266
|
+
managedTableWidth ?? explicitManagedTableWidth ?? relativeResolvedTableWidth
|
|
267
|
+
);
|
|
196
268
|
|
|
197
269
|
context = ctx;
|
|
198
270
|
|
|
@@ -223,6 +295,33 @@
|
|
|
223
295
|
element = tableElement;
|
|
224
296
|
});
|
|
225
297
|
|
|
298
|
+
$effect(() => {
|
|
299
|
+
if (!tableElement) return;
|
|
300
|
+
|
|
301
|
+
const resizeTarget = tableElement.parentElement ?? tableElement;
|
|
302
|
+
const refreshMeasuredLayout = () => {
|
|
303
|
+
ctx.refreshMeasuredLayout();
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
window.addEventListener('resize', refreshMeasuredLayout);
|
|
307
|
+
window.visualViewport?.addEventListener('resize', refreshMeasuredLayout);
|
|
308
|
+
|
|
309
|
+
const resizeObserver =
|
|
310
|
+
typeof ResizeObserver !== 'undefined'
|
|
311
|
+
? new ResizeObserver(() => {
|
|
312
|
+
refreshMeasuredLayout();
|
|
313
|
+
})
|
|
314
|
+
: null;
|
|
315
|
+
|
|
316
|
+
resizeObserver?.observe(resizeTarget);
|
|
317
|
+
|
|
318
|
+
return () => {
|
|
319
|
+
window.removeEventListener('resize', refreshMeasuredLayout);
|
|
320
|
+
window.visualViewport?.removeEventListener('resize', refreshMeasuredLayout);
|
|
321
|
+
resizeObserver?.disconnect();
|
|
322
|
+
};
|
|
323
|
+
});
|
|
324
|
+
|
|
226
325
|
$effect(() => {
|
|
227
326
|
ctx.setSelectionMode(selectionMode);
|
|
228
327
|
});
|
|
@@ -369,8 +468,13 @@
|
|
|
369
468
|
bind:this={tableElement}
|
|
370
469
|
role="grid"
|
|
371
470
|
class={className}
|
|
372
|
-
style
|
|
373
|
-
style:
|
|
471
|
+
style:--table-visible-column-count={ariaColCount ? `${ariaColCount}` : undefined}
|
|
472
|
+
style:table-layout={hasResizable || hasDefinedColumnWidths || resolvedTableWidth !== undefined
|
|
473
|
+
? 'fixed'
|
|
474
|
+
: undefined}
|
|
475
|
+
style:width={resolvedTableWidth !== undefined
|
|
476
|
+
? `${resolvedTableWidth}px`
|
|
477
|
+
: fallbackRelativeTableWidth}
|
|
374
478
|
style:min-width={resolvedTableWidth !== undefined ? '0' : undefined}
|
|
375
479
|
aria-label={ariaLabel}
|
|
376
480
|
aria-labelledby={ariaLabelledby}
|
|
@@ -388,6 +492,19 @@
|
|
|
388
492
|
onkeydown={handleKeyDown}
|
|
389
493
|
{...restProps}
|
|
390
494
|
>
|
|
495
|
+
{#if layoutColumns.length > 0}
|
|
496
|
+
<colgroup>
|
|
497
|
+
{#each layoutColumns as column (column.id)}
|
|
498
|
+
<col
|
|
499
|
+
data-table-implicit-width={column.implicitWidth ? 'true' : undefined}
|
|
500
|
+
style:width={column.widthStyle}
|
|
501
|
+
style:min-width={column.minWidth !== undefined ? `${column.minWidth}px` : undefined}
|
|
502
|
+
style:max-width={column.maxWidth !== undefined ? `${column.maxWidth}px` : undefined}
|
|
503
|
+
/>
|
|
504
|
+
{/each}
|
|
505
|
+
</colgroup>
|
|
506
|
+
{/if}
|
|
507
|
+
|
|
391
508
|
{#if children}
|
|
392
509
|
{@render children()}
|
|
393
510
|
{/if}
|
|
@@ -400,3 +517,17 @@
|
|
|
400
517
|
<span id={ctx.selectionUnavailableDescriptionId} style={visuallyHiddenStyle}
|
|
401
518
|
>Selection unavailable for this row.</span
|
|
402
519
|
>
|
|
520
|
+
|
|
521
|
+
<style>
|
|
522
|
+
:global(table[role='grid']:has([data-table-column-resizer='true'])) {
|
|
523
|
+
table-layout: fixed;
|
|
524
|
+
width: 100%;
|
|
525
|
+
min-width: 0;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
:global(
|
|
529
|
+
table[role='grid']:has([data-table-column-resizer='true']) col[data-table-implicit-width='true']
|
|
530
|
+
) {
|
|
531
|
+
width: calc(100% / var(--table-visible-column-count, 1));
|
|
532
|
+
}
|
|
533
|
+
</style>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
2
|
import { onDestroy } from 'svelte';
|
|
3
|
-
import { SvelteMap } from 'svelte/reactivity';
|
|
4
3
|
import { writable } from 'svelte/store';
|
|
5
4
|
import { setTableRowContext, useTableContext, useTableSectionContext } from '../root/context';
|
|
6
5
|
import type { TableRowProps } from '../types.js';
|
|
@@ -22,14 +21,12 @@
|
|
|
22
21
|
const section = useTableSectionContext();
|
|
23
22
|
const rowToken = table.createInstanceToken('row');
|
|
24
23
|
const cellOrder: string[] = [];
|
|
25
|
-
const cellElements
|
|
24
|
+
const cellElements: Record<string, () => HTMLElement | undefined> = {};
|
|
26
25
|
const cellOrderVersion = writable(0);
|
|
27
26
|
|
|
28
27
|
let rowElement = $state<HTMLTableRowElement | undefined>(undefined);
|
|
29
|
-
let childListObserver: MutationObserver | null = null;
|
|
30
28
|
|
|
31
29
|
function notifyCellOrderChange() {
|
|
32
|
-
cellIndexCache = null;
|
|
33
30
|
cellOrderVersion.update((value) => value + 1);
|
|
34
31
|
}
|
|
35
32
|
|
|
@@ -39,12 +36,12 @@
|
|
|
39
36
|
notifyCellOrderChange();
|
|
40
37
|
}
|
|
41
38
|
if (getElement) {
|
|
42
|
-
cellElements
|
|
39
|
+
cellElements[token] = getElement;
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
|
|
46
43
|
function unregisterCellToken(token: string) {
|
|
47
|
-
cellElements
|
|
44
|
+
delete cellElements[token];
|
|
48
45
|
const index = cellOrder.indexOf(token);
|
|
49
46
|
if (index >= 0) {
|
|
50
47
|
cellOrder.splice(index, 1);
|
|
@@ -52,40 +49,12 @@
|
|
|
52
49
|
}
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
let cellIndexCache: Map<string, number> | null = null;
|
|
56
|
-
|
|
57
|
-
function buildCellIndexCache(): Map<string, number> {
|
|
58
|
-
if (cellIndexCache) return cellIndexCache;
|
|
59
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- plain cache, not reactive state
|
|
60
|
-
const cache = new Map<string, number>();
|
|
61
|
-
if (rowElement) {
|
|
62
|
-
const directCells = rowElement.children;
|
|
63
|
-
// eslint-disable-next-line svelte/prefer-svelte-reactivity -- plain cache, not reactive state
|
|
64
|
-
const elementToIndex = new Map<HTMLElement, number>();
|
|
65
|
-
for (let i = 0; i < directCells.length; i += 1) {
|
|
66
|
-
const child = directCells[i];
|
|
67
|
-
if (child instanceof HTMLElement) {
|
|
68
|
-
elementToIndex.set(child, i);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
for (const registeredToken of cellOrder) {
|
|
72
|
-
const element = cellElements.get(registeredToken)?.();
|
|
73
|
-
if (element) {
|
|
74
|
-
const idx = elementToIndex.get(element);
|
|
75
|
-
if (idx !== undefined) {
|
|
76
|
-
cache.set(registeredToken, idx);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
cellIndexCache = cache;
|
|
82
|
-
return cache;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
52
|
function getCellIndex(token: string) {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
53
|
+
const element = cellElements[token]?.();
|
|
54
|
+
if (rowElement && element) {
|
|
55
|
+
const index = Array.from(rowElement.children).indexOf(element);
|
|
56
|
+
if (index >= 0) return index;
|
|
57
|
+
}
|
|
89
58
|
return cellOrder.indexOf(token);
|
|
90
59
|
}
|
|
91
60
|
|
|
@@ -120,23 +89,7 @@
|
|
|
120
89
|
syncRowRegistration();
|
|
121
90
|
});
|
|
122
91
|
|
|
123
|
-
let pendingCellOrderNotify = false;
|
|
124
|
-
|
|
125
|
-
function scheduleCellOrderNotify() {
|
|
126
|
-
if (!pendingCellOrderNotify) {
|
|
127
|
-
pendingCellOrderNotify = true;
|
|
128
|
-
queueMicrotask(() => {
|
|
129
|
-
pendingCellOrderNotify = false;
|
|
130
|
-
cellIndexCache = null;
|
|
131
|
-
notifyCellOrderChange();
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
92
|
$effect(() => {
|
|
137
|
-
childListObserver?.disconnect();
|
|
138
|
-
childListObserver = null;
|
|
139
|
-
|
|
140
93
|
if (!rowElement) {
|
|
141
94
|
return;
|
|
142
95
|
}
|
|
@@ -145,18 +98,25 @@
|
|
|
145
98
|
scheduleCellOrderNotify();
|
|
146
99
|
});
|
|
147
100
|
observer.observe(rowElement, { childList: true });
|
|
148
|
-
childListObserver = observer;
|
|
149
101
|
|
|
150
102
|
return () => {
|
|
151
103
|
observer.disconnect();
|
|
152
|
-
if (childListObserver === observer) {
|
|
153
|
-
childListObserver = null;
|
|
154
|
-
}
|
|
155
104
|
};
|
|
156
105
|
});
|
|
157
106
|
|
|
107
|
+
let pendingCellOrderNotify = false;
|
|
108
|
+
|
|
109
|
+
function scheduleCellOrderNotify() {
|
|
110
|
+
if (!pendingCellOrderNotify) {
|
|
111
|
+
pendingCellOrderNotify = true;
|
|
112
|
+
queueMicrotask(() => {
|
|
113
|
+
pendingCellOrderNotify = false;
|
|
114
|
+
notifyCellOrderChange();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
158
119
|
onDestroy(() => {
|
|
159
|
-
childListObserver?.disconnect();
|
|
160
120
|
table.unregisterRow(rowToken);
|
|
161
121
|
});
|
|
162
122
|
|
package/dist/table/types.d.ts
CHANGED
|
@@ -35,16 +35,16 @@ export type TableRootProps = Omit<HTMLAttributes<HTMLTableElement>, 'children'>
|
|
|
35
35
|
defaultSelectedKeys?: Iterable<TableSelectionKey>;
|
|
36
36
|
sortDescriptor?: TableSortDescriptor;
|
|
37
37
|
defaultSortDescriptor?: TableSortDescriptor;
|
|
38
|
-
columnWidths?: Map<string,
|
|
39
|
-
defaultColumnWidths?: Iterable<readonly [string,
|
|
38
|
+
columnWidths?: Map<string, TableColumnWidth>;
|
|
39
|
+
defaultColumnWidths?: Iterable<readonly [string, TableColumnWidth]>;
|
|
40
40
|
disabledKeys?: Iterable<TableSelectionKey>;
|
|
41
41
|
onRowAction?: TableRowActionHandler;
|
|
42
42
|
onSelectionChange?: (keys: Set<TableSelectionKey>) => void;
|
|
43
43
|
onSortChange?: (descriptor: TableSortDescriptor | undefined) => void;
|
|
44
|
-
onColumnWidthsChange?: (widths: Map<string,
|
|
44
|
+
onColumnWidthsChange?: (widths: Map<string, TableColumnWidth>) => void;
|
|
45
45
|
onHiddenColumnsChange?: (columnIds: string[]) => void;
|
|
46
46
|
onColumnResizeStart?: (columnId: string) => void;
|
|
47
|
-
onColumnResizeEnd?: (widths: Map<string,
|
|
47
|
+
onColumnResizeEnd?: (widths: Map<string, TableColumnWidth>) => void;
|
|
48
48
|
children?: Snippet;
|
|
49
49
|
class?: string;
|
|
50
50
|
context?: TableContext;
|