@x33025/sveltely 0.1.18 → 0.1.21

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.
Files changed (138) hide show
  1. package/dist/actions/LoaderOverlay.svelte +33 -8
  2. package/dist/actions/LoaderOverlay.svelte.d.ts +3 -0
  3. package/dist/actions/loader.d.ts +3 -0
  4. package/dist/actions/loader.js +20 -7
  5. package/dist/components/Library/AnimatedNumber/AnimatedNumber.demo.svelte +3 -9
  6. package/dist/components/Library/ArticleEditor/ArticleEditor.svelte +1 -1
  7. package/dist/components/Library/ArticleEditor/ArticleEditorHeader.svelte +20 -30
  8. package/dist/components/Library/ArticleEditor/Blocks/Code.svelte +0 -1
  9. package/dist/components/Library/ArticleEditor/Blocks/FAQ.svelte +1 -1
  10. package/dist/components/Library/ArticleEditor/Blocks/Heading.svelte +7 -7
  11. package/dist/components/Library/ArticleEditor/Blocks/Image.svelte +20 -36
  12. package/dist/components/Library/ArticleEditor/Blocks/Image.svelte.d.ts +1 -0
  13. package/dist/components/Library/ArticleEditor/Blocks/List.svelte +2 -2
  14. package/dist/components/Library/ArticleEditor/Blocks/Paragraph.svelte +1 -1
  15. package/dist/components/Library/ArticleEditor/Blocks/index.d.ts +0 -1
  16. package/dist/components/Library/ArticleEditor/Blocks/index.js +0 -1
  17. package/dist/components/Library/AsyncButton/AsyncButton.demo.svelte +2 -6
  18. package/dist/components/Library/AsyncButton/AsyncButton.svelte +9 -5
  19. package/dist/components/Library/AsyncButton/AsyncButton.svelte.d.ts +2 -1
  20. package/dist/components/Library/Button/Button.demo.svelte +2 -17
  21. package/dist/components/Library/Button/Button.demo.svelte.d.ts +0 -1
  22. package/dist/components/Library/Button/Button.svelte +15 -16
  23. package/dist/components/Library/Button/Button.svelte.d.ts +2 -1
  24. package/dist/components/Library/Calendar/Calendar.svelte +17 -27
  25. package/dist/components/Library/Checkbox/Checkbox.demo.svelte +7 -4
  26. package/dist/components/Library/Checkbox/Checkbox.svelte +24 -61
  27. package/dist/components/Library/Checkbox/Checkbox.svelte.d.ts +2 -4
  28. package/dist/components/Library/ChipInput/ChipInput.demo.svelte +2 -2
  29. package/dist/components/Library/ChipInput/ChipInput.svelte +7 -11
  30. package/dist/components/Library/ChipInput/ChipInput.svelte.d.ts +3 -2
  31. package/dist/components/Library/Dropdown/Action.svelte +1 -1
  32. package/dist/components/Library/Dropdown/Dropdown.demo.svelte +10 -10
  33. package/dist/components/Library/Dropdown/Dropdown.svelte +2 -6
  34. package/dist/components/Library/Dropdown/Item.svelte +2 -2
  35. package/dist/components/Library/Dropdown/Section.svelte +1 -1
  36. package/dist/components/Library/Dropdown/Trigger.svelte +3 -7
  37. package/dist/components/Library/Image/Image.demo.svelte +3 -3
  38. package/dist/components/Library/Image/Image.svelte +57 -12
  39. package/dist/components/Library/Image/Image.svelte.d.ts +1 -2
  40. package/dist/components/Library/Image/ImagePlaceholder.demo.svelte +12 -0
  41. package/dist/components/Library/Image/ImagePlaceholder.demo.svelte.d.ts +23 -0
  42. package/dist/components/Library/Image/ImagePlaceholder.svelte +28 -4
  43. package/dist/components/Library/Image/ImagePlaceholder.svelte.d.ts +1 -1
  44. package/dist/components/Library/Image/index.d.ts +1 -0
  45. package/dist/components/Library/Image/index.js +1 -0
  46. package/dist/components/Library/ImageMask/BrushPreview.svelte +6 -6
  47. package/dist/components/Library/ImageMask/ImageMask.demo.svelte +10 -8
  48. package/dist/components/Library/ImageMask/ImageMask.svelte +14 -6
  49. package/dist/components/Library/ImageMask/ImageMask.svelte.d.ts +1 -2
  50. package/dist/components/Library/ImageMask/MaskLayer.svelte +12 -6
  51. package/dist/components/Library/Label/Label.demo.svelte +16 -3
  52. package/dist/components/Library/Label/Label.svelte +15 -3
  53. package/dist/components/Library/Label/Label.svelte.d.ts +1 -0
  54. package/dist/components/Library/Link/Link.svelte +10 -22
  55. package/dist/components/Library/Link/Link.svelte.d.ts +2 -3
  56. package/dist/components/Library/Loader/Loader.demo.svelte +9 -3
  57. package/dist/components/Library/NavigationStack/Link.svelte +8 -12
  58. package/dist/components/Library/NavigationStack/Link.svelte.d.ts +1 -3
  59. package/dist/components/Library/NavigationStack/SidebarToggle.svelte +8 -2
  60. package/dist/components/Library/NumberField/NumberField.svelte +21 -17
  61. package/dist/components/Library/NumberField/NumberField.svelte.d.ts +4 -2
  62. package/dist/components/Library/Pagination/Pagination.svelte +3 -3
  63. package/dist/components/Library/Popover/Popover.svelte +2 -7
  64. package/dist/components/Library/ScrollView/ScrollView.demo.svelte +50 -0
  65. package/dist/components/Library/ScrollView/ScrollView.demo.svelte.d.ts +10 -0
  66. package/dist/components/Library/ScrollView/ScrollView.svelte +414 -67
  67. package/dist/components/Library/ScrollView/ScrollView.svelte.d.ts +17 -1
  68. package/dist/components/Library/ScrollView/index.d.ts +1 -1
  69. package/dist/components/Library/SearchField/SearchField.demo.svelte +2 -2
  70. package/dist/components/Library/SearchField/SearchField.svelte +9 -4
  71. package/dist/components/Library/SearchField/SearchField.svelte.d.ts +2 -1
  72. package/dist/components/Library/SegmentedPicker/SegmentedPicker.demo.svelte +2 -2
  73. package/dist/components/Library/SegmentedPicker/SegmentedPicker.svelte +7 -7
  74. package/dist/components/Library/Sheet/Sheet.demo.svelte +1 -1
  75. package/dist/components/Library/Sheet/Sheet.svelte +2 -7
  76. package/dist/components/Library/Slider/Slider.demo.svelte +1 -1
  77. package/dist/components/Library/Slider/Slider.svelte +11 -7
  78. package/dist/components/Library/Slider/Slider.svelte.d.ts +2 -1
  79. package/dist/components/Library/Spinner/Spinner.demo.svelte +1 -1
  80. package/dist/components/Library/Switch/Switch.demo.svelte +7 -4
  81. package/dist/components/Library/Switch/Switch.svelte +28 -68
  82. package/dist/components/Library/Switch/Switch.svelte.d.ts +2 -4
  83. package/dist/components/Library/Table/Column.svelte +81 -0
  84. package/dist/components/Library/Table/Column.svelte.d.ts +39 -0
  85. package/dist/components/Library/Table/Table.demo.svelte +148 -0
  86. package/dist/components/Library/Table/Table.demo.svelte.d.ts +10 -0
  87. package/dist/components/Library/Table/Table.svelte +624 -0
  88. package/dist/components/Library/Table/Table.svelte.d.ts +42 -0
  89. package/dist/components/Library/Table/context.d.ts +5 -0
  90. package/dist/components/Library/Table/context.js +2 -0
  91. package/dist/components/Library/Table/index.js +5 -0
  92. package/dist/components/Library/Table/types.d.ts +37 -0
  93. package/dist/components/Library/Table/types.js +1 -0
  94. package/dist/components/Library/Text/Text.demo.svelte +21 -0
  95. package/dist/components/Library/Text/Text.demo.svelte.d.ts +24 -0
  96. package/dist/components/Library/Text/Text.svelte +41 -0
  97. package/dist/components/Library/Text/Text.svelte.d.ts +9 -0
  98. package/dist/components/Library/Text/index.d.ts +1 -0
  99. package/dist/components/Library/Text/index.js +1 -0
  100. package/dist/components/Library/TextEditor/TextEditor.svelte +15 -9
  101. package/dist/components/Library/TextEditor/TextEditor.svelte.d.ts +2 -4
  102. package/dist/components/Library/TextField/TextField.demo.svelte +1 -1
  103. package/dist/components/Library/TextField/TextField.svelte +21 -18
  104. package/dist/components/Library/TextField/TextField.svelte.d.ts +4 -2
  105. package/dist/components/Library/TextShimmer/TextShimmer.demo.svelte +1 -1
  106. package/dist/components/Library/TimePicker/TimePicker.demo.svelte +10 -2
  107. package/dist/components/Library/TimePicker/TimePicker.svelte +10 -5
  108. package/dist/components/Library/TokenSearchField/TokenSearchField.demo.svelte +4 -2
  109. package/dist/components/Library/TokenSearchField/TokenSearchField.svelte +11 -11
  110. package/dist/components/Library/TokenSearchField/TokenSearchField.svelte.d.ts +2 -1
  111. package/dist/components/Library/WheelPicker/WheelColumn.svelte +183 -126
  112. package/dist/components/Library/WheelPicker/WheelPicker.svelte +4 -4
  113. package/dist/components/Library/WheelPicker/WheelPicker.svelte.d.ts +2 -2
  114. package/dist/components/Library/WheelPicker/index.d.ts +1 -1
  115. package/dist/components/Library/WheelPicker/types.d.ts +6 -0
  116. package/dist/components/Local/ColorStyleControls.svelte +201 -0
  117. package/dist/components/Local/ColorStyleControls.svelte.d.ts +13 -0
  118. package/dist/components/Local/HeroCard.svelte +3 -3
  119. package/dist/components/Local/LayoutStyleControls.svelte +67 -0
  120. package/dist/components/Local/LayoutStyleControls.svelte.d.ts +11 -0
  121. package/dist/components/Local/StyleControls.svelte +48 -124
  122. package/dist/components/Local/StyleControls.svelte.d.ts +7 -5
  123. package/dist/index.d.ts +9 -2
  124. package/dist/index.js +5 -1
  125. package/dist/style/index.css +7 -12
  126. package/dist/style/label.d.ts +2 -1
  127. package/dist/style/label.js +2 -1
  128. package/dist/style/surface.js +4 -0
  129. package/dist/style/text-editor.d.ts +2 -13
  130. package/dist/style/text-editor.js +1 -12
  131. package/dist/style/text.d.ts +26 -0
  132. package/dist/style/text.js +69 -0
  133. package/dist/style/tooltip.d.ts +4 -0
  134. package/dist/style/tooltip.js +1 -0
  135. package/dist/style.css +44 -111
  136. package/package.json +1 -1
  137. package/dist/components/Library/ArticleEditor/Blocks/ImagePreview.svelte +0 -71
  138. package/dist/components/Library/ArticleEditor/Blocks/ImagePreview.svelte.d.ts +0 -8
