@streamscloud/kit 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ui/badge/cmp.badge.svelte +4 -0
- package/dist/ui/badge/cmp.badge.svelte.d.ts +1 -0
- package/dist/ui/page-toolbar/cmp.reorderable-checkbox-list.svelte +131 -0
- package/dist/ui/page-toolbar/cmp.reorderable-checkbox-list.svelte.d.ts +31 -0
- package/dist/ui/page-toolbar/cmp.toolbar-checkbox.svelte +14 -0
- package/dist/ui/page-toolbar/cmp.toolbar-checkbox.svelte.d.ts +19 -0
- package/dist/ui/page-toolbar/cmp.toolbar-divider.svelte +19 -0
- package/dist/ui/page-toolbar/cmp.toolbar-divider.svelte.d.ts +35 -0
- package/dist/ui/page-toolbar/cmp.toolbar-filter-select.svelte +143 -0
- package/dist/ui/page-toolbar/cmp.toolbar-filter-select.svelte.d.ts +68 -0
- package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte +71 -0
- package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte.d.ts +32 -0
- package/dist/ui/page-toolbar/cmp.toolbar-option.svelte +189 -0
- package/dist/ui/page-toolbar/cmp.toolbar-option.svelte.d.ts +47 -0
- package/dist/ui/page-toolbar/cmp.toolbar-popover.svelte +23 -8
- package/dist/ui/page-toolbar/cmp.toolbar-popover.svelte.d.ts +14 -6
- package/dist/ui/page-toolbar/cmp.toolbar-section-title.svelte +46 -0
- package/dist/ui/page-toolbar/cmp.toolbar-section-title.svelte.d.ts +24 -0
- package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte +31 -0
- package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte.d.ts +44 -0
- package/dist/ui/page-toolbar/{cmp.toolbar-switch-item.svelte → cmp.toolbar-switch.svelte} +10 -10
- package/dist/ui/page-toolbar/{cmp.toolbar-switch-item.svelte.d.ts → cmp.toolbar-switch.svelte.d.ts} +2 -2
- package/dist/ui/page-toolbar/cmp.toolbar-toggle.svelte +14 -0
- package/dist/ui/page-toolbar/cmp.toolbar-toggle.svelte.d.ts +19 -0
- package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte +51 -0
- package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte.d.ts +27 -0
- package/dist/ui/page-toolbar/index.d.ts +12 -7
- package/dist/ui/page-toolbar/index.js +11 -6
- package/dist/ui/page-toolbar/reorderable-checkbox-list-localization.d.ts +5 -0
- package/dist/ui/page-toolbar/reorderable-checkbox-list-localization.js +17 -0
- package/dist/ui/page-toolbar/toolbar-filter-select-localization.d.ts +5 -0
- package/dist/ui/page-toolbar/toolbar-filter-select-localization.js +20 -0
- package/dist/ui/page-toolbar/toolbar-sort-select-localization.d.ts +3 -0
- package/dist/ui/page-toolbar/toolbar-sort-select-localization.js +9 -0
- package/dist/ui/page-toolbar/toolbar-view-controls-localization.d.ts +5 -0
- package/dist/ui/page-toolbar/toolbar-view-controls-localization.js +17 -0
- package/dist/ui/page-toolbar/types.d.ts +1 -1
- package/dist/ui/select/index.d.ts +1 -0
- package/dist/ui/select/index.js +1 -0
- package/dist/ui/select/select-listbox.svelte +11 -5
- package/dist/ui/select/select-listbox.svelte.d.ts +1 -1
- package/dist/ui/table/_table-styles.scss +156 -0
- package/dist/ui/table/cmp.table-draggable.svelte +338 -0
- package/dist/ui/table/cmp.table-draggable.svelte.d.ts +79 -0
- package/dist/ui/table/cmp.table.svelte +96 -265
- package/dist/ui/table/cmp.table.svelte.d.ts +16 -8
- package/dist/ui/table/index.d.ts +1 -0
- package/dist/ui/table/index.js +1 -0
- package/dist/ui/table/table-cells/index.d.ts +1 -12
- package/dist/ui/table/table-cells/index.js +1 -12
- package/dist/ui/table/table-cells/table-cell.svelte +77 -0
- package/dist/ui/table/table-cells/{table-select-cell.svelte.d.ts → table-cell.svelte.d.ts} +12 -6
- package/dist/ui/table/table-column-models.svelte.d.ts +1 -2
- package/dist/ui/table/table-column-models.svelte.js +0 -1
- package/dist/ui/table/table-columns-manager/cmp.table-columns-manager.svelte +20 -47
- package/dist/ui/table/table-columns-manager/cmp.table-columns-manager.svelte.d.ts +3 -7
- package/dist/ui/table/table-columns-manager/table-columns-manager-localization.d.ts +1 -1
- package/dist/ui/table/table-columns-manager/table-columns-manager-localization.js +7 -7
- package/dist/ui/table/table-localization.d.ts +0 -2
- package/dist/ui/table/table-localization.js +0 -14
- package/dist/ui/table/table-model.svelte.d.ts +2 -3
- package/dist/ui/table/table-model.svelte.js +5 -35
- package/dist/ui/table/table-view.svelte.d.ts +14 -0
- package/dist/ui/table/table-view.svelte.js +32 -0
- package/dist/ui/table/types.d.ts +5 -28
- package/dist/ui/toggle/cmp.toggle.svelte +32 -26
- package/dist/ui/toggle/cmp.toggle.svelte.d.ts +2 -0
- package/dist/ui/tree/cmp.tree-node-view.svelte +7 -2
- package/dist/ui/tree/cmp.tree.svelte +9 -1
- package/dist/ui/tree/cmp.tree.svelte.d.ts +1 -1
- package/dist/ui/tree/tree-navigation.svelte.d.ts +2 -0
- package/dist/ui/tree/tree-navigation.svelte.js +3 -0
- package/package.json +1 -1
- package/dist/ui/page-toolbar/cmp.reorderable-toggle-list.svelte +0 -153
- package/dist/ui/page-toolbar/cmp.reorderable-toggle-list.svelte.d.ts +0 -20
- package/dist/ui/page-toolbar/cmp.toolbar-checkbox-item.svelte +0 -19
- package/dist/ui/page-toolbar/cmp.toolbar-checkbox-item.svelte.d.ts +0 -12
- package/dist/ui/page-toolbar/cmp.toolbar-panel-button.svelte +0 -42
- package/dist/ui/page-toolbar/cmp.toolbar-panel-button.svelte.d.ts +0 -20
- package/dist/ui/page-toolbar/cmp.toolbar-panel.svelte +0 -57
- package/dist/ui/page-toolbar/cmp.toolbar-panel.svelte.d.ts +0 -19
- package/dist/ui/page-toolbar/cmp.toolbar-toggle-item.svelte +0 -16
- package/dist/ui/page-toolbar/cmp.toolbar-toggle-item.svelte.d.ts +0 -12
- package/dist/ui/table/table-cells/table-select-cell.svelte +0 -28
- package/dist/ui/table/table-cells/table-text-input-cell.svelte +0 -69
- package/dist/ui/table/table-cells/table-text-input-cell.svelte.d.ts +0 -38
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DynamicComponentModel } from '../dynamic-component';
|
|
2
2
|
import type { IOneOfTableColumn, ITableActionsColumn, TableColumnSortDirection, TableColumnType, WithId } from './types';
|
|
3
|
-
export type ColumnDef<T extends WithId> = IOneOfTableColumn<T
|
|
3
|
+
export type ColumnDef<T extends WithId> = IOneOfTableColumn<T> | ITableActionsColumn;
|
|
4
4
|
export type SortableTableColumn = {
|
|
5
5
|
uniqueId: symbol;
|
|
6
6
|
title: string;
|
|
@@ -17,7 +17,6 @@ export declare class ResolvedColumn<T extends WithId> {
|
|
|
17
17
|
readonly def: ColumnDef<T>;
|
|
18
18
|
readonly uniqueId: symbol;
|
|
19
19
|
headerView: DynamicComponentModel;
|
|
20
|
-
editorColumn: ResolvedColumn<T> | null;
|
|
21
20
|
hidden: boolean;
|
|
22
21
|
sortDirection: TableColumnSortDirection;
|
|
23
22
|
constructor(def: ColumnDef<T>);
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
<script lang="ts" generics="T extends {id: string}">import { LocalStorageItem } from '../../../core/local-storage';
|
|
2
|
-
import {
|
|
3
|
-
import { IconText } from '../../icon-text';
|
|
4
|
-
import { ReorderableToggleList } from '../../page-toolbar';
|
|
5
|
-
import { Popover } from '../../popover';
|
|
2
|
+
import { ReorderableCheckboxList, ToolbarPopover } from '../../page-toolbar';
|
|
6
3
|
import { TableColumnsManagerLocalization } from './table-columns-manager-localization';
|
|
7
|
-
import IconChevronDown from '@fluentui/svg-icons/icons/chevron_down_20_regular.svg?raw';
|
|
8
4
|
import IconColumnTwo from '@fluentui/svg-icons/icons/text_column_two_20_regular.svg?raw';
|
|
9
5
|
import { untrack } from 'svelte';
|
|
10
6
|
let { model, localStorageKey, on } = $props();
|
|
@@ -16,15 +12,20 @@ const items = $derived(hideableColumns.map((col) => ({
|
|
|
16
12
|
label: col.title,
|
|
17
13
|
enabled: !col.hidden
|
|
18
14
|
})));
|
|
19
|
-
// Load from localStorage once columns are available
|
|
20
15
|
let initialized = false;
|
|
16
|
+
let standardItems = $state.raw([]);
|
|
21
17
|
$effect(() => {
|
|
22
18
|
const columns = hideableColumns;
|
|
23
|
-
if (
|
|
19
|
+
if (columns.length === 0 || initialized) {
|
|
24
20
|
return;
|
|
25
21
|
}
|
|
26
22
|
initialized = true;
|
|
27
|
-
|
|
23
|
+
standardItems = columns.map((col) => ({
|
|
24
|
+
id: String(col.id),
|
|
25
|
+
label: col.title,
|
|
26
|
+
enabled: !('hidden' in col.def && col.def.hidden === true)
|
|
27
|
+
}));
|
|
28
|
+
const saved = storage?.get();
|
|
28
29
|
if (!saved || !Array.isArray(saved)) {
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
@@ -75,46 +76,18 @@ const handleChange = (updatedItems) => {
|
|
|
75
76
|
};
|
|
76
77
|
</script>
|
|
77
78
|
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
<div class="columns-manager__body">
|
|
87
|
-
<ReorderableToggleList items={items} on={{ change: handleChange }} />
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
</Popover>
|
|
79
|
+
<ToolbarPopover triggerLabel={localization.view} icon={{ src: IconColumnTwo, color: 'accent' }}>
|
|
80
|
+
<ReorderableCheckboxList
|
|
81
|
+
items={items}
|
|
82
|
+
title={localization.columns}
|
|
83
|
+
standard={standardItems.length ? standardItems : undefined}
|
|
84
|
+
showClearAll={false}
|
|
85
|
+
on={{ change: handleChange }} />
|
|
86
|
+
</ToolbarPopover>
|
|
91
87
|
|
|
92
88
|
<!--
|
|
93
89
|
@component
|
|
94
|
-
Column visibility and order manager for tables.
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
### CSS Custom Properties
|
|
98
|
-
| Property | Description | Default |
|
|
99
|
-
|---|---|---|
|
|
100
|
-
| `--sc-kit--table-columns-manager--title--color` | Title text color | `var(--sc-kit--color--text--primary)` |
|
|
90
|
+
Column visibility and order manager for tables. Opens a `ToolbarPopover` with a `ReorderableCheckboxList`
|
|
91
|
+
of the hideable columns — drag to reorder, click to show / hide. Supports optional localStorage persistence
|
|
92
|
+
of column order and visibility.
|
|
101
93
|
-->
|
|
102
|
-
|
|
103
|
-
<style>.columns-manager {
|
|
104
|
-
--_columns-manager--title--color: var(--sc-kit--table-columns-manager--title--color, var(--sc-kit--color--text--primary));
|
|
105
|
-
width: 21.875rem;
|
|
106
|
-
padding: 1.5rem;
|
|
107
|
-
}
|
|
108
|
-
.columns-manager__title {
|
|
109
|
-
font-size: 0.875rem;
|
|
110
|
-
font-weight: 600;
|
|
111
|
-
line-height: 1.25rem;
|
|
112
|
-
color: var(--_columns-manager--title--color);
|
|
113
|
-
margin-bottom: 0.5rem;
|
|
114
|
-
}
|
|
115
|
-
.columns-manager__body {
|
|
116
|
-
display: flex;
|
|
117
|
-
flex-direction: column;
|
|
118
|
-
gap: 1rem;
|
|
119
|
-
padding-block: 0.5rem;
|
|
120
|
-
}</style>
|
|
@@ -41,13 +41,9 @@ interface $$IsomorphicComponent {
|
|
|
41
41
|
z_$$bindings?: ReturnType<__sveltets_Render<any>['bindings']>;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* Column visibility and order manager for tables.
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* ### CSS Custom Properties
|
|
48
|
-
* | Property | Description | Default |
|
|
49
|
-
* |---|---|---|
|
|
50
|
-
* | `--sc-kit--table-columns-manager--title--color` | Title text color | `var(--sc-kit--color--text--primary)` |
|
|
44
|
+
* Column visibility and order manager for tables. Opens a `ToolbarPopover` with a `ReorderableCheckboxList`
|
|
45
|
+
* of the hideable columns — drag to reorder, click to show / hide. Supports optional localStorage persistence
|
|
46
|
+
* of column order and visibility.
|
|
51
47
|
*/
|
|
52
48
|
declare const Cmp: $$IsomorphicComponent;
|
|
53
49
|
type Cmp<T extends {
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import { AppLocale } from '../../../core/locale';
|
|
2
2
|
export class TableColumnsManagerLocalization {
|
|
3
|
+
get view() {
|
|
4
|
+
return loc.view[AppLocale.current];
|
|
5
|
+
}
|
|
3
6
|
get columns() {
|
|
4
7
|
return loc.columns[AppLocale.current];
|
|
5
8
|
}
|
|
6
|
-
get selectColumns() {
|
|
7
|
-
return loc.selectColumns[AppLocale.current];
|
|
8
|
-
}
|
|
9
9
|
}
|
|
10
10
|
const loc = {
|
|
11
|
+
view: {
|
|
12
|
+
en: 'View',
|
|
13
|
+
no: 'Visning'
|
|
14
|
+
},
|
|
11
15
|
columns: {
|
|
12
16
|
en: 'Columns',
|
|
13
17
|
no: 'Kolonner'
|
|
14
|
-
},
|
|
15
|
-
selectColumns: {
|
|
16
|
-
en: 'Select Columns',
|
|
17
|
-
no: 'Velg kolonner'
|
|
18
18
|
}
|
|
19
19
|
};
|
|
@@ -6,12 +6,6 @@ export class TableLocalization {
|
|
|
6
6
|
get noItemsFoundBody() {
|
|
7
7
|
return loc.noItemsFoundBody[AppLocale.current];
|
|
8
8
|
}
|
|
9
|
-
get save() {
|
|
10
|
-
return loc.save[AppLocale.current];
|
|
11
|
-
}
|
|
12
|
-
get cancel() {
|
|
13
|
-
return loc.cancel[AppLocale.current];
|
|
14
|
-
}
|
|
15
9
|
}
|
|
16
10
|
const loc = {
|
|
17
11
|
noItemsFoundTitle: {
|
|
@@ -21,13 +15,5 @@ const loc = {
|
|
|
21
15
|
noItemsFoundBody: {
|
|
22
16
|
en: 'No data found. Create one to get started',
|
|
23
17
|
no: 'Ingen data funnet. Opprett en for å komme i gang'
|
|
24
|
-
},
|
|
25
|
-
save: {
|
|
26
|
-
en: 'Save',
|
|
27
|
-
no: 'Lagre'
|
|
28
|
-
},
|
|
29
|
-
cancel: {
|
|
30
|
-
en: 'Cancel',
|
|
31
|
-
no: 'Avbryt'
|
|
32
18
|
}
|
|
33
19
|
};
|
|
@@ -20,7 +20,6 @@ export declare class TableModel<T extends WithId> {
|
|
|
20
20
|
private _selection;
|
|
21
21
|
private _preserveSelection;
|
|
22
22
|
private _dataProvider;
|
|
23
|
-
private _editingItem;
|
|
24
23
|
private _highlightedItemIds;
|
|
25
24
|
constructor(init: TableModelInit<T>);
|
|
26
25
|
get items(): T[];
|
|
@@ -29,13 +28,13 @@ export declare class TableModel<T extends WithId> {
|
|
|
29
28
|
get itemGroupActions(): TableGroupAction<T>[];
|
|
30
29
|
get styles(): Readonly<TableStyles>;
|
|
31
30
|
get selection(): Repository<T>;
|
|
32
|
-
get editingItem(): T | null;
|
|
33
31
|
get highlightedItemIds(): string[];
|
|
34
32
|
set items(value: T[]);
|
|
35
33
|
set itemActions(value: TableItemAction<T>[]);
|
|
36
34
|
set columns(value: IAnyTableColumn<T>[]);
|
|
37
|
-
set editingItem(value: T | null);
|
|
38
35
|
set highlightedItemIds(value: string[]);
|
|
36
|
+
/** Reassign items as a pure reorder of the same set — preserves the current selection (unlike the `items` setter). */
|
|
37
|
+
reorderItems: (value: T[]) => void;
|
|
39
38
|
updateStyles: (styles: Partial<TableStyles>) => void;
|
|
40
39
|
setResolvedColumns: (columns: ResolvedColumn<T>[]) => void;
|
|
41
40
|
private reconcileColumns;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { Css } from '../../core/css';
|
|
2
2
|
import { createRepository } from '../../core/repository';
|
|
3
|
-
import { Utils } from '../../core/utils';
|
|
4
3
|
import { DynamicComponentModel } from '../dynamic-component';
|
|
5
4
|
import { ResolvedColumn } from './table-column-models.svelte';
|
|
6
5
|
import { TableCheckboxHeader, TableHeader, TableHeaderSortable } from './table-headers';
|
|
@@ -15,7 +14,6 @@ export class TableModel {
|
|
|
15
14
|
_selection;
|
|
16
15
|
_preserveSelection = false;
|
|
17
16
|
_dataProvider;
|
|
18
|
-
_editingItem = $state(null);
|
|
19
17
|
_highlightedItemIds = $state.raw([]);
|
|
20
18
|
constructor(init) {
|
|
21
19
|
this._init = init;
|
|
@@ -64,9 +62,6 @@ export class TableModel {
|
|
|
64
62
|
get selection() {
|
|
65
63
|
return this._selection;
|
|
66
64
|
}
|
|
67
|
-
get editingItem() {
|
|
68
|
-
return this._editingItem;
|
|
69
|
-
}
|
|
70
65
|
get highlightedItemIds() {
|
|
71
66
|
return this._highlightedItemIds;
|
|
72
67
|
}
|
|
@@ -76,9 +71,6 @@ export class TableModel {
|
|
|
76
71
|
if (!this._preserveSelection) {
|
|
77
72
|
this._selection.clear();
|
|
78
73
|
}
|
|
79
|
-
if (this._editingItem && !this._items.find((x) => x.id === this._editingItem?.id)) {
|
|
80
|
-
this._editingItem = null;
|
|
81
|
-
}
|
|
82
74
|
}
|
|
83
75
|
set itemActions(value) {
|
|
84
76
|
this._itemActions = value;
|
|
@@ -86,17 +78,14 @@ export class TableModel {
|
|
|
86
78
|
set columns(value) {
|
|
87
79
|
this.reconcileColumns(value);
|
|
88
80
|
}
|
|
89
|
-
set editingItem(value) {
|
|
90
|
-
if (!this._items.find((x) => x.id === value?.id)) {
|
|
91
|
-
this._editingItem = null;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
this._editingItem = structuredClone($state.snapshot(value));
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
81
|
set highlightedItemIds(value) {
|
|
98
82
|
this._highlightedItemIds = value;
|
|
99
83
|
}
|
|
84
|
+
/** Reassign items as a pure reorder of the same set — preserves the current selection (unlike the `items` setter). */
|
|
85
|
+
reorderItems = (value) => {
|
|
86
|
+
this._items = value;
|
|
87
|
+
this._dataProvider.set(...value);
|
|
88
|
+
};
|
|
100
89
|
updateStyles = (styles) => {
|
|
101
90
|
Object.assign(this._styles, styles);
|
|
102
91
|
};
|
|
@@ -157,22 +146,6 @@ export class TableModel {
|
|
|
157
146
|
props: { column: resolved }
|
|
158
147
|
});
|
|
159
148
|
};
|
|
160
|
-
const resolveEditorColumn = (column) => {
|
|
161
|
-
switch (column.type) {
|
|
162
|
-
case 'text-input':
|
|
163
|
-
case 'select': {
|
|
164
|
-
const resolved = new ResolvedColumn(column);
|
|
165
|
-
resolved.headerView = new DynamicComponentModel({
|
|
166
|
-
component: TableHeader,
|
|
167
|
-
props: { column: resolved }
|
|
168
|
-
});
|
|
169
|
-
return resolved;
|
|
170
|
-
}
|
|
171
|
-
default: {
|
|
172
|
-
Utils.assertUnreachable(column);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
};
|
|
176
149
|
switch (column.type) {
|
|
177
150
|
case 'checkbox': {
|
|
178
151
|
const resolved = new ResolvedColumn({
|
|
@@ -198,9 +171,6 @@ export class TableModel {
|
|
|
198
171
|
case 'text': {
|
|
199
172
|
const resolved = new ResolvedColumn(column);
|
|
200
173
|
resolved.headerView = resolveSortableHeader(resolved);
|
|
201
|
-
if (column.editorColumn) {
|
|
202
|
-
resolved.editorColumn = resolveEditorColumn(column.editorColumn);
|
|
203
|
-
}
|
|
204
174
|
return resolved;
|
|
205
175
|
}
|
|
206
176
|
default: {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ResolvedColumn } from './table-column-models.svelte';
|
|
2
|
+
import type { TableModel } from './table-model.svelte';
|
|
3
|
+
import type { WithId } from './types';
|
|
4
|
+
type SelectionCallbacks<T> = {
|
|
5
|
+
selectionChanged?: (e: T[]) => void;
|
|
6
|
+
itemsSelected?: (e: T[]) => void;
|
|
7
|
+
itemsDeselected?: (e: T[]) => void;
|
|
8
|
+
};
|
|
9
|
+
export declare const cssWidth: <T extends WithId>(column: ResolvedColumn<T>) => string;
|
|
10
|
+
/** Shared presentation wiring for the table views: selection-event subscriptions + the debounced no-items placeholder. */
|
|
11
|
+
export declare const createTableView: <T extends WithId>(model: () => TableModel<T>, on: () => SelectionCallbacks<T> | undefined) => {
|
|
12
|
+
readonly showNoItemsPlaceholder: boolean;
|
|
13
|
+
};
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { untrack } from 'svelte';
|
|
2
|
+
export const cssWidth = (column) => (column.fixedCssWidth ? `width: ${column.fixedCssWidth};` : 'width: auto;');
|
|
3
|
+
/** Shared presentation wiring for the table views: selection-event subscriptions + the debounced no-items placeholder. */
|
|
4
|
+
export const createTableView = (model, on) => {
|
|
5
|
+
let showNoItemsPlaceholder = $state(false);
|
|
6
|
+
let timeout = 0;
|
|
7
|
+
$effect(() => untrack(() => {
|
|
8
|
+
const selection = model().selection;
|
|
9
|
+
const subscriptions = [
|
|
10
|
+
selection.subscribeOnChange((values) => on()?.selectionChanged?.(values)),
|
|
11
|
+
selection.subscribeOnAdd((values) => on()?.itemsSelected?.(values)),
|
|
12
|
+
selection.subscribeOnRemove((values) => on()?.itemsDeselected?.(values))
|
|
13
|
+
];
|
|
14
|
+
return () => subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
15
|
+
}));
|
|
16
|
+
$effect(() => {
|
|
17
|
+
if (model().items.length) {
|
|
18
|
+
clearTimeout(timeout);
|
|
19
|
+
showNoItemsPlaceholder = false;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
timeout = window.setTimeout(() => {
|
|
23
|
+
showNoItemsPlaceholder = !model().items.length;
|
|
24
|
+
}, 700);
|
|
25
|
+
return () => clearTimeout(timeout);
|
|
26
|
+
});
|
|
27
|
+
return {
|
|
28
|
+
get showNoItemsPlaceholder() {
|
|
29
|
+
return showNoItemsPlaceholder;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
package/dist/ui/table/types.d.ts
CHANGED
|
@@ -3,18 +3,13 @@ import type { BadgeVariant } from '../badge';
|
|
|
3
3
|
import type { ButtonSize, ButtonVariant } from '../button';
|
|
4
4
|
import { DynamicComponentModel } from '../dynamic-component';
|
|
5
5
|
import type { IconProp } from '../icon';
|
|
6
|
-
import type { SelectOption } from '../select';
|
|
7
6
|
import type { Snippet } from 'svelte';
|
|
8
|
-
import type { ISchema } from 'yup';
|
|
9
7
|
export type WithId = {
|
|
10
8
|
id: string;
|
|
11
9
|
};
|
|
12
|
-
export type IAnyTableColumn<T> =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export type IOneOfTableColumn<T, K extends keyof T> = ITableBadgeColumn<T> | ITableButtonColumn<T> | ITableByColumn<T> | ITableCheckboxColumn | ITableCustomColumn<T> | ITableIconColumn<T> | ITableImageColumn<T> | ITableMoveRowColumn | ITableSelectColumn<T, K> | ITableSnippetColumn<T> | ITableTextColumn<T> | ITableTextInputColumn<T>;
|
|
16
|
-
export type IEditorColumn<T> = ITableTextInputColumn<T> | ITableSelectColumn<T, keyof T>;
|
|
17
|
-
export type TableColumnType = 'actions' | 'badge' | 'button' | 'by' | 'checkbox' | 'custom' | 'icon' | 'image' | 'move-row' | 'select' | 'snippet' | 'text' | 'text-input';
|
|
10
|
+
export type IAnyTableColumn<T> = IOneOfTableColumn<T>;
|
|
11
|
+
export type IOneOfTableColumn<T> = ITableBadgeColumn<T> | ITableButtonColumn<T> | ITableByColumn<T> | ITableCheckboxColumn | ITableCustomColumn<T> | ITableIconColumn<T> | ITableImageColumn<T> | ITableMoveRowColumn | ITableSnippetColumn<T> | ITableTextColumn<T>;
|
|
12
|
+
export type TableColumnType = 'actions' | 'badge' | 'button' | 'by' | 'checkbox' | 'custom' | 'icon' | 'image' | 'move-row' | 'snippet' | 'text';
|
|
18
13
|
interface ITableColumnBase {
|
|
19
14
|
type: TableColumnType;
|
|
20
15
|
fixedCssWidth?: string;
|
|
@@ -45,7 +40,8 @@ export interface ITableBadgeColumn<T> extends ITableColumn<T> {
|
|
|
45
40
|
}
|
|
46
41
|
export interface ITableByColumn<T> extends ITableColumn<T> {
|
|
47
42
|
type: 'by';
|
|
48
|
-
|
|
43
|
+
/** The "when" — an ISO date string. */
|
|
44
|
+
valueFactory?: ((item: T) => string) | null;
|
|
49
45
|
byValueFactory?: ((item: T) => {
|
|
50
46
|
id: string;
|
|
51
47
|
name: string;
|
|
@@ -92,17 +88,6 @@ export interface ITableMoveRowColumn extends ITableColumnBase {
|
|
|
92
88
|
type: 'move-row';
|
|
93
89
|
disableFactory?: (() => boolean) | null;
|
|
94
90
|
}
|
|
95
|
-
export interface ITableSelectColumn<T, K extends keyof T> extends ITableColumn<T> {
|
|
96
|
-
id: K;
|
|
97
|
-
type: 'select';
|
|
98
|
-
valueFactory?: ((item: T) => T[K]) | null;
|
|
99
|
-
optionsFactory: (item: T) => SelectOption<T[K]>[];
|
|
100
|
-
placeholder?: string | null;
|
|
101
|
-
disabledFactory?: ((item: T) => boolean) | null;
|
|
102
|
-
visibilityFactory?: ((item: T) => boolean) | null;
|
|
103
|
-
clearable?: boolean;
|
|
104
|
-
onSelected: (item: T, value: T[K] | null) => void;
|
|
105
|
-
}
|
|
106
91
|
export interface ITableSnippetColumn<T> extends ITableColumn<T> {
|
|
107
92
|
type: 'snippet';
|
|
108
93
|
/** Render-prop snippet receiving the row item. Use for rich / custom cell composition without creating a separate cell component. */
|
|
@@ -114,14 +99,6 @@ export interface ITableTextColumn<T> extends ITableColumn<T> {
|
|
|
114
99
|
/** @default 1 */
|
|
115
100
|
maxLines?: number | 'single-line-no-trim' | null;
|
|
116
101
|
valueFactory?: ((item: T) => string) | null;
|
|
117
|
-
editorColumn?: IEditorColumn<T> | null;
|
|
118
|
-
}
|
|
119
|
-
export interface ITableTextInputColumn<T> extends ITableColumn<T> {
|
|
120
|
-
type: 'text-input';
|
|
121
|
-
inputType?: 'text' | 'number' | null;
|
|
122
|
-
validationSchema?: ISchema<unknown> | null;
|
|
123
|
-
onValidated?: ((item: T, error: string) => void) | null;
|
|
124
|
-
onInput?: ((item: T, value: string) => void) | null;
|
|
125
102
|
}
|
|
126
103
|
export type TableTextColumnFormat = 'text' | 'html' | 'money' | 'date' | 'date-time';
|
|
127
104
|
export type TableImageColumnFormat = 'circle' | 'square-contain' | 'square-cover';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<script lang="ts">const { checked, size = 'md', disabled = false, layout = 'spread', label, sublabel, title, name, on } = $props();
|
|
1
|
+
<script lang="ts">const { checked, size = 'md', disabled = false, presentational = false, layout = 'spread', label, sublabel, title, name, on } = $props();
|
|
2
2
|
const hasLabel = $derived(label !== undefined || sublabel !== undefined);
|
|
3
3
|
const ariaLabel = $derived(typeof label === 'string' ? label : name);
|
|
4
4
|
const handleClick = (event) => {
|
|
@@ -12,19 +12,25 @@ export {};
|
|
|
12
12
|
</script>
|
|
13
13
|
|
|
14
14
|
{#snippet switchControl()}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
15
|
+
{#if presentational}
|
|
16
|
+
<span class="toggle toggle--{size}" class:toggle--on={checked} class:toggle--disabled={disabled} aria-hidden="true">
|
|
17
|
+
<span class="toggle__thumb"></span>
|
|
18
|
+
</span>
|
|
19
|
+
{:else}
|
|
20
|
+
<button
|
|
21
|
+
type="button"
|
|
22
|
+
class="toggle toggle--{size}"
|
|
23
|
+
class:toggle--on={checked}
|
|
24
|
+
class:toggle--disabled={disabled}
|
|
25
|
+
role="switch"
|
|
26
|
+
aria-checked={checked}
|
|
27
|
+
aria-label={ariaLabel}
|
|
28
|
+
title={hasLabel ? undefined : title}
|
|
29
|
+
disabled={disabled}
|
|
30
|
+
onclick={handleClick}>
|
|
31
|
+
<span class="toggle__thumb" aria-hidden="true"></span>
|
|
32
|
+
</button>
|
|
33
|
+
{/if}
|
|
28
34
|
{/snippet}
|
|
29
35
|
|
|
30
36
|
{#if hasLabel}
|
|
@@ -140,10 +146,10 @@ switch left + label right, intrinsic width).
|
|
|
140
146
|
}
|
|
141
147
|
|
|
142
148
|
.toggle {
|
|
143
|
-
--_toggle--track-width: var(--sc-kit--toggle--track-width, 2rem);
|
|
144
|
-
--_toggle--track-height: var(--sc-kit--toggle--track-height, 1.125rem);
|
|
145
|
-
--_toggle--thumb-size: var(--sc-kit--toggle--thumb-size, 0.875rem);
|
|
146
|
-
--_toggle--thumb-travel: var(--sc-kit--toggle--thumb-travel, 0.875rem);
|
|
149
|
+
--_toggle--track-width: var(--sc-kit--toggle--track-width, var(--_toggle--size-track-width, 2rem));
|
|
150
|
+
--_toggle--track-height: var(--sc-kit--toggle--track-height, var(--_toggle--size-track-height, 1.125rem));
|
|
151
|
+
--_toggle--thumb-size: var(--sc-kit--toggle--thumb-size, var(--_toggle--size-thumb-size, 0.875rem));
|
|
152
|
+
--_toggle--thumb-travel: var(--sc-kit--toggle--thumb-travel, var(--_toggle--size-thumb-travel, 0.875rem));
|
|
147
153
|
--_toggle--thumb-offset: var(--sc-kit--toggle--thumb-offset, 0.125rem);
|
|
148
154
|
--_toggle--thumb-translate: 0;
|
|
149
155
|
--_toggle--background: var(--sc-kit--toggle--background, var(--sc-kit--color--border--strong));
|
|
@@ -166,16 +172,16 @@ switch left + label right, intrinsic width).
|
|
|
166
172
|
outline-offset: 2px;
|
|
167
173
|
}
|
|
168
174
|
.toggle--sm {
|
|
169
|
-
--
|
|
170
|
-
--
|
|
171
|
-
--
|
|
172
|
-
--
|
|
175
|
+
--_toggle--size-track-width: 1.625rem;
|
|
176
|
+
--_toggle--size-track-height: 0.875rem;
|
|
177
|
+
--_toggle--size-thumb-size: 0.625rem;
|
|
178
|
+
--_toggle--size-thumb-travel: 0.75rem;
|
|
173
179
|
}
|
|
174
180
|
.toggle--md {
|
|
175
|
-
--
|
|
176
|
-
--
|
|
177
|
-
--
|
|
178
|
-
--
|
|
181
|
+
--_toggle--size-track-width: 2rem;
|
|
182
|
+
--_toggle--size-track-height: 1.125rem;
|
|
183
|
+
--_toggle--size-thumb-size: 0.875rem;
|
|
184
|
+
--_toggle--size-thumb-travel: 0.875rem;
|
|
179
185
|
}
|
|
180
186
|
.toggle--on {
|
|
181
187
|
--sc-kit--toggle--background: var(--sc-kit--toggle--background--on, var(--sc-kit--color--accent));
|
|
@@ -4,6 +4,8 @@ type Props = {
|
|
|
4
4
|
/** @default 'md' */
|
|
5
5
|
size?: 'sm' | 'md';
|
|
6
6
|
disabled?: boolean;
|
|
7
|
+
/** Render a non-interactive visual switch (a `<span>`, not a button) for embedding inside another interactive row that owns the click + semantics. @default false */
|
|
8
|
+
presentational?: boolean;
|
|
7
9
|
/** Label position relative to the switch. `spread` = label left, switch right (settings-row). `inline` = switch left, label right (compact). @default 'spread' */
|
|
8
10
|
layout?: 'spread' | 'inline';
|
|
9
11
|
/** Label: plain text or a snippet for rich content. */
|
|
@@ -65,6 +65,10 @@ const handleDrop = (e) => {
|
|
|
65
65
|
DraggingNodeState.draggingNode = null;
|
|
66
66
|
DraggingNodeState.dragOverNode = null;
|
|
67
67
|
};
|
|
68
|
+
const handlePointerDown = () => {
|
|
69
|
+
nav.setActive(node);
|
|
70
|
+
nav.keyboardActive = false;
|
|
71
|
+
};
|
|
68
72
|
const handleSelectNode = (e) => {
|
|
69
73
|
e.stopPropagation();
|
|
70
74
|
nav.setActive(node);
|
|
@@ -122,7 +126,7 @@ const handleMouseLeave = () => {
|
|
|
122
126
|
<div
|
|
123
127
|
class="tree-node__leaf"
|
|
124
128
|
class:tree-node__leaf--active={node.selected}
|
|
125
|
-
class:tree-node__leaf--focused={nav.hasFocus && nav.activeId === node.id}
|
|
129
|
+
class:tree-node__leaf--focused={nav.hasFocus && nav.keyboardActive && nav.activeId === node.id}
|
|
126
130
|
class:tree-node__leaf--draggable={node.dragConfig}
|
|
127
131
|
class:tree-node__leaf--expandable={children.length}
|
|
128
132
|
class:tree-node__leaf--drag-over={isDraggedOver}
|
|
@@ -143,6 +147,7 @@ const handleMouseLeave = () => {
|
|
|
143
147
|
class:tree-node__expand-button--active={node.selected || node.involvedIntoActivePath}
|
|
144
148
|
tabindex="-1"
|
|
145
149
|
aria-hidden="true"
|
|
150
|
+
onpointerdown={handlePointerDown}
|
|
146
151
|
onclick={handleExpandNode}>
|
|
147
152
|
<span class="tree-node__expand-icon" class:tree-node__expand-icon--expanded={node.expanded}>
|
|
148
153
|
<Icon src={IconChevronRight} />
|
|
@@ -151,7 +156,7 @@ const handleMouseLeave = () => {
|
|
|
151
156
|
{/if}
|
|
152
157
|
|
|
153
158
|
<!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
|
|
154
|
-
<div class="tree-node__anchor" onclick={handleSelectNode} ondblclick={handleExpandOnDoubleClick}>
|
|
159
|
+
<div class="tree-node__anchor" onpointerdown={handlePointerDown} onclick={handleSelectNode} ondblclick={handleExpandOnDoubleClick}>
|
|
155
160
|
{#if !node.hideIcon}
|
|
156
161
|
<div class="tree-node__icon" class:tree-node__icon--active={node.selected}>
|
|
157
162
|
{#if node.icon}
|
|
@@ -4,7 +4,15 @@ let { nodes, showSelectedCheckmark, children } = $props();
|
|
|
4
4
|
const nodesComputed = $derived(nodes ? (Array.isArray(nodes) ? nodes : [nodes]) : []);
|
|
5
5
|
const hideExpandIcon = $derived(nodesComputed.every((f) => !f.children.length));
|
|
6
6
|
const nav = new TreeNavigation(() => nodesComputed);
|
|
7
|
+
const handleFocusIn = (e) => {
|
|
8
|
+
nav.focus();
|
|
9
|
+
nav.keyboardActive = e.currentTarget.matches(':focus-visible');
|
|
10
|
+
};
|
|
7
11
|
const handleKeydown = (e) => {
|
|
12
|
+
// Only the container itself drives tree navigation; a focused descendant (e.g. the actions popover) keeps its own keys.
|
|
13
|
+
if (e.target !== e.currentTarget) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
8
16
|
nav.handleKeydown(e);
|
|
9
17
|
// ArrowLeft collapses a subtree; if focus sat on a treeitem inside it, the unmount would drop focus
|
|
10
18
|
// to <body> and arrow keys would die. Re-grab the container before the unmount so the single
|
|
@@ -20,7 +28,7 @@ const handleKeydown = (e) => {
|
|
|
20
28
|
role="tree"
|
|
21
29
|
tabindex="0"
|
|
22
30
|
aria-activedescendant={nav.activeId ? `tree-item-${nav.activeId}` : undefined}
|
|
23
|
-
onfocusin={
|
|
31
|
+
onfocusin={handleFocusIn}
|
|
24
32
|
onfocusout={(e) => {
|
|
25
33
|
if (!(e.relatedTarget instanceof Node) || !e.currentTarget.contains(e.relatedTarget)) {
|
|
26
34
|
nav.blur();
|
|
@@ -2,7 +2,7 @@ import type { TreeNode } from './tree-node.svelte';
|
|
|
2
2
|
import type { Snippet } from 'svelte';
|
|
3
3
|
type Props = {
|
|
4
4
|
/** Root node, or array of root nodes. */
|
|
5
|
-
nodes: TreeNode | TreeNode[];
|
|
5
|
+
nodes: TreeNode | readonly TreeNode[];
|
|
6
6
|
/** Show a success checkmark on the selected node when it has no actions. */
|
|
7
7
|
showSelectedCheckmark?: boolean;
|
|
8
8
|
/** Optional content rendered above the nodes (e.g. a header). */
|
|
@@ -2,6 +2,8 @@ import type { TreeNode } from './tree-node.svelte';
|
|
|
2
2
|
export declare class TreeNavigation {
|
|
3
3
|
activeId: string | null;
|
|
4
4
|
hasFocus: boolean;
|
|
5
|
+
/** True only while the active node was reached via keyboard — gates the focus ring (mouse selection shows background only). */
|
|
6
|
+
keyboardActive: boolean;
|
|
5
7
|
private _getRoots;
|
|
6
8
|
constructor(getRoots: () => readonly TreeNode[]);
|
|
7
9
|
itemId(node: TreeNode): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export class TreeNavigation {
|
|
2
2
|
activeId = $state(null);
|
|
3
3
|
hasFocus = $state(false);
|
|
4
|
+
/** True only while the active node was reached via keyboard — gates the focus ring (mouse selection shows background only). */
|
|
5
|
+
keyboardActive = $state(false);
|
|
4
6
|
_getRoots;
|
|
5
7
|
constructor(getRoots) {
|
|
6
8
|
this._getRoots = getRoots;
|
|
@@ -31,6 +33,7 @@ export class TreeNavigation {
|
|
|
31
33
|
if (!visible.length) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
36
|
+
this.keyboardActive = true;
|
|
34
37
|
const index = visible.findIndex((n) => n.id === this.activeId);
|
|
35
38
|
const current = index < 0 ? null : visible[index];
|
|
36
39
|
switch (e.key) {
|