@human-kit/svelte-components 1.0.0-alpha.16 → 1.0.0-alpha.17
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/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 +17 -0
- package/dist/table/checkbox/README.md +1 -1
- package/dist/table/checkbox/table-checkbox.svelte +2 -15
- 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 +19 -16
- 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-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 +13 -7
- package/dist/table/root/context.js +363 -38
- package/dist/table/root/table-root.svelte +113 -9
- package/dist/table/types.d.ts +4 -4
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { getContext, setContext } from 'svelte';
|
|
1
|
+
import { flushSync, getContext, setContext } from 'svelte';
|
|
2
2
|
import { writable } from 'svelte/store';
|
|
3
3
|
const TABLE_KEY = Symbol('table');
|
|
4
4
|
const TABLE_SECTION_KEY = Symbol('table-section');
|
|
5
5
|
const TABLE_ROW_KEY = Symbol('table-row');
|
|
6
6
|
const TABLE_COLUMN_KEY = Symbol('table-column');
|
|
7
7
|
const TABLE_CELL_KEY = Symbol('table-cell');
|
|
8
|
+
const IS_BROWSER = typeof window !== 'undefined';
|
|
9
|
+
export const DEFAULT_TABLE_COLUMN_MIN_WIDTH = 60;
|
|
8
10
|
export function createTableContext(options = {}) {
|
|
9
11
|
let selectionMode = options.selectionMode ?? 'none';
|
|
10
12
|
let selectionBehavior = options.selectionBehavior ?? 'toggle';
|
|
@@ -20,15 +22,18 @@ export function createTableContext(options = {}) {
|
|
|
20
22
|
const disabledKeys = new Set(options.disabledKeys ?? []);
|
|
21
23
|
const hiddenColumnIds = new Set(options.initialHiddenColumns ?? []);
|
|
22
24
|
let resizingColumnId = null;
|
|
25
|
+
let resizeSession = null;
|
|
23
26
|
let suppressNextHeaderClick = false;
|
|
24
27
|
const columns = new Map();
|
|
25
28
|
const columnIds = new Map();
|
|
26
29
|
const columnOrder = [];
|
|
27
30
|
const columnsWithResizers = new Set();
|
|
31
|
+
let resizerLayoutReady = false;
|
|
28
32
|
const columnWidths = new Map(options.initialColumnWidths ?? []);
|
|
29
33
|
const rows = new Map();
|
|
30
34
|
const headerRowOrder = [];
|
|
31
35
|
const bodyRowOrder = [];
|
|
36
|
+
let bodyRowsInitialized = false;
|
|
32
37
|
const cells = new Map();
|
|
33
38
|
const cellOrder = [];
|
|
34
39
|
let orderedRowTokensCache = {
|
|
@@ -39,6 +44,7 @@ export function createTableContext(options = {}) {
|
|
|
39
44
|
let visibleOrderedColumnTokensCache = null;
|
|
40
45
|
let columnWidthsCache = null;
|
|
41
46
|
let visibleColumnWidthsCache = null;
|
|
47
|
+
let resolvedVisibleColumnWidthsCache = null;
|
|
42
48
|
let navigableCellsCache = null;
|
|
43
49
|
let rowsWithCellsCache = null;
|
|
44
50
|
const layoutVersion = writable(0);
|
|
@@ -60,11 +66,20 @@ export function createTableContext(options = {}) {
|
|
|
60
66
|
visibleOrderedColumnTokensCache = null;
|
|
61
67
|
columnWidthsCache = null;
|
|
62
68
|
visibleColumnWidthsCache = null;
|
|
69
|
+
resolvedVisibleColumnWidthsCache = null;
|
|
63
70
|
navigableCellsCache = null;
|
|
64
71
|
rowsWithCellsCache = null;
|
|
65
72
|
}
|
|
66
73
|
let layoutNotifyScheduled = false;
|
|
67
74
|
let widthNotifyScheduled = false;
|
|
75
|
+
function syncResizerLayoutReady(nextReady) {
|
|
76
|
+
if (resizerLayoutReady === nextReady)
|
|
77
|
+
return;
|
|
78
|
+
resizerLayoutReady = nextReady;
|
|
79
|
+
invalidateLayoutCaches();
|
|
80
|
+
layoutVersion.update((value) => value + 1);
|
|
81
|
+
notifyWidth();
|
|
82
|
+
}
|
|
68
83
|
function notifyLayout() {
|
|
69
84
|
invalidateLayoutCaches();
|
|
70
85
|
if (!layoutNotifyScheduled) {
|
|
@@ -88,6 +103,7 @@ export function createTableContext(options = {}) {
|
|
|
88
103
|
function notifyWidth() {
|
|
89
104
|
columnWidthsCache = null;
|
|
90
105
|
visibleColumnWidthsCache = null;
|
|
106
|
+
resolvedVisibleColumnWidthsCache = null;
|
|
91
107
|
if (!widthNotifyScheduled) {
|
|
92
108
|
widthNotifyScheduled = true;
|
|
93
109
|
queueMicrotask(() => {
|
|
@@ -96,22 +112,49 @@ export function createTableContext(options = {}) {
|
|
|
96
112
|
});
|
|
97
113
|
}
|
|
98
114
|
}
|
|
115
|
+
function notifyWidthImmediately() {
|
|
116
|
+
columnWidthsCache = null;
|
|
117
|
+
visibleColumnWidthsCache = null;
|
|
118
|
+
resolvedVisibleColumnWidthsCache = null;
|
|
119
|
+
flushSync(() => {
|
|
120
|
+
widthVersion.update((value) => value + 1);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
99
123
|
function notifyResize() {
|
|
100
124
|
resizeVersion.update((value) => value + 1);
|
|
101
125
|
}
|
|
102
|
-
function
|
|
126
|
+
function parseColumnWidth(width) {
|
|
103
127
|
if (typeof width === 'number') {
|
|
104
|
-
return Number.isFinite(width) ? width : undefined;
|
|
128
|
+
return Number.isFinite(width) ? { unit: 'px', value: width } : undefined;
|
|
105
129
|
}
|
|
106
130
|
if (typeof width === 'string') {
|
|
107
|
-
const match = width.trim().match(/^(\d+(?:\.\d+)?)px$/i);
|
|
131
|
+
const match = width.trim().match(/^(\d+(?:\.\d+)?)(px|%|fr)$/i);
|
|
108
132
|
if (!match)
|
|
109
133
|
return undefined;
|
|
110
134
|
const next = Number(match[1]);
|
|
111
|
-
|
|
135
|
+
if (!Number.isFinite(next))
|
|
136
|
+
return undefined;
|
|
137
|
+
const unit = match[2].toLowerCase();
|
|
138
|
+
return { unit, value: next };
|
|
112
139
|
}
|
|
113
140
|
return undefined;
|
|
114
141
|
}
|
|
142
|
+
function normalizeColumnWidth(width) {
|
|
143
|
+
const parsed = parseColumnWidth(width);
|
|
144
|
+
if (!parsed)
|
|
145
|
+
return undefined;
|
|
146
|
+
if (parsed.unit === 'px') {
|
|
147
|
+
return typeof width === 'number' ? width : `${parsed.value}px`;
|
|
148
|
+
}
|
|
149
|
+
if (parsed.unit === '%') {
|
|
150
|
+
return `${parsed.value}%`;
|
|
151
|
+
}
|
|
152
|
+
return `${parsed.value}fr`;
|
|
153
|
+
}
|
|
154
|
+
function isRelativeColumnWidth(width) {
|
|
155
|
+
const parsed = parseColumnWidth(width);
|
|
156
|
+
return parsed ? parsed.unit !== 'px' : false;
|
|
157
|
+
}
|
|
115
158
|
function getColumnRegistrationById(columnId) {
|
|
116
159
|
const token = columnIds.get(columnId);
|
|
117
160
|
return token ? columns.get(token) : undefined;
|
|
@@ -125,9 +168,61 @@ export function createTableContext(options = {}) {
|
|
|
125
168
|
function getColumnMaxWidth(columnId) {
|
|
126
169
|
return getColumnRegistrationById(columnId)?.maxWidth;
|
|
127
170
|
}
|
|
171
|
+
function getFixedColumnWidthSpec(columnId) {
|
|
172
|
+
return normalizeColumnWidth(getColumnRegistrationById(columnId)?.width);
|
|
173
|
+
}
|
|
174
|
+
function getManagedColumnWidthSpec(columnId) {
|
|
175
|
+
return normalizeColumnWidth(columnWidths.get(columnId));
|
|
176
|
+
}
|
|
177
|
+
function getDefaultColumnWidthSpec(columnId) {
|
|
178
|
+
return normalizeColumnWidth(getColumnRegistrationById(columnId)?.defaultWidth);
|
|
179
|
+
}
|
|
180
|
+
function hasAuthoredColumnWidthSpec(columnId) {
|
|
181
|
+
return (getFixedColumnWidthSpec(columnId) !== undefined ||
|
|
182
|
+
getManagedColumnWidthSpec(columnId) !== undefined ||
|
|
183
|
+
getDefaultColumnWidthSpec(columnId) !== undefined);
|
|
184
|
+
}
|
|
185
|
+
function getEffectiveColumnWidthSpec(columnId) {
|
|
186
|
+
const fixedWidth = getFixedColumnWidthSpec(columnId);
|
|
187
|
+
if (fixedWidth !== undefined) {
|
|
188
|
+
return fixedWidth;
|
|
189
|
+
}
|
|
190
|
+
const managedWidth = getManagedColumnWidthSpec(columnId);
|
|
191
|
+
if (managedWidth !== undefined) {
|
|
192
|
+
return managedWidth;
|
|
193
|
+
}
|
|
194
|
+
const defaultWidth = getDefaultColumnWidthSpec(columnId);
|
|
195
|
+
if (defaultWidth !== undefined) {
|
|
196
|
+
return defaultWidth;
|
|
197
|
+
}
|
|
198
|
+
return resizerLayoutReady && columnsWithResizers.size > 0 ? '1fr' : undefined;
|
|
199
|
+
}
|
|
200
|
+
function getMeasuredTableWidth() {
|
|
201
|
+
const tableCell = Array.from(cells.values()).find((cell) => cell.element)?.element;
|
|
202
|
+
const tableElement = tableCell?.closest('table');
|
|
203
|
+
const tableWidth = tableElement?.getBoundingClientRect().width;
|
|
204
|
+
const containerElement = tableElement?.parentElement;
|
|
205
|
+
const containerClientWidth = containerElement?.clientWidth;
|
|
206
|
+
const containerStyle = containerElement ? getComputedStyle(containerElement) : undefined;
|
|
207
|
+
const containerPaddingLeft = Number.parseFloat(containerStyle?.paddingLeft ?? '0');
|
|
208
|
+
const containerPaddingRight = Number.parseFloat(containerStyle?.paddingRight ?? '0');
|
|
209
|
+
const containerWidth = containerClientWidth !== undefined && Number.isFinite(containerClientWidth)
|
|
210
|
+
? containerClientWidth - containerPaddingLeft - containerPaddingRight
|
|
211
|
+
: undefined;
|
|
212
|
+
// Use the container width as the stable reference for fr/% resolution.
|
|
213
|
+
// Using Math.max(table, container) causes a feedback loop during resize:
|
|
214
|
+
// the table grows → measurement returns more → fr columns get more space →
|
|
215
|
+
// the table grows further. The container width is stable and represents
|
|
216
|
+
// the actual available space the table should fill.
|
|
217
|
+
const width = containerWidth ?? tableWidth;
|
|
218
|
+
if (width === undefined || width <= 0 || !Number.isFinite(width)) {
|
|
219
|
+
return undefined;
|
|
220
|
+
}
|
|
221
|
+
return Math.round(width);
|
|
222
|
+
}
|
|
128
223
|
function clampColumnWidth(columnId, width) {
|
|
129
224
|
const registration = getColumnRegistrationById(columnId);
|
|
130
|
-
const minWidth = registration?.minWidth ??
|
|
225
|
+
const minWidth = registration?.minWidth ?? DEFAULT_TABLE_COLUMN_MIN_WIDTH;
|
|
131
226
|
const maxWidth = registration?.maxWidth;
|
|
132
227
|
let next = Math.round(width);
|
|
133
228
|
if (Number.isNaN(next) || !Number.isFinite(next)) {
|
|
@@ -141,9 +236,7 @@ export function createTableContext(options = {}) {
|
|
|
141
236
|
}
|
|
142
237
|
function hasResizableColumns() {
|
|
143
238
|
for (const column of columns.values()) {
|
|
144
|
-
if (
|
|
145
|
-
continue;
|
|
146
|
-
if (columnsWithResizers.has(column.token))
|
|
239
|
+
if (isColumnResizable(column.id))
|
|
147
240
|
return true;
|
|
148
241
|
}
|
|
149
242
|
return false;
|
|
@@ -152,12 +245,26 @@ export function createTableContext(options = {}) {
|
|
|
152
245
|
if (columnsWithResizers.has(columnToken))
|
|
153
246
|
return;
|
|
154
247
|
columnsWithResizers.add(columnToken);
|
|
155
|
-
|
|
248
|
+
if (IS_BROWSER) {
|
|
249
|
+
syncResizerLayoutReady(true);
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
resizerLayoutReady = false;
|
|
253
|
+
invalidateLayoutCaches();
|
|
254
|
+
layoutVersion.update((value) => value + 1);
|
|
255
|
+
notifyWidth();
|
|
156
256
|
}
|
|
157
257
|
function unregisterColumnResizer(columnToken) {
|
|
158
258
|
if (!columnsWithResizers.delete(columnToken))
|
|
159
259
|
return;
|
|
160
|
-
|
|
260
|
+
if (IS_BROWSER) {
|
|
261
|
+
syncResizerLayoutReady(columnsWithResizers.size > 0);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
resizerLayoutReady = false;
|
|
265
|
+
invalidateLayoutCaches();
|
|
266
|
+
layoutVersion.update((value) => value + 1);
|
|
267
|
+
notifyWidth();
|
|
161
268
|
}
|
|
162
269
|
function sameColumnMetadata(left, right) {
|
|
163
270
|
return (left.token === right.token &&
|
|
@@ -271,15 +378,72 @@ export function createTableContext(options = {}) {
|
|
|
271
378
|
return getColumnRegistrationById(columnId)?.textValue;
|
|
272
379
|
}
|
|
273
380
|
function getColumnWidth(columnId) {
|
|
274
|
-
const
|
|
275
|
-
if (
|
|
276
|
-
return
|
|
381
|
+
const parsed = parseColumnWidth(getEffectiveColumnWidthSpec(columnId));
|
|
382
|
+
if (!parsed)
|
|
383
|
+
return undefined;
|
|
384
|
+
if (!isColumnHidden(columnId)) {
|
|
385
|
+
if (parsed.unit !== 'px') {
|
|
386
|
+
return undefined;
|
|
387
|
+
}
|
|
388
|
+
return getResolvedVisibleColumnWidths().get(columnId);
|
|
277
389
|
}
|
|
278
|
-
|
|
279
|
-
|
|
390
|
+
return parsed?.unit === 'px' ? clampColumnWidth(columnId, parsed.value) : undefined;
|
|
391
|
+
}
|
|
392
|
+
function formatCssLength(length) {
|
|
393
|
+
const rounded = Math.round(length * 1000) / 1000;
|
|
394
|
+
return Number.isInteger(rounded) ? `${rounded}` : `${rounded}`;
|
|
395
|
+
}
|
|
396
|
+
function getColumnWidthStyle(columnId) {
|
|
397
|
+
const resolvedWidth = getColumnWidth(columnId);
|
|
398
|
+
if (resolvedWidth !== undefined) {
|
|
399
|
+
return `${resolvedWidth}px`;
|
|
400
|
+
}
|
|
401
|
+
const parsed = parseColumnWidth(getEffectiveColumnWidthSpec(columnId));
|
|
402
|
+
if (!parsed)
|
|
403
|
+
return undefined;
|
|
404
|
+
if (parsed.unit === 'px') {
|
|
405
|
+
return `${clampColumnWidth(columnId, parsed.value)}px`;
|
|
406
|
+
}
|
|
407
|
+
if (parsed.unit === '%') {
|
|
408
|
+
return `${parsed.value}%`;
|
|
409
|
+
}
|
|
410
|
+
let fixedPxTotal = 0;
|
|
411
|
+
let percentTotal = 0;
|
|
412
|
+
let totalFr = 0;
|
|
413
|
+
for (const token of getVisibleOrderedColumnTokens()) {
|
|
414
|
+
const column = columns.get(token);
|
|
415
|
+
if (!column)
|
|
416
|
+
continue;
|
|
417
|
+
const spec = parseColumnWidth(getEffectiveColumnWidthSpec(column.id));
|
|
418
|
+
if (!spec)
|
|
419
|
+
continue;
|
|
420
|
+
if (spec.unit === 'px') {
|
|
421
|
+
fixedPxTotal += clampColumnWidth(column.id, spec.value);
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
if (spec.unit === '%') {
|
|
425
|
+
percentTotal += spec.value;
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
totalFr += spec.value;
|
|
429
|
+
}
|
|
430
|
+
if (totalFr <= 0)
|
|
280
431
|
return undefined;
|
|
281
|
-
const
|
|
282
|
-
|
|
432
|
+
const frShare = parsed.value / totalFr;
|
|
433
|
+
const availableWidthTerms = ['100%'];
|
|
434
|
+
if (fixedPxTotal > 0) {
|
|
435
|
+
availableWidthTerms.push(`${formatCssLength(fixedPxTotal)}px`);
|
|
436
|
+
}
|
|
437
|
+
if (percentTotal > 0) {
|
|
438
|
+
availableWidthTerms.push(`${formatCssLength(percentTotal)}%`);
|
|
439
|
+
}
|
|
440
|
+
const availableWidthExpression = availableWidthTerms.length === 1
|
|
441
|
+
? availableWidthTerms[0]
|
|
442
|
+
: `(${availableWidthTerms.join(' - ')})`;
|
|
443
|
+
if (frShare === 1) {
|
|
444
|
+
return `calc(${availableWidthExpression})`;
|
|
445
|
+
}
|
|
446
|
+
return `calc(${availableWidthExpression} * ${formatCssLength(frShare)})`;
|
|
283
447
|
}
|
|
284
448
|
function isColumnResizable(columnId) {
|
|
285
449
|
const column = getColumnRegistrationById(columnId);
|
|
@@ -297,7 +461,7 @@ export function createTableContext(options = {}) {
|
|
|
297
461
|
const column = columns.get(token);
|
|
298
462
|
if (!column)
|
|
299
463
|
continue;
|
|
300
|
-
const width =
|
|
464
|
+
const width = getManagedColumnWidthSpec(column.id);
|
|
301
465
|
if (width !== undefined) {
|
|
302
466
|
widths.set(column.id, width);
|
|
303
467
|
}
|
|
@@ -317,6 +481,63 @@ export function createTableContext(options = {}) {
|
|
|
317
481
|
visibleColumnWidthsCache = widths;
|
|
318
482
|
return widths;
|
|
319
483
|
}
|
|
484
|
+
function hasRelativeVisibleColumnWidths() {
|
|
485
|
+
for (const token of getVisibleOrderedColumnTokens()) {
|
|
486
|
+
const column = columns.get(token);
|
|
487
|
+
if (!column)
|
|
488
|
+
continue;
|
|
489
|
+
if (isRelativeColumnWidth(getEffectiveColumnWidthSpec(column.id))) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return false;
|
|
494
|
+
}
|
|
495
|
+
function getResolvedVisibleColumnWidths() {
|
|
496
|
+
if (resolvedVisibleColumnWidthsCache)
|
|
497
|
+
return resolvedVisibleColumnWidthsCache;
|
|
498
|
+
const widths = new Map();
|
|
499
|
+
const flexibleColumns = [];
|
|
500
|
+
const tableWidth = getMeasuredTableWidth();
|
|
501
|
+
let remainingWidth = tableWidth;
|
|
502
|
+
let totalFr = 0;
|
|
503
|
+
for (const token of getVisibleOrderedColumnTokens()) {
|
|
504
|
+
const column = columns.get(token);
|
|
505
|
+
if (!column)
|
|
506
|
+
continue;
|
|
507
|
+
const parsed = parseColumnWidth(getEffectiveColumnWidthSpec(column.id));
|
|
508
|
+
if (!parsed)
|
|
509
|
+
continue;
|
|
510
|
+
if (parsed.unit === 'px') {
|
|
511
|
+
const nextWidth = clampColumnWidth(column.id, parsed.value);
|
|
512
|
+
widths.set(column.id, nextWidth);
|
|
513
|
+
if (remainingWidth !== undefined) {
|
|
514
|
+
remainingWidth -= nextWidth;
|
|
515
|
+
}
|
|
516
|
+
continue;
|
|
517
|
+
}
|
|
518
|
+
if (parsed.unit === '%') {
|
|
519
|
+
if (tableWidth === undefined)
|
|
520
|
+
continue;
|
|
521
|
+
const nextWidth = clampColumnWidth(column.id, (tableWidth * parsed.value) / 100);
|
|
522
|
+
widths.set(column.id, nextWidth);
|
|
523
|
+
if (remainingWidth !== undefined) {
|
|
524
|
+
remainingWidth -= nextWidth;
|
|
525
|
+
}
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
flexibleColumns.push({ columnId: column.id, fr: parsed.value });
|
|
529
|
+
totalFr += parsed.value;
|
|
530
|
+
}
|
|
531
|
+
if (tableWidth !== undefined && flexibleColumns.length > 0 && totalFr > 0) {
|
|
532
|
+
const distributableWidth = Math.max(remainingWidth ?? tableWidth, 0);
|
|
533
|
+
for (const entry of flexibleColumns) {
|
|
534
|
+
const nextWidth = clampColumnWidth(entry.columnId, (distributableWidth * entry.fr) / totalFr);
|
|
535
|
+
widths.set(entry.columnId, nextWidth);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
resolvedVisibleColumnWidthsCache = widths;
|
|
539
|
+
return widths;
|
|
540
|
+
}
|
|
320
541
|
function getMeasuredHeaderWidth(columnToken) {
|
|
321
542
|
const headerCell = Array.from(cells.values()).find((cell) => cell.section === 'header' && cell.columnToken === columnToken && cell.element);
|
|
322
543
|
const width = headerCell?.element?.getBoundingClientRect().width;
|
|
@@ -325,42 +546,91 @@ export function createTableContext(options = {}) {
|
|
|
325
546
|
}
|
|
326
547
|
return Math.round(width);
|
|
327
548
|
}
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
549
|
+
function getResizableRelativeTailColumnId(activeColumnId) {
|
|
550
|
+
return getVisibleOrderedColumnTokens()
|
|
551
|
+
.map((token) => columns.get(token))
|
|
552
|
+
.filter((column) => !!column)
|
|
553
|
+
.filter((column) => {
|
|
554
|
+
if (column.id === activeColumnId)
|
|
555
|
+
return false;
|
|
556
|
+
if (getFixedColumnWidthSpec(column.id) !== undefined)
|
|
557
|
+
return false;
|
|
558
|
+
return isRelativeColumnWidth(getEffectiveColumnWidthSpec(column.id));
|
|
559
|
+
})
|
|
560
|
+
.at(-1)?.id;
|
|
561
|
+
}
|
|
562
|
+
function prepareColumnWidthsForResize(activeColumnId) {
|
|
563
|
+
const next = new Map(columnWidths);
|
|
564
|
+
const baselineWidths = new Map();
|
|
565
|
+
const preservedFlexibleColumnId = getResizableRelativeTailColumnId(activeColumnId);
|
|
566
|
+
const preservedFlexibleEffectiveWidth = preservedFlexibleColumnId
|
|
567
|
+
? normalizeColumnWidth(getEffectiveColumnWidthSpec(preservedFlexibleColumnId))
|
|
568
|
+
: undefined;
|
|
569
|
+
const measuredTableWidth = getMeasuredTableWidth();
|
|
570
|
+
for (const token of getVisibleOrderedColumnTokens()) {
|
|
332
571
|
const column = columns.get(token);
|
|
333
572
|
if (!column)
|
|
334
573
|
continue;
|
|
574
|
+
if (getFixedColumnWidthSpec(column.id) !== undefined)
|
|
575
|
+
continue;
|
|
335
576
|
const measuredWidth = getMeasuredHeaderWidth(token);
|
|
336
|
-
const resolvedWidth =
|
|
577
|
+
const resolvedWidth = getColumnWidth(column.id) ?? measuredWidth;
|
|
337
578
|
if (resolvedWidth === undefined)
|
|
338
579
|
continue;
|
|
339
|
-
const
|
|
340
|
-
next.set(column.id,
|
|
341
|
-
|
|
342
|
-
|
|
580
|
+
const frozenWidth = clampColumnWidth(column.id, resolvedWidth);
|
|
581
|
+
next.set(column.id, frozenWidth);
|
|
582
|
+
baselineWidths.set(column.id, frozenWidth);
|
|
583
|
+
}
|
|
584
|
+
let changed = next.size !== columnWidths.size;
|
|
585
|
+
if (!changed) {
|
|
586
|
+
for (const [columnId, width] of next) {
|
|
587
|
+
if (columnWidths.get(columnId) !== width) {
|
|
588
|
+
changed = true;
|
|
589
|
+
break;
|
|
590
|
+
}
|
|
343
591
|
}
|
|
344
592
|
}
|
|
345
|
-
if (!changed
|
|
593
|
+
if (!changed)
|
|
346
594
|
return;
|
|
347
595
|
columnWidths.clear();
|
|
348
596
|
for (const [columnId, width] of next) {
|
|
349
597
|
columnWidths.set(columnId, width);
|
|
350
598
|
}
|
|
351
599
|
columnWidthsCache = null;
|
|
600
|
+
visibleColumnWidthsCache = null;
|
|
601
|
+
const baselineTotalWidth = Array.from(baselineWidths.values()).reduce((total, current) => total + current, 0);
|
|
602
|
+
resizeSession = {
|
|
603
|
+
activeColumnId: activeColumnId,
|
|
604
|
+
flexibleTailColumnId: preservedFlexibleColumnId,
|
|
605
|
+
flexibleTailRestoreWidth: preservedFlexibleColumnId && isRelativeColumnWidth(preservedFlexibleEffectiveWidth)
|
|
606
|
+
? preservedFlexibleEffectiveWidth
|
|
607
|
+
: undefined,
|
|
608
|
+
baselineWidths,
|
|
609
|
+
baselineTableWidth: measuredTableWidth ?? baselineTotalWidth,
|
|
610
|
+
baselineTotalWidth
|
|
611
|
+
};
|
|
352
612
|
options.onColumnWidthsChange?.(getColumnWidths());
|
|
353
613
|
notifyWidth();
|
|
354
614
|
}
|
|
615
|
+
function shouldPrepareColumnWidthsForResize(columnId) {
|
|
616
|
+
if (getFixedColumnWidthSpec(columnId) !== undefined)
|
|
617
|
+
return false;
|
|
618
|
+
if (!hasRelativeVisibleColumnWidths())
|
|
619
|
+
return false;
|
|
620
|
+
return resizeSession?.activeColumnId !== columnId;
|
|
621
|
+
}
|
|
355
622
|
function setColumnWidths(widths) {
|
|
356
623
|
const next = new Map();
|
|
624
|
+
const incomingWidths = widths ? new Map(widths) : undefined;
|
|
357
625
|
for (const token of columnOrder) {
|
|
358
626
|
const column = columns.get(token);
|
|
359
627
|
if (!column)
|
|
360
628
|
continue;
|
|
361
|
-
|
|
629
|
+
if (getFixedColumnWidthSpec(column.id) !== undefined)
|
|
630
|
+
continue;
|
|
631
|
+
const incomingWidth = normalizeColumnWidth(incomingWidths?.get(column.id));
|
|
362
632
|
if (incomingWidth !== undefined) {
|
|
363
|
-
next.set(column.id,
|
|
633
|
+
next.set(column.id, incomingWidth);
|
|
364
634
|
}
|
|
365
635
|
}
|
|
366
636
|
columnWidths.clear();
|
|
@@ -370,13 +640,36 @@ export function createTableContext(options = {}) {
|
|
|
370
640
|
notifyWidth();
|
|
371
641
|
}
|
|
372
642
|
function setColumnWidth(columnId, width) {
|
|
643
|
+
if (getFixedColumnWidthSpec(columnId) !== undefined)
|
|
644
|
+
return;
|
|
373
645
|
if (!isColumnResizable(columnId))
|
|
374
646
|
return;
|
|
647
|
+
if (shouldPrepareColumnWidthsForResize(columnId)) {
|
|
648
|
+
prepareColumnWidthsForResize(columnId);
|
|
649
|
+
}
|
|
375
650
|
const nextWidth = clampColumnWidth(columnId, width);
|
|
376
|
-
|
|
651
|
+
const currentWidth = columnWidths.get(columnId);
|
|
652
|
+
if (resizeSession?.activeColumnId === columnId &&
|
|
653
|
+
resizeSession.flexibleTailColumnId !== undefined) {
|
|
654
|
+
const baselineActiveWidth = resizeSession.baselineWidths.get(columnId);
|
|
655
|
+
const baselineTailWidth = resizeSession.baselineWidths.get(resizeSession.flexibleTailColumnId);
|
|
656
|
+
if (baselineActiveWidth !== undefined && baselineTailWidth !== undefined) {
|
|
657
|
+
const widthDelta = nextWidth - baselineActiveWidth;
|
|
658
|
+
const overflowWidth = Math.max(resizeSession.baselineTotalWidth - resizeSession.baselineTableWidth, 0);
|
|
659
|
+
const tailTargetWidth = widthDelta >= 0
|
|
660
|
+
? baselineTailWidth - widthDelta
|
|
661
|
+
: baselineTailWidth + Math.max(-widthDelta - overflowWidth, 0);
|
|
662
|
+
const tailWidth = clampColumnWidth(resizeSession.flexibleTailColumnId, tailTargetWidth);
|
|
663
|
+
if (columnWidths.get(resizeSession.flexibleTailColumnId) !== tailWidth) {
|
|
664
|
+
columnWidths.set(resizeSession.flexibleTailColumnId, tailWidth);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
if (currentWidth === nextWidth)
|
|
377
669
|
return;
|
|
378
670
|
columnWidths.set(columnId, nextWidth);
|
|
379
671
|
columnWidthsCache = null;
|
|
672
|
+
visibleColumnWidthsCache = null;
|
|
380
673
|
options.onColumnWidthsChange?.(getColumnWidths());
|
|
381
674
|
notifyWidth();
|
|
382
675
|
}
|
|
@@ -502,9 +795,7 @@ export function createTableContext(options = {}) {
|
|
|
502
795
|
notifyWidth();
|
|
503
796
|
}
|
|
504
797
|
function measureIntrinsicElementWidth(cell) {
|
|
505
|
-
const target = cell
|
|
506
|
-
cell.firstElementChild ??
|
|
507
|
-
cell;
|
|
798
|
+
const target = cell;
|
|
508
799
|
const clone = target.cloneNode(true);
|
|
509
800
|
for (const separator of clone.querySelectorAll('[role="separator"]')) {
|
|
510
801
|
separator.remove();
|
|
@@ -563,9 +854,7 @@ export function createTableContext(options = {}) {
|
|
|
563
854
|
function startColumnResize(columnId) {
|
|
564
855
|
if (!isColumnResizable(columnId) || resizingColumnId === columnId)
|
|
565
856
|
return;
|
|
566
|
-
|
|
567
|
-
freezeColumnWidthsFromLayout();
|
|
568
|
-
}
|
|
857
|
+
resizeSession = null;
|
|
569
858
|
resizingColumnId = columnId;
|
|
570
859
|
options.onColumnResizeStart?.(columnId);
|
|
571
860
|
notifyResize();
|
|
@@ -573,8 +862,24 @@ export function createTableContext(options = {}) {
|
|
|
573
862
|
function endColumnResize() {
|
|
574
863
|
if (!resizingColumnId)
|
|
575
864
|
return;
|
|
865
|
+
let restoredFlexibleTail = false;
|
|
866
|
+
if (resizeSession?.flexibleTailColumnId !== undefined &&
|
|
867
|
+
resizeSession.activeColumnId !== resizeSession.flexibleTailColumnId &&
|
|
868
|
+
resizeSession.flexibleTailRestoreWidth !== undefined) {
|
|
869
|
+
const tailColumnId = resizeSession.flexibleTailColumnId;
|
|
870
|
+
columnWidths.set(tailColumnId, resizeSession.flexibleTailRestoreWidth);
|
|
871
|
+
columnWidthsCache = null;
|
|
872
|
+
visibleColumnWidthsCache = null;
|
|
873
|
+
resolvedVisibleColumnWidthsCache = null;
|
|
874
|
+
restoredFlexibleTail = true;
|
|
875
|
+
}
|
|
576
876
|
resizingColumnId = null;
|
|
877
|
+
if (restoredFlexibleTail) {
|
|
878
|
+
options.onColumnWidthsChange?.(getColumnWidths());
|
|
879
|
+
}
|
|
880
|
+
resizeSession = null;
|
|
577
881
|
options.onColumnResizeEnd?.(getColumnWidths());
|
|
882
|
+
notifyWidthImmediately();
|
|
578
883
|
notifyResize();
|
|
579
884
|
}
|
|
580
885
|
function suppressHeaderClickOnce() {
|
|
@@ -606,6 +911,7 @@ export function createTableContext(options = {}) {
|
|
|
606
911
|
if (targetOrder && !targetOrder.includes(row.token)) {
|
|
607
912
|
targetOrder.push(row.token);
|
|
608
913
|
}
|
|
914
|
+
notifySelection();
|
|
609
915
|
notifyLayout();
|
|
610
916
|
}
|
|
611
917
|
function unregisterRow(token) {
|
|
@@ -633,8 +939,19 @@ export function createTableContext(options = {}) {
|
|
|
633
939
|
notifyFocus();
|
|
634
940
|
}
|
|
635
941
|
}
|
|
942
|
+
notifySelection();
|
|
636
943
|
notifyLayout();
|
|
637
944
|
}
|
|
945
|
+
function markBodyRowsInitialized() {
|
|
946
|
+
if (bodyRowsInitialized)
|
|
947
|
+
return;
|
|
948
|
+
const optimisticHasSelectableRows = selectionMode === 'multiple' || selectedKeys.size > 0;
|
|
949
|
+
bodyRowsInitialized = true;
|
|
950
|
+
const actualHasSelectableRows = getOrderedSelectableRowIds().length > 0 || selectedKeys.size > 0;
|
|
951
|
+
if (optimisticHasSelectableRows !== actualHasSelectableRows) {
|
|
952
|
+
notifySelection();
|
|
953
|
+
}
|
|
954
|
+
}
|
|
638
955
|
function getBodyRowCount() {
|
|
639
956
|
return getOrderedRowTokens('body').length;
|
|
640
957
|
}
|
|
@@ -719,6 +1036,9 @@ export function createTableContext(options = {}) {
|
|
|
719
1036
|
return 'some';
|
|
720
1037
|
}
|
|
721
1038
|
function hasSelectableRows() {
|
|
1039
|
+
if (!bodyRowsInitialized) {
|
|
1040
|
+
return selectionMode === 'multiple' || selectedKeys.size > 0;
|
|
1041
|
+
}
|
|
722
1042
|
return getOrderedSelectableRowIds().length > 0 || selectedKeys.size > 0;
|
|
723
1043
|
}
|
|
724
1044
|
function isRowFocused(token) {
|
|
@@ -1465,12 +1785,16 @@ export function createTableContext(options = {}) {
|
|
|
1465
1785
|
getVisibleColumnIndexByToken,
|
|
1466
1786
|
getColumnTextValue,
|
|
1467
1787
|
getColumnWidth,
|
|
1788
|
+
getColumnWidthStyle,
|
|
1789
|
+
hasAuthoredColumnWidthSpec,
|
|
1468
1790
|
getColumnMinWidth,
|
|
1469
1791
|
getColumnMaxWidth,
|
|
1470
1792
|
isColumnHidden,
|
|
1471
1793
|
isColumnResizable,
|
|
1472
1794
|
getColumnWidths,
|
|
1473
1795
|
getVisibleColumnWidths,
|
|
1796
|
+
getResolvedVisibleColumnWidths,
|
|
1797
|
+
hasRelativeVisibleColumnWidths,
|
|
1474
1798
|
setColumnWidths,
|
|
1475
1799
|
setColumnWidth,
|
|
1476
1800
|
setHiddenColumns,
|
|
@@ -1482,6 +1806,7 @@ export function createTableContext(options = {}) {
|
|
|
1482
1806
|
hasResizableColumns,
|
|
1483
1807
|
registerRow,
|
|
1484
1808
|
unregisterRow,
|
|
1809
|
+
markBodyRowsInitialized,
|
|
1485
1810
|
getHeaderRowCount,
|
|
1486
1811
|
getBodyRowCount,
|
|
1487
1812
|
isRowSelected,
|