@@ -0,0 +1,624 @@
1
+ <script module lang="ts">
2
+ export { default as Column } from './Column.svelte';
3
+ </script>
4
+
5
+ <script lang="ts" generics="T extends Record<string, unknown>">
6
+ import { ArrowDownIcon, ArrowUpDownIcon, ArrowUpIcon } from '@lucide/svelte';
7
+ import { setContext, type Snippet } from 'svelte';
8
+ import { extractLoaderProps, resolveLoaderOptions, type LoaderProps } from '../../../style/loader';
9
+ import { extractLayoutProps, layoutStyle, type LayoutProps } from '../../../style/layout';
10
+ import { extractStyleProps, surfaceStyle, type StyleProps } from '../../../style/surface';
11
+ import ScrollView from '../ScrollView';
12
+ import { TABLE_CONTEXT } from './context';
13
+ import type {
14
+ TableColumn,
15
+ TableColumnCustomization,
16
+ TableColumnHeaderVisibility,
17
+ TableContext,
18
+ TableRowKey,
19
+ TableSelection,
20
+ TableSelectionMode,
21
+ TableSortDescriptor
22
+ } from './types';
23
+
24
+ type Props = {
25
+ data: T[];
26
+ children?: Snippet;
27
+ columns?: TableColumn<T>[];
28
+ selection?: TableSelection;
29
+ selectionMode?: TableSelectionMode;
30
+ sortOrder?: TableSortDescriptor<T>[];
31
+ columnHeaders?: TableColumnHeaderVisibility;
32
+ columnCustomization?: TableColumnCustomization;
33
+ emptyLabel?: string;
34
+ onSortOrderChange?: (sortOrder: TableSortDescriptor<T>[]) => void;
35
+ onSelectionChange?: (selection: TableSelection) => void;
36
+ } & LayoutProps &
37
+ StyleProps &
38
+ LoaderProps &
39
+ Record<string, unknown>;
40
+
41
+ let {
42
+ data,
43
+ children,
44
+ columns = [],
45
+ selection = $bindable<TableSelection>(null),
46
+ selectionMode = 'none',
47
+ sortOrder = $bindable<TableSortDescriptor<T>[]>([]),
48
+ columnHeaders = 'visible',
49
+ columnCustomization = $bindable<TableColumnCustomization>({}),
50
+ emptyLabel = 'No rows',
51
+ onSortOrderChange,
52
+ onSelectionChange,
53
+ ...restProps
54
+ }: Props = $props();
55
+
56
+ let tableSortOrder = $state<TableSortDescriptor<T>[]>(sortOrder);
57
+ let dragSelection = $state<{
58
+ active: boolean;
59
+ pointerId: number;
60
+ shouldSelect: boolean;
61
+ keys: Set<TableRowKey>;
62
+ visited: Set<TableRowKey>;
63
+ lastKey: TableRowKey;
64
+ } | null>(null);
65
+ let nextSyntheticRowKey = 0;
66
+ const syntheticRowKeys = new WeakMap<T, TableRowKey>();
67
+
68
+ const extractedLoaderProps = $derived.by(() => extractLoaderProps(restProps));
69
+ const loaderProps = $derived(extractedLoaderProps.loaderProps);
70
+ const afterLoaderProps = $derived(extractedLoaderProps.restProps);
71
+ const providedLoaderOptions = $derived.by(() => resolveLoaderOptions(loaderProps));
72
+ const bodyLoaderOptions = $derived.by(() => {
73
+ if (loaderProps.loader !== undefined) return providedLoaderOptions;
74
+ return {
75
+ text: '',
76
+ loading: data.length === 0
77
+ };
78
+ });
79
+ const extractedLayoutProps = $derived.by(() => extractLayoutProps(afterLoaderProps));
80
+ const layoutProps = $derived(extractedLayoutProps.layoutProps);
81
+ const afterLayoutProps = $derived(extractedLayoutProps.restProps);
82
+ const extractedStyleProps = $derived.by(() => extractStyleProps(afterLayoutProps));
83
+ const styleProps = $derived(extractedStyleProps.styleProps);
84
+ const forwardedProps = $derived(extractedStyleProps.restProps);
85
+ const rootStyle = $derived.by(() => surfaceStyle(styleProps, 'table'));
86
+
87
+ const toCssSize = (value: number | string | undefined) =>
88
+ typeof value === 'number' ? `${value}px` : value;
89
+
90
+ const tableShellStyle = $derived.by(() => {
91
+ const hasExplicitHeight = layoutProps.height !== undefined || layoutProps.size !== undefined;
92
+ const fallbackHeight = hasExplicitHeight ? undefined : (layoutProps.maxHeight ?? '100%');
93
+
94
+ return [
95
+ layoutStyle(layoutProps),
96
+ rootStyle,
97
+ layoutProps.height === undefined ? '' : `height: ${toCssSize(layoutProps.height)};`,
98
+ layoutProps.maxHeight === undefined ? '' : `max-height: ${toCssSize(layoutProps.maxHeight)};`,
99
+ fallbackHeight === undefined ? '' : `height: ${toCssSize(fallbackHeight)};`
100
+ ]
101
+ .filter(Boolean)
102
+ .join(' ');
103
+ });
104
+
105
+ const headerSpan = (value: TableColumn<T>['colspan'] | TableColumn<T>['rowspan']) =>
106
+ typeof value === 'number' ? value : undefined;
107
+
108
+ const bodySpan = (
109
+ value: TableColumn<T>['colspan'] | TableColumn<T>['rowspan'],
110
+ row: T,
111
+ index: number
112
+ ) => (typeof value === 'function' ? value(row, index) : value);
113
+
114
+ let registeredColumns = $state<Array<{ id: symbol; column: TableColumn<T> }>>([]);
115
+
116
+ const registerColumn: TableContext<T>['registerColumn'] = (column) => {
117
+ const id = Symbol(column.label);
118
+ registeredColumns = [...registeredColumns, { id, column }];
119
+ return id;
120
+ };
121
+
122
+ const updateColumn: TableContext<T>['updateColumn'] = (id, column) => {
123
+ registeredColumns = registeredColumns.map((registered) =>
124
+ registered.id === id ? { id, column } : registered
125
+ );
126
+ };
127
+
128
+ const unregisterColumn: TableContext<T>['unregisterColumn'] = (id) => {
129
+ registeredColumns = registeredColumns.filter((registered) => registered.id !== id);
130
+ };
131
+
132
+ setContext<TableContext<T>>(TABLE_CONTEXT, {
133
+ registerColumn,
134
+ updateColumn,
135
+ unregisterColumn
136
+ });
137
+
138
+ const columnKey = (column: TableColumn<T>, index = 0) => {
139
+ if (column.key) return column.key;
140
+ if (typeof column.value === 'string') return column.value;
141
+ if (typeof column.sortKey === 'string') return column.sortKey;
142
+ return `${column.label}-${index}`;
143
+ };
144
+
145
+ const columnSortKey = (column: TableColumn<T>, index = 0) =>
146
+ String(column.sortKey ?? columnKey(column, index));
147
+
148
+ const stableRowKey = (row: T) => {
149
+ const implicitId = row.id;
150
+ if (typeof implicitId === 'string' || typeof implicitId === 'number') return implicitId;
151
+
152
+ const implicitKey = row.key;
153
+ if (typeof implicitKey === 'string' || typeof implicitKey === 'number') return implicitKey;
154
+
155
+ const syntheticKey = syntheticRowKeys.get(row);
156
+ if (syntheticKey !== undefined) return syntheticKey;
157
+
158
+ const nextKey = `row-${nextSyntheticRowKey++}`;
159
+ syntheticRowKeys.set(row, nextKey);
160
+ return nextKey;
161
+ };
162
+
163
+ const rowKey = (row: T, _index: number): TableRowKey => stableRowKey(row);
164
+
165
+ const columnValue = (column: TableColumn<T>, row: T) => {
166
+ if (typeof column.value === 'function') return column.value(row);
167
+ if (column.value !== undefined) return row[column.value];
168
+ if (typeof column.sortKey === 'string') return row[column.sortKey];
169
+ const key = column.key;
170
+ return key === undefined ? undefined : row[key];
171
+ };
172
+
173
+ const displayValue = (value: unknown) => {
174
+ if (value === null || value === undefined) return '';
175
+ if (value instanceof Date) return value.toLocaleDateString();
176
+ return String(value);
177
+ };
178
+
179
+ const fallbackCompare = (column: TableColumn<T>, a: T, b: T) => {
180
+ const first = columnValue(column, a);
181
+ const second = columnValue(column, b);
182
+
183
+ if (typeof first === 'number' && typeof second === 'number') {
184
+ return first - second;
185
+ }
186
+
187
+ if (first instanceof Date && second instanceof Date) {
188
+ return first.getTime() - second.getTime();
189
+ }
190
+
191
+ return displayValue(first).localeCompare(displayValue(second), undefined, {
192
+ numeric: true,
193
+ sensitivity: 'base'
194
+ });
195
+ };
196
+
197
+ const visibleColumns = $derived.by(() => {
198
+ const hidden = new Set(columnCustomization.hidden ?? []);
199
+ const availableColumns = [
200
+ ...columns,
201
+ ...registeredColumns.map((registered) => registered.column)
202
+ ];
203
+ const byKey = new Map(
204
+ availableColumns.map((column, index) => [columnKey(column, index), column])
205
+ );
206
+ const ordered = [
207
+ ...(columnCustomization.order ?? []).map((key) => byKey.get(key)).filter(Boolean),
208
+ ...availableColumns.filter(
209
+ (column, index) => !(columnCustomization.order ?? []).includes(columnKey(column, index))
210
+ )
211
+ ] as TableColumn<T>[];
212
+
213
+ return ordered.filter(
214
+ (column, index) => !column.hidden && !hidden.has(columnKey(column, index))
215
+ );
216
+ });
217
+
218
+ const sortedRows = $derived.by(() => {
219
+ if (tableSortOrder.length === 0) return data;
220
+
221
+ const availableColumns = [
222
+ ...columns,
223
+ ...registeredColumns.map((registered) => registered.column)
224
+ ];
225
+ const columnByKey = new Map<string, TableColumn<T>>();
226
+ availableColumns.forEach((column, index) => {
227
+ columnByKey.set(columnKey(column, index), column);
228
+ columnByKey.set(columnSortKey(column, index), column);
229
+ });
230
+
231
+ return [...data].sort((a, b) => {
232
+ for (const descriptor of tableSortOrder) {
233
+ const column = columnByKey.get(descriptor.key);
234
+ if (!column) continue;
235
+ const compare =
236
+ descriptor.compare ?? column.compare ?? ((x: T, y: T) => fallbackCompare(column, x, y));
237
+ const result = compare(a, b);
238
+ if (result !== 0) {
239
+ return descriptor.direction === 'ascending' ? result : result * -1;
240
+ }
241
+ }
242
+
243
+ return 0;
244
+ });
245
+ });
246
+
247
+ const selectedKeys = $derived.by(() => {
248
+ if (selection instanceof Set) return new Set<TableRowKey>(selection);
249
+ if (Array.isArray(selection)) return new Set<TableRowKey>(selection);
250
+ return selection === null ? new Set<TableRowKey>() : new Set<TableRowKey>([selection]);
251
+ });
252
+
253
+ const isSelected = (key: TableRowKey) => selectedKeys.has(key);
254
+
255
+ const selectionFromKeys = (keys: Set<TableRowKey>): TableSelection => {
256
+ if (Array.isArray(selection)) return [...keys];
257
+ if (selection instanceof Set || selectionMode === 'multiple') return new Set(keys);
258
+ const [first] = keys;
259
+ return first ?? null;
260
+ };
261
+
262
+ const publishSelection = (keys: Set<TableRowKey>) => {
263
+ const next = selectionFromKeys(keys);
264
+ selection = next;
265
+ onSelectionChange?.(next);
266
+ };
267
+
268
+ const activeSortDirection = (column: TableColumn<T>, index: number) =>
269
+ tableSortOrder.find((descriptor) => descriptor.key === columnSortKey(column, index))?.direction;
270
+
271
+ const updateSort = (column: TableColumn<T>, index = 0) => {
272
+ if (!column.sortable) return;
273
+
274
+ const key = columnSortKey(column, index);
275
+ const active = tableSortOrder.find((descriptor) => descriptor.key === key);
276
+ const next: TableSortDescriptor<T>[] =
277
+ active?.direction === 'ascending'
278
+ ? [{ key, direction: 'descending', compare: column.compare }]
279
+ : [{ key, direction: 'ascending', compare: column.compare }];
280
+
281
+ tableSortOrder = next;
282
+ sortOrder = next;
283
+ onSortOrderChange?.(next);
284
+ };
285
+
286
+ const updateSelection = (key: TableRowKey) => {
287
+ if (selectionMode === 'none') return;
288
+
289
+ let next: TableSelection;
290
+
291
+ if (selectionMode === 'single') {
292
+ next = isSelected(key) ? null : key;
293
+ } else {
294
+ const keys = new Set(selectedKeys);
295
+ if (keys.has(key)) {
296
+ keys.delete(key);
297
+ } else {
298
+ keys.add(key);
299
+ }
300
+ next = Array.isArray(selection) ? [...keys] : keys;
301
+ }
302
+
303
+ selection = next;
304
+ onSelectionChange?.(next);
305
+ };
306
+
307
+ const isInteractiveTarget = (target: EventTarget | null) =>
308
+ target instanceof Element &&
309
+ Boolean(target.closest('button, a, input, textarea, select, [contenteditable="true"]'));
310
+
311
+ const applyDragSelection = (key: TableRowKey) => {
312
+ if (!dragSelection || dragSelection.visited.has(key)) return;
313
+
314
+ const nextKeys = new Set(dragSelection.keys);
315
+ if (dragSelection.shouldSelect) {
316
+ nextKeys.add(key);
317
+ } else {
318
+ nextKeys.delete(key);
319
+ }
320
+
321
+ dragSelection = {
322
+ ...dragSelection,
323
+ keys: nextKeys,
324
+ visited: new Set([...dragSelection.visited, key]),
325
+ lastKey: key
326
+ };
327
+ publishSelection(nextKeys);
328
+ };
329
+
330
+ const rowFromPoint = (event: PointerEvent) => {
331
+ const element = document.elementFromPoint(event.clientX, event.clientY);
332
+ const row = element?.closest<HTMLTableRowElement>('[data-table-row-key]');
333
+ return row?.dataset.tableRowKey;
334
+ };
335
+
336
+ const keyFromDomValue = (value: string | undefined) => {
337
+ if (value === undefined) return undefined;
338
+ for (const [index, row] of sortedRows.entries()) {
339
+ const key = rowKey(row, index);
340
+ if (String(key) === value) return key;
341
+ }
342
+ return undefined;
343
+ };
344
+
345
+ const applyDragSelectionAtPoint = (event: PointerEvent) => {
346
+ if (!dragSelection?.active || dragSelection.pointerId !== event.pointerId) return;
347
+ const key = keyFromDomValue(rowFromPoint(event));
348
+ if (key === undefined || key === dragSelection.lastKey) return;
349
+ applyDragSelection(key);
350
+ };
351
+
352
+ const startDragSelection = (event: PointerEvent, key: TableRowKey) => {
353
+ if (selectionMode !== 'multiple' || event.button !== 0 || isInteractiveTarget(event.target))
354
+ return;
355
+
356
+ const shouldSelect = !isSelected(key);
357
+ const nextKeys = new Set(selectedKeys);
358
+ if (shouldSelect) {
359
+ nextKeys.add(key);
360
+ } else {
361
+ nextKeys.delete(key);
362
+ }
363
+
364
+ event.preventDefault();
365
+ dragSelection = {
366
+ active: true,
367
+ pointerId: event.pointerId,
368
+ shouldSelect,
369
+ keys: nextKeys,
370
+ visited: new Set([key]),
371
+ lastKey: key
372
+ };
373
+ publishSelection(nextKeys);
374
+ };
375
+
376
+ const continueDragSelection = (event: PointerEvent, key: TableRowKey) => {
377
+ if (!dragSelection?.active || dragSelection.pointerId !== event.pointerId) return;
378
+ applyDragSelection(key);
379
+ };
380
+
381
+ const continueDragSelectionAtPoint = (event: PointerEvent) => {
382
+ applyDragSelectionAtPoint(event);
383
+ };
384
+
385
+ const endDragSelection = (event: PointerEvent) => {
386
+ if (dragSelection?.pointerId !== event.pointerId) return;
387
+ dragSelection = null;
388
+ };
389
+
390
+ const handleRowClick = (event: MouseEvent, key: TableRowKey) => {
391
+ if (selectionMode === 'multiple' || isInteractiveTarget(event.target)) return;
392
+ updateSelection(key);
393
+ };
394
+ </script>
395
+
396
+ {#if children}
397
+ {@render children()}
398
+ {/if}
399
+
400
+ <svelte:window
401
+ onpointermove={continueDragSelectionAtPoint}
402
+ onpointerup={endDragSelection}
403
+ onpointercancel={endDragSelection}
404
+ />
405
+
406
+ <div class="table-shell" style={tableShellStyle} {...forwardedProps}>
407
+ {#if columnHeaders === 'visible'}
408
+ <table
409
+ class="table-header-table table"
410
+ role={selectionMode === 'none' ? undefined : 'grid'}
411
+ aria-multiselectable={selectionMode === 'multiple' ? 'true' : undefined}
412
+ >
413
+ <thead class="table-header">
414
+ <tr>
415
+ {#each visibleColumns as column, index (columnKey(column, index))}
416
+ <th
417
+ class={`table-heading table-align-${column.alignment ?? 'leading'}`}
418
+ colspan={headerSpan(column.colspan)}
419
+ rowspan={headerSpan(column.rowspan)}
420
+ style:min-width={toCssSize(column.minWidth)}
421
+ style:width={toCssSize(column.width)}
422
+ aria-sort={tableSortOrder[0]?.key === columnSortKey(column, index)
423
+ ? tableSortOrder[0].direction === 'ascending'
424
+ ? 'ascending'
425
+ : 'descending'
426
+ : undefined}
427
+ >
428
+ {#if column.sortable}
429
+ {@const sortDirection = activeSortDirection(column, index)}
430
+ <button
431
+ type="button"
432
+ class="table-sort-button"
433
+ onclick={() => updateSort(column, index)}
434
+ >
435
+ <span>{column.label}</span>
436
+ {#if sortDirection === 'ascending'}
437
+ <ArrowUpIcon class="table-sort-icon" size={12} strokeWidth={2.25} />
438
+ {:else if sortDirection === 'descending'}
439
+ <ArrowDownIcon class="table-sort-icon" size={12} strokeWidth={2.25} />
440
+ {:else}
441
+ <ArrowUpDownIcon
442
+ class="table-sort-icon table-sort-icon-muted"
443
+ size={12}
444
+ strokeWidth={2.25}
445
+ />
446
+ {/if}
447
+ </button>
448
+ {:else}
449
+ {column.label}
450
+ {/if}
451
+ </th>
452
+ {/each}
453
+ </tr>
454
+ </thead>
455
+ </table>
456
+ {/if}
457
+
458
+ <ScrollView axis="both" loader={bodyLoaderOptions} contentStyles={{ paddingX: 0, paddingY: 0 }}>
459
+ <table
460
+ class="table-body-table table"
461
+ role={selectionMode === 'none' ? undefined : 'grid'}
462
+ aria-multiselectable={selectionMode === 'multiple' ? 'true' : undefined}
463
+ >
464
+ <tbody>
465
+ {#each sortedRows as row, index (rowKey(row, index))}
466
+ {@const key = rowKey(row, index)}
467
+ <tr
468
+ data-table-row-key={String(key)}
469
+ class="table-row"
470
+ class:table-row-selectable={selectionMode !== 'none'}
471
+ class:table-row-selected={isSelected(key)}
472
+ aria-selected={selectionMode === 'none' ? undefined : isSelected(key)}
473
+ onclick={(event) => handleRowClick(event, key)}
474
+ onpointerdown={(event) => startDragSelection(event, key)}
475
+ onpointerenter={(event) => continueDragSelection(event, key)}
476
+ onpointerup={endDragSelection}
477
+ onpointercancel={endDragSelection}
478
+ >
479
+ {#each visibleColumns as column, columnIndex (columnKey(column, columnIndex))}
480
+ <td
481
+ class={`table-cell table-align-${column.alignment ?? 'leading'}`}
482
+ colspan={bodySpan(column.colspan, row, index)}
483
+ rowspan={bodySpan(column.rowspan, row, index)}
484
+ style:min-width={toCssSize(column.minWidth)}
485
+ style:width={toCssSize(column.width)}
486
+ >
487
+ {#if column.cell}
488
+ {@render column.cell(row, index)}
489
+ {:else}
490
+ {displayValue(columnValue(column, row))}
491
+ {/if}
492
+ </td>
493
+ {/each}
494
+ </tr>
495
+ {:else}
496
+ <tr>
497
+ <td class="table-empty" colspan={Math.max(visibleColumns.length, 1)}>{emptyLabel}</td>
498
+ </tr>
499
+ {/each}
500
+ </tbody>
501
+ </table>
502
+ </ScrollView>
503
+ </div>
504
+
505
+ <style>
506
+ .table-shell {
507
+ --table-font-size: calc(var(--sveltely-font-size) * 0.875);
508
+ --table-scale: calc(var(--table-font-size) / 0.875rem);
509
+ --table-padding-x: calc(var(--sveltely-padding-x) * 0.75 * var(--table-scale));
510
+ --table-padding-y: calc(var(--sveltely-padding-y) * 0.58 * var(--table-scale));
511
+ display: flex;
512
+ flex-direction: column;
513
+ flex: 1 1 auto;
514
+ align-self: stretch;
515
+ min-height: 0;
516
+ width: 100%;
517
+ overflow: hidden;
518
+ border: 1px solid var(--sveltely-border-color);
519
+ border-radius: var(--sveltely-border-radius);
520
+ background: var(--sveltely-background-color);
521
+ color: var(--sveltely-text-primary-color);
522
+ font-size: var(--table-font-size);
523
+ }
524
+
525
+ .table {
526
+ width: 100%;
527
+ min-width: max-content;
528
+ border-collapse: separate;
529
+ border-spacing: 0;
530
+ }
531
+
532
+ .table-header {
533
+ background: color-mix(
534
+ in oklab,
535
+ var(--sveltely-background-color) 92%,
536
+ var(--sveltely-control-active-color)
537
+ );
538
+ }
539
+
540
+ .table-header-table {
541
+ flex: 0 0 auto;
542
+ border-bottom: 1px solid var(--sveltely-border-color);
543
+ }
544
+
545
+ .table-header-table + :global(.scroll-view) {
546
+ border-top-left-radius: 0;
547
+ border-top-right-radius: 0;
548
+ }
549
+
550
+ .table-shell > :global(.scroll-view) {
551
+ flex: 1 1 auto;
552
+ min-height: 0;
553
+ }
554
+
555
+ .table-heading,
556
+ .table-cell {
557
+ border-bottom: 1px solid var(--sveltely-border-color);
558
+ padding: var(--table-padding-y) var(--table-padding-x);
559
+ vertical-align: middle;
560
+ white-space: nowrap;
561
+ }
562
+
563
+ .table-heading {
564
+ border-bottom: 0;
565
+ color: var(--sveltely-text-secondary-color);
566
+ font-weight: 600;
567
+ text-align: left;
568
+ }
569
+
570
+ .table-cell {
571
+ color: inherit;
572
+ }
573
+
574
+ .table-row:last-child .table-cell {
575
+ border-bottom: 0;
576
+ }
577
+
578
+ .table-row-selected {
579
+ background: color-mix(in oklab, var(--sveltely-control-active-color) 14%, transparent);
580
+ }
581
+
582
+ .table-sort-button {
583
+ display: inline-flex;
584
+ align-items: center;
585
+ justify-content: inherit;
586
+ width: 100%;
587
+ gap: calc(var(--sveltely-gap) * 0.5);
588
+ border-radius: var(--sveltely-border-radius-nested);
589
+ text-align: inherit;
590
+ }
591
+
592
+ .table-sort-button:hover {
593
+ color: var(--sveltely-text-primary-color);
594
+ }
595
+
596
+ .table-sort-icon {
597
+ width: 0.75rem;
598
+ height: 0.75rem;
599
+ flex: 0 0 auto;
600
+ stroke-width: 2.25;
601
+ }
602
+
603
+ .table-sort-icon-muted {
604
+ opacity: 0.45;
605
+ }
606
+
607
+ .table-align-leading {
608
+ text-align: left;
609
+ }
610
+
611
+ .table-align-center {
612
+ text-align: center;
613
+ }
614
+
615
+ .table-align-trailing {
616
+ text-align: right;
617
+ }
618
+
619
+ .table-empty {
620
+ padding: calc(var(--table-padding-y) * 2) var(--table-padding-x);
621
+ color: var(--sveltely-text-secondary-color);
622
+ text-align: center;
623
+ }
624
+ </style>
@@ -0,0 +1,42 @@
1
+ export { default as Column } from './Column.svelte';
2
+ import { type Snippet } from 'svelte';
3
+ import { type LoaderProps } from '../../../style/loader';
4
+ import { type LayoutProps } from '../../../style/layout';
5
+ import { type StyleProps } from '../../../style/surface';
6
+ import type { TableColumn, TableColumnCustomization, TableColumnHeaderVisibility, TableSelection, TableSelectionMode, TableSortDescriptor } from './types';
7
+ declare function $$render<T extends Record<string, unknown>>(): {
8
+ props: {
9
+ data: T[];
10
+ children?: Snippet;
11
+ columns?: TableColumn<T>[];
12
+ selection?: TableSelection;
13
+ selectionMode?: TableSelectionMode;
14
+ sortOrder?: TableSortDescriptor<T>[];
15
+ columnHeaders?: TableColumnHeaderVisibility;
16
+ columnCustomization?: TableColumnCustomization;
17
+ emptyLabel?: string;
18
+ onSortOrderChange?: (sortOrder: TableSortDescriptor<T>[]) => void;
19
+ onSelectionChange?: (selection: TableSelection) => void;
20
+ } & LayoutProps & StyleProps & LoaderProps & Record<string, unknown>;
21
+ exports: {};
22
+ bindings: "selection" | "sortOrder" | "columnCustomization";
23
+ slots: {};
24
+ events: {};
25
+ };
26
+ declare class __sveltets_Render<T extends Record<string, unknown>> {
27
+ props(): ReturnType<typeof $$render<T>>['props'];
28
+ events(): ReturnType<typeof $$render<T>>['events'];
29
+ slots(): ReturnType<typeof $$render<T>>['slots'];
30
+ bindings(): "selection" | "sortOrder" | "columnCustomization";
31
+ exports(): {};
32
+ }
33
+ interface $$IsomorphicComponent {
34
+ new <T extends Record<string, unknown>>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T>['props']>, ReturnType<__sveltets_Render<T>['events']>, ReturnType<__sveltets_Render<T>['slots']>> & {
35
+ $$bindings?: ReturnType<__sveltets_Render<T>['bindings']>;
36
+ } & ReturnType<__sveltets_Render<T>['exports']>;
37
+ <T extends Record<string, unknown>>(internal: unknown, props: ReturnType<__sveltets_Render<T>['props']> & {}): ReturnType<__sveltets_Render<T>['exports']>;
38
+ z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
39
+ }
40
+ declare const Table: $$IsomorphicComponent;
41
+ type Table<T extends Record<string, unknown>> = InstanceType<typeof Table<T>>;
42
+ export default Table;
@@ -0,0 +1,5 @@
1
+ import type { TableContext } from './types';
2
+ export declare const TABLE_CONTEXT: unique symbol;
3
+ export declare const tableContext: <T>() => symbol & {
4
+ __table?: TableContext<T>;
5
+ };
@@ -0,0 +1,2 @@
1
+ export const TABLE_CONTEXT = Symbol('sveltely-table');
2
+ export const tableContext = () => TABLE_CONTEXT;
@@ -0,0 +1,5 @@
1
+ import Root from './Table.svelte';
2
+ import Column from './Column.svelte';
3
+ const Table = Object.assign(Root, { Column });
4
+ export { Column };
5
+ export default Table;