@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.
Files changed (86) hide show
  1. package/dist/ui/badge/cmp.badge.svelte +4 -0
  2. package/dist/ui/badge/cmp.badge.svelte.d.ts +1 -0
  3. package/dist/ui/page-toolbar/cmp.reorderable-checkbox-list.svelte +131 -0
  4. package/dist/ui/page-toolbar/cmp.reorderable-checkbox-list.svelte.d.ts +31 -0
  5. package/dist/ui/page-toolbar/cmp.toolbar-checkbox.svelte +14 -0
  6. package/dist/ui/page-toolbar/cmp.toolbar-checkbox.svelte.d.ts +19 -0
  7. package/dist/ui/page-toolbar/cmp.toolbar-divider.svelte +19 -0
  8. package/dist/ui/page-toolbar/cmp.toolbar-divider.svelte.d.ts +35 -0
  9. package/dist/ui/page-toolbar/cmp.toolbar-filter-select.svelte +143 -0
  10. package/dist/ui/page-toolbar/cmp.toolbar-filter-select.svelte.d.ts +68 -0
  11. package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte +71 -0
  12. package/dist/ui/page-toolbar/cmp.toolbar-icon-button.svelte.d.ts +32 -0
  13. package/dist/ui/page-toolbar/cmp.toolbar-option.svelte +189 -0
  14. package/dist/ui/page-toolbar/cmp.toolbar-option.svelte.d.ts +47 -0
  15. package/dist/ui/page-toolbar/cmp.toolbar-popover.svelte +23 -8
  16. package/dist/ui/page-toolbar/cmp.toolbar-popover.svelte.d.ts +14 -6
  17. package/dist/ui/page-toolbar/cmp.toolbar-section-title.svelte +46 -0
  18. package/dist/ui/page-toolbar/cmp.toolbar-section-title.svelte.d.ts +24 -0
  19. package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte +31 -0
  20. package/dist/ui/page-toolbar/cmp.toolbar-sort-select.svelte.d.ts +44 -0
  21. package/dist/ui/page-toolbar/{cmp.toolbar-switch-item.svelte → cmp.toolbar-switch.svelte} +10 -10
  22. package/dist/ui/page-toolbar/{cmp.toolbar-switch-item.svelte.d.ts → cmp.toolbar-switch.svelte.d.ts} +2 -2
  23. package/dist/ui/page-toolbar/cmp.toolbar-toggle.svelte +14 -0
  24. package/dist/ui/page-toolbar/cmp.toolbar-toggle.svelte.d.ts +19 -0
  25. package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte +51 -0
  26. package/dist/ui/page-toolbar/cmp.toolbar-view-controls.svelte.d.ts +27 -0
  27. package/dist/ui/page-toolbar/index.d.ts +12 -7
  28. package/dist/ui/page-toolbar/index.js +11 -6
  29. package/dist/ui/page-toolbar/reorderable-checkbox-list-localization.d.ts +5 -0
  30. package/dist/ui/page-toolbar/reorderable-checkbox-list-localization.js +17 -0
  31. package/dist/ui/page-toolbar/toolbar-filter-select-localization.d.ts +5 -0
  32. package/dist/ui/page-toolbar/toolbar-filter-select-localization.js +20 -0
  33. package/dist/ui/page-toolbar/toolbar-sort-select-localization.d.ts +3 -0
  34. package/dist/ui/page-toolbar/toolbar-sort-select-localization.js +9 -0
  35. package/dist/ui/page-toolbar/toolbar-view-controls-localization.d.ts +5 -0
  36. package/dist/ui/page-toolbar/toolbar-view-controls-localization.js +17 -0
  37. package/dist/ui/page-toolbar/types.d.ts +1 -1
  38. package/dist/ui/select/index.d.ts +1 -0
  39. package/dist/ui/select/index.js +1 -0
  40. package/dist/ui/select/select-listbox.svelte +11 -5
  41. package/dist/ui/select/select-listbox.svelte.d.ts +1 -1
  42. package/dist/ui/table/_table-styles.scss +156 -0
  43. package/dist/ui/table/cmp.table-draggable.svelte +338 -0
  44. package/dist/ui/table/cmp.table-draggable.svelte.d.ts +79 -0
  45. package/dist/ui/table/cmp.table.svelte +96 -265
  46. package/dist/ui/table/cmp.table.svelte.d.ts +16 -8
  47. package/dist/ui/table/index.d.ts +1 -0
  48. package/dist/ui/table/index.js +1 -0
  49. package/dist/ui/table/table-cells/index.d.ts +1 -12
  50. package/dist/ui/table/table-cells/index.js +1 -12
  51. package/dist/ui/table/table-cells/table-cell.svelte +77 -0
  52. package/dist/ui/table/table-cells/{table-select-cell.svelte.d.ts → table-cell.svelte.d.ts} +12 -6
  53. package/dist/ui/table/table-column-models.svelte.d.ts +1 -2
  54. package/dist/ui/table/table-column-models.svelte.js +0 -1
  55. package/dist/ui/table/table-columns-manager/cmp.table-columns-manager.svelte +20 -47
  56. package/dist/ui/table/table-columns-manager/cmp.table-columns-manager.svelte.d.ts +3 -7
  57. package/dist/ui/table/table-columns-manager/table-columns-manager-localization.d.ts +1 -1
  58. package/dist/ui/table/table-columns-manager/table-columns-manager-localization.js +7 -7
  59. package/dist/ui/table/table-localization.d.ts +0 -2
  60. package/dist/ui/table/table-localization.js +0 -14
  61. package/dist/ui/table/table-model.svelte.d.ts +2 -3
  62. package/dist/ui/table/table-model.svelte.js +5 -35
  63. package/dist/ui/table/table-view.svelte.d.ts +14 -0
  64. package/dist/ui/table/table-view.svelte.js +32 -0
  65. package/dist/ui/table/types.d.ts +5 -28
  66. package/dist/ui/toggle/cmp.toggle.svelte +32 -26
  67. package/dist/ui/toggle/cmp.toggle.svelte.d.ts +2 -0
  68. package/dist/ui/tree/cmp.tree-node-view.svelte +7 -2
  69. package/dist/ui/tree/cmp.tree.svelte +9 -1
  70. package/dist/ui/tree/cmp.tree.svelte.d.ts +1 -1
  71. package/dist/ui/tree/tree-navigation.svelte.d.ts +2 -0
  72. package/dist/ui/tree/tree-navigation.svelte.js +3 -0
  73. package/package.json +1 -1
  74. package/dist/ui/page-toolbar/cmp.reorderable-toggle-list.svelte +0 -153
  75. package/dist/ui/page-toolbar/cmp.reorderable-toggle-list.svelte.d.ts +0 -20
  76. package/dist/ui/page-toolbar/cmp.toolbar-checkbox-item.svelte +0 -19
  77. package/dist/ui/page-toolbar/cmp.toolbar-checkbox-item.svelte.d.ts +0 -12
  78. package/dist/ui/page-toolbar/cmp.toolbar-panel-button.svelte +0 -42
  79. package/dist/ui/page-toolbar/cmp.toolbar-panel-button.svelte.d.ts +0 -20
  80. package/dist/ui/page-toolbar/cmp.toolbar-panel.svelte +0 -57
  81. package/dist/ui/page-toolbar/cmp.toolbar-panel.svelte.d.ts +0 -19
  82. package/dist/ui/page-toolbar/cmp.toolbar-toggle-item.svelte +0 -16
  83. package/dist/ui/page-toolbar/cmp.toolbar-toggle-item.svelte.d.ts +0 -12
  84. package/dist/ui/table/table-cells/table-select-cell.svelte +0 -28
  85. package/dist/ui/table/table-cells/table-text-input-cell.svelte +0 -69
  86. 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, keyof T> | ITableActionsColumn;
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>);
@@ -2,7 +2,6 @@ export class ResolvedColumn {
2
2
  def;
3
3
  uniqueId = Symbol();
4
4
  headerView = null;
5
- editorColumn = null;
6
5
  hidden = $state(false);
7
6
  sortDirection = $state('none');
8
7
  constructor(def) {
@@ -1,10 +1,6 @@
1
1
  <script lang="ts" generics="T extends {id: string}">import { LocalStorageItem } from '../../../core/local-storage';
2
- import { Button } from '../../button';
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 (!storage || columns.length === 0 || initialized) {
19
+ if (columns.length === 0 || initialized) {
24
20
  return;
25
21
  }
26
22
  initialized = true;
27
- const saved = storage.get();
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
- <Popover position="bottom-start" keepOpen>
79
- {#snippet trigger()}
80
- <Button type="presentational" variant="secondary" size="sm">
81
- <IconText icon={{ src: IconColumnTwo, color: 'accent' }} secondaryIcon={IconChevronDown} size="sm" iconScale="large">{localization.columns}</IconText>
82
- </Button>
83
- {/snippet}
84
- <div class="columns-manager">
85
- <div class="columns-manager__title">{localization.selectColumns}</div>
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. Shows a popover with a reorderable toggle list for each hideable column.
95
- Supports optional localStorage persistence of column order and visibility.
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. Shows a popover with a reorderable toggle list for each hideable column.
45
- * Supports optional localStorage persistence of column order and visibility.
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,4 +1,4 @@
1
1
  export declare class TableColumnsManagerLocalization {
2
+ get view(): string;
2
3
  get columns(): string;
3
- get selectColumns(): string;
4
4
  }
@@ -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
  };
@@ -1,6 +1,4 @@
1
1
  export declare class TableLocalization {
2
2
  get noItemsFoundTitle(): string;
3
3
  get noItemsFoundBody(): string;
4
- get save(): string;
5
- get cancel(): string;
6
4
  }
@@ -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
+ };
@@ -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
- [P in keyof T]: IOneOfTableColumn<T, P>;
14
- }[keyof T];
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
- valueFactory?: ((item: T) => Date) | null;
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
- <button
16
- type="button"
17
- class="toggle toggle--{size}"
18
- class:toggle--on={checked}
19
- class:toggle--disabled={disabled}
20
- role="switch"
21
- aria-checked={checked}
22
- aria-label={ariaLabel}
23
- title={hasLabel ? undefined : title}
24
- disabled={disabled}
25
- onclick={handleClick}>
26
- <span class="toggle__thumb" aria-hidden="true"></span>
27
- </button>
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
- --sc-kit--toggle--track-width: 1.625rem;
170
- --sc-kit--toggle--track-height: 0.875rem;
171
- --sc-kit--toggle--thumb-size: 0.625rem;
172
- --sc-kit--toggle--thumb-travel: 0.75rem;
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
- --sc-kit--toggle--track-width: 2rem;
176
- --sc-kit--toggle--track-height: 1.125rem;
177
- --sc-kit--toggle--thumb-size: 0.875rem;
178
- --sc-kit--toggle--thumb-travel: 0.875rem;
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={() => nav.focus()}
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamscloud/kit",
3
- "version": "0.17.0",
3
+ "version": "0.18.0",
4
4
  "author": "StreamsCloud",
5
5
  "repository": {
6
6
  "type": "git",