@lavalogic/scoria 0.37.56 → 0.38.1

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 (50) hide show
  1. package/dist/Components/Table/ARCHITECTURE.md +22 -22
  2. package/dist/Components/Table/Misc/ColumnPanel.svelte +17 -61
  3. package/dist/Components/Table/Misc/ColumnPanel.svelte.d.ts +2 -24
  4. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +3 -92
  5. package/dist/Components/Table/Misc/ColumnPanelModal.svelte.d.ts +2 -24
  6. package/dist/Components/Table/Misc/ColumnPanelModalProps.d.ts +2 -2
  7. package/dist/Components/Table/Misc/FilterPanel.svelte +25 -113
  8. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +626 -0
  9. package/dist/Components/Table/Misc/TableConfigurationModal.svelte.d.ts +26 -0
  10. package/dist/Components/Table/Misc/TableConfigurationModalProps.d.ts +13 -0
  11. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +15 -0
  12. package/dist/Components/Table/Misc/TableSidebar.svelte +45 -2
  13. package/dist/Components/Table/Misc/TableViewDropdown.svelte +97 -0
  14. package/dist/Components/Table/Misc/TableViewDropdown.svelte.d.ts +35 -0
  15. package/dist/Components/Table/SubApis.svelte.js +0 -41
  16. package/dist/Components/Table/Types/Columns/JSONTableLayout.d.ts +5 -5
  17. package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.d.ts +5 -5
  18. package/dist/Components/Table/Types/Context/ColumnLayoutState.svelte.js +5 -5
  19. package/dist/Components/Table/Types/Context/PreferencesState.svelte.d.ts +106 -147
  20. package/dist/Components/Table/Types/Context/PreferencesState.svelte.js +156 -203
  21. package/dist/Components/Table/Types/Context/TableContext.svelte.d.ts +323 -44
  22. package/dist/Components/Table/Types/Context/TableContext.svelte.js +687 -187
  23. package/dist/Components/Table/Types/Context/TableInitOptions.d.ts +9 -0
  24. package/dist/Components/Table/Types/Context/ViewState.svelte.d.ts +152 -0
  25. package/dist/Components/Table/Types/Context/ViewState.svelte.js +223 -0
  26. package/dist/Components/Table/Types/ExpandedPanel.d.ts +9 -5
  27. package/dist/Components/Table/Types/ExpandedPanel.js +9 -5
  28. package/dist/Components/Table/Types/Persistence/DatatableView.d.ts +95 -0
  29. package/dist/Components/Table/Types/Persistence/DatatableViewEnvelope.d.ts +21 -0
  30. package/dist/Components/Table/Types/Persistence/DatatableViewEnvelope.js +1 -0
  31. package/dist/Components/Table/Types/Persistence/DatatableViewKind.d.ts +10 -0
  32. package/dist/Components/Table/Types/Persistence/DatatableViewKind.js +1 -0
  33. package/dist/Components/Table/Types/Persistence/JSONActiveView.d.ts +58 -0
  34. package/dist/Components/Table/Types/Persistence/JSONActiveView.js +41 -0
  35. package/dist/Components/Table/Types/Persistence/JSONTableFilter.d.ts +53 -0
  36. package/dist/Components/Table/Types/Persistence/JSONTableFilter.js +42 -0
  37. package/dist/Components/Table/Types/Persistence/RemoteTableLayoutAdapter.d.ts +47 -0
  38. package/dist/Components/Table/Types/Persistence/RemoteTableLayoutAdapter.js +1 -0
  39. package/dist/Components/Table/Types/Public/CreateTableOptions.d.ts +10 -0
  40. package/dist/Components/Table/Types/Public/TablePersistence.d.ts +1 -4
  41. package/dist/Components/Table/Types/Public/TableSubApis.d.ts +0 -18
  42. package/dist/Components/Table/Types/Public/index.d.ts +1 -1
  43. package/dist/Components/Table/createTable.svelte.js +2 -0
  44. package/dist/index.d.ts +8 -1
  45. package/dist/index.js +9 -0
  46. package/package.json +1 -1
  47. package/dist/Components/Table/Types/Columns/Definitions/ColumnDefSet.d.ts +0 -20
  48. package/dist/Components/Table/Types/Columns/Definitions/JSONColumnDefSet.d.ts +0 -25
  49. /package/dist/Components/Table/{Types/Columns/Definitions/ColumnDefSet.js → Misc/TableConfigurationModalProps.js} +0 -0
  50. /package/dist/Components/Table/Types/{Columns/Definitions/JSONColumnDefSet.js → Persistence/DatatableView.js} +0 -0
@@ -17,24 +17,39 @@
17
17
  import ColumnPanel from './ColumnPanel.svelte';
18
18
  import FilterPanel from './FilterPanel.svelte';
19
19
  import SidePanel from './SidePanel.svelte';
20
+ import TableConfigurationModal from './TableConfigurationModal.svelte';
20
21
  import ToolboxPanel from './ToolboxPanel.svelte';
21
22
 
22
23
  const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
23
24
 
25
+ // `TableConfigurationModal` is a full `DesktopModal` rather than a
26
+ // side-panel body, so it is registered here but rendered standalone
27
+ // (outside `SidePanel`) below - `panels` only maps the side-panel
28
+ // kinds. Keeping the Configuration key in the map documents the
29
+ // relationship and mirrors `TableHorizontalBar`'s registration.
24
30
  const panels = {
25
31
  [ExpandedPanel.Column]: ColumnPanel,
26
32
  [ExpandedPanel.Filter]: FilterPanel,
27
33
  [ExpandedPanel.Toolbox]: ToolboxPanel,
34
+ [ExpandedPanel.Configuration]: TableConfigurationModal,
28
35
  } as const;
29
36
 
30
37
  let expandedPanel = $state<ExpandedPanel | null>(null);
31
38
 
32
39
  $effect(() => {
33
- tableContext.limitFooter = !!expandedPanel;
40
+ // The Configuration modal owns its own overlay and must not shrink
41
+ // the table footer the way an inline side panel does.
42
+ tableContext.limitFooter = !!expandedPanel && expandedPanel !== ExpandedPanel.Configuration;
34
43
  });
35
44
 
45
+ // Side-panel body for the inline panel kinds only; the Configuration
46
+ // kind is excluded and handled by the standalone modal block below.
36
47
  let ExpandedPanelElement = $derived(
37
- expandedPanel && expandedPanel !== ExpandedPanel.None ? panels[expandedPanel] : null
48
+ expandedPanel &&
49
+ expandedPanel !== ExpandedPanel.None &&
50
+ expandedPanel !== ExpandedPanel.Configuration
51
+ ? panels[expandedPanel]
52
+ : null
38
53
  );
39
54
 
40
55
  function handleClickOutside(e: MouseEvent) {
@@ -58,6 +73,12 @@
58
73
  if (target.closest('[data-scoria-portal-dropdown]') != null) {
59
74
  return;
60
75
  }
76
+ // The Table Configuration modal owns its own backdrop + close
77
+ // affordances; a click inside its `.modal-root` must not be
78
+ // treated as a click-outside that dismisses the panel.
79
+ if (target.closest('.modal-root') != null) {
80
+ return;
81
+ }
61
82
  expandedPanel = null;
62
83
  }
63
84
 
@@ -78,6 +99,14 @@
78
99
  <ExpandedPanelElement />
79
100
  </SidePanel>
80
101
  {/if}
102
+
103
+ {#if expandedPanel === ExpandedPanel.Configuration}
104
+ <TableConfigurationModal
105
+ onclose={() => {
106
+ expandedPanel = null;
107
+ }}
108
+ />
109
+ {/if}
81
110
  <div class="sidebar">
82
111
  <Button
83
112
  side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
@@ -133,6 +162,20 @@
133
162
  }}
134
163
  label="My Tool Box"
135
164
  />
165
+ <Button
166
+ side={tableContext.toolbarPosition === ToolbarPosition.Left ? Side.Left : Side.Right}
167
+ toggled={expandedPanel === ExpandedPanel.Configuration}
168
+ onclick={() => {
169
+ expandedPanel =
170
+ expandedPanel === ExpandedPanel.Configuration ? null : ExpandedPanel.Configuration;
171
+ }}
172
+ variant={Variant.Toolbar}
173
+ iconTop={{
174
+ name: 'setting-cog',
175
+ size: Size.Medium,
176
+ }}
177
+ label="Table Configuration"
178
+ />
136
179
 
137
180
  {#if tableContext.paginationRepo?.reload}
138
181
  <Button
@@ -0,0 +1,97 @@
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="T extends object, IdType extends Primitive"
6
+ >
7
+ import SingleSelect from '../../SingleSelect.svelte';
8
+ import type { SelectOption } from '../../../Types/Internal/SelectOption.js';
9
+ import { getContext } from 'svelte';
10
+ import { TableContext } from '../Types/Context/TableContext.svelte.js';
11
+ import type { Primitive } from '../Types/Context/TableInitOptions.js';
12
+ import type { DatatableViewKind } from '../Types/Persistence/DatatableViewKind.js';
13
+
14
+ interface Props {
15
+ /**
16
+ * Which view-state slice this dropdown switches between. `'layout'`
17
+ * lists saved column layouts; `'filter'` lists saved filter sets.
18
+ * Drives every `_viewState` read (label / active view / saved
19
+ * views) and which kind `resetToDefault` is called with.
20
+ */
21
+ kind: DatatableViewKind;
22
+ }
23
+
24
+ const { kind }: Props = $props();
25
+
26
+ const tableContext = getContext<TableContext<T, IdType>>(TableContext.identifier);
27
+
28
+ // Sentinel value for the "Default" option. A real saved view always
29
+ // carries a server-assigned, non-empty `id`, so an empty string never
30
+ // collides with one.
31
+ const DEFAULT_OPTION = '';
32
+
33
+ // The view-state slice for `kind`, read live from the context.
34
+ const activeView = $derived(tableContext._viewState.activeView(kind));
35
+ const savedViews = $derived(tableContext._viewState.savedViews(kind));
36
+
37
+ // Dropdown label: 'Default' / 'Custom (unsaved)' / '<name>' / '<name>
38
+ // (unsaved)'. Used as the displayed label of the currently-selected
39
+ // option so the dropdown reflects the dirty state exactly.
40
+ const dropdownLabel = $derived(tableContext._viewState.dropdownLabel(kind));
41
+
42
+ // The value of the currently-active option: the sentinel for Default,
43
+ // otherwise the saved view's id.
44
+ const activeValue = $derived(activeView.kind === 'saved' ? activeView.id : DEFAULT_OPTION);
45
+
46
+ // Options for the select. The currently-active option's label is the
47
+ // `dropdownLabel` (so a dirty view reads "<name> (unsaved)"); every
48
+ // other option shows its plain name. "Default" is always first, even
49
+ // when remote saved views are disabled and `savedViews` is empty.
50
+ const options: Array<SelectOption<string>> = $derived([
51
+ {
52
+ label: activeValue === DEFAULT_OPTION ? dropdownLabel : 'Default',
53
+ value: DEFAULT_OPTION,
54
+ },
55
+ ...savedViews.map((view) => ({
56
+ label: view.id === activeValue ? dropdownLabel : view.name,
57
+ value: view.id,
58
+ })),
59
+ ]);
60
+
61
+ const selectedOption = $derived(options.find((o) => o.value === activeValue) ?? options[0]);
62
+
63
+ /** Apply the option the user picked from the dropdown. */
64
+ function selectOption(option: SelectOption<string> | null): void {
65
+ if (!option) {
66
+ return;
67
+ }
68
+ if (option.value === DEFAULT_OPTION) {
69
+ tableContext.resetToDefault(kind);
70
+ return;
71
+ }
72
+ const view = savedViews.find((v) => v.id === option.value);
73
+ if (view) {
74
+ tableContext.applyDatatableView(view);
75
+ }
76
+ }
77
+ </script>
78
+
79
+ <div class="view-dropdown">
80
+ <SingleSelect
81
+ label={kind === 'filter' ? 'Filter' : 'Layout'}
82
+ name={kind === 'filter' ? 'Filter' : 'Layout'}
83
+ {options}
84
+ labelProp="label"
85
+ valueProp="value"
86
+ value={selectedOption}
87
+ valueAsObject
88
+ searchable={false}
89
+ onchange={selectOption}
90
+ />
91
+ </div>
92
+
93
+ <style>.view-dropdown {
94
+ display: flex;
95
+ flex-flow: column nowrap;
96
+ width: 100%;
97
+ }</style>
@@ -0,0 +1,35 @@
1
+ import type { Primitive } from '../Types/Context/TableInitOptions.js';
2
+ import type { DatatableViewKind } from '../Types/Persistence/DatatableViewKind.js';
3
+ interface Props {
4
+ /**
5
+ * Which view-state slice this dropdown switches between. `'layout'`
6
+ * lists saved column layouts; `'filter'` lists saved filter sets.
7
+ * Drives every `_viewState` read (label / active view / saved
8
+ * views) and which kind `resetToDefault` is called with.
9
+ */
10
+ kind: DatatableViewKind;
11
+ }
12
+ declare function $$render<T extends object, IdType extends Primitive>(): {
13
+ props: Props;
14
+ exports: {};
15
+ bindings: "";
16
+ slots: {};
17
+ events: {};
18
+ };
19
+ declare class __sveltets_Render<T extends object, IdType extends Primitive> {
20
+ props(): ReturnType<typeof $$render<T, IdType>>['props'];
21
+ events(): ReturnType<typeof $$render<T, IdType>>['events'];
22
+ slots(): ReturnType<typeof $$render<T, IdType>>['slots'];
23
+ bindings(): "";
24
+ exports(): {};
25
+ }
26
+ interface $$IsomorphicComponent {
27
+ new <T extends object, IdType extends Primitive>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<T, IdType>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<T, IdType>['props']>, ReturnType<__sveltets_Render<T, IdType>['events']>, ReturnType<__sveltets_Render<T, IdType>['slots']>> & {
28
+ $$bindings?: ReturnType<__sveltets_Render<T, IdType>['bindings']>;
29
+ } & ReturnType<__sveltets_Render<T, IdType>['exports']>;
30
+ <T extends object, IdType extends Primitive>(internal: unknown, props: ReturnType<__sveltets_Render<T, IdType>['props']> & {}): ReturnType<__sveltets_Render<T, IdType>['exports']>;
31
+ z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
32
+ }
33
+ declare const TableViewDropdown: $$IsomorphicComponent;
34
+ type TableViewDropdown<T extends object, IdType extends Primitive> = InstanceType<typeof TableViewDropdown<T, IdType>>;
35
+ export default TableViewDropdown;
@@ -237,48 +237,7 @@ export function makeFocusApi(ctx) {
237
237
  // Preferences
238
238
  // ─────────────────────────────────────────────────────────────────────────
239
239
  export function makePreferencesApi(ctx) {
240
- // Adapt the internal `ColumnDefSet<T>` shape to the public `SavedPreset`
241
- // shape. `ColumnDefSet` has no `savedAt` timestamp or per-column sizing
242
- // map, so `savedAt` is an epoch-zero placeholder and per-column `width`
243
- // falls back to the def's `minSize`.
244
- const adapt = (set) => ({
245
- name: set.name,
246
- // Literal placeholder, no reactivity needed.
247
- // eslint-disable-next-line svelte/prefer-svelte-reactivity
248
- savedAt: new Date(0).toISOString(),
249
- columns: set.columnDefs.map((c) => ({
250
- id: c.id,
251
- visible: set.visibilityState[c.id] !== false,
252
- width: c.minSize,
253
- pinned: set.columnPinningState.left?.has(c.id)
254
- ? 'left'
255
- : set.columnPinningState.right?.has(c.id)
256
- ? 'right'
257
- : 'none',
258
- })),
259
- });
260
240
  return Object.freeze({
261
- get presets() {
262
- return ctx._preferences.columnPresets.map(adapt);
263
- },
264
- get selectedPreset() {
265
- const sel = ctx._preferences.selectedPreset;
266
- return sel ? adapt(sel) : undefined;
267
- },
268
- savePreset(name) {
269
- ctx._preferences.saveColumnPreset(name);
270
- },
271
- applyPreset(preset) {
272
- const internal = ctx._preferences.columnPresets.find((p) => p.name === preset.name);
273
- if (internal) {
274
- ctx._preferences.setSelectedPreset(internal);
275
- }
276
- },
277
- deletePreset(name) {
278
- const next = ctx._preferences.columnPresets.filter((p) => p.name !== name);
279
- ctx._preferences.setPresets(next);
280
- ctx._preferences.persistPresets(next);
281
- },
282
241
  get settings() {
283
242
  return Object.freeze({
284
243
  isCompact: ctx.isCompact,
@@ -4,14 +4,14 @@ import type { VisibilityState } from './VisibilityState.js';
4
4
  /**
5
5
  * Persisted-to-localStorage snapshot of a table's *live* column layout -
6
6
  * the order / visibility / pinning / width state a user builds up by
7
- * dragging, hiding and resizing columns, as distinct from an explicitly
8
- * saved named preset (`JSONColumnDefSet`).
7
+ * dragging, hiding and resizing columns. This is the working copy the
8
+ * FPM 403 datatable-view machine measures dirtiness against.
9
9
  *
10
10
  * `TableContext` writes this automatically on every layout change and
11
11
  * re-applies it on the next mount, so a user's tweaks survive navigation
12
- * and reloads without having to save a preset - persisted until the
13
- * browser storage is cleared or the table's column set changes (schema
14
- * drift, which drops the stale snapshot).
12
+ * and reloads - persisted until the browser storage is cleared or the
13
+ * table's column set changes (schema drift, which drops the stale
14
+ * snapshot).
15
15
  *
16
16
  * Only ids and primitive maps are stored - never the column-def objects
17
17
  * themselves - so the snapshot stays small and is trivially validated.
@@ -50,8 +50,8 @@ export declare class ColumnLayoutState<T extends object> {
50
50
  *
51
51
  * Setter accepts any array of `ColumnDef<T>`; the parent
52
52
  * `TableContext` is responsible for index normalisation when
53
- * applying drag-drop or preset switches (see `reorderColumn`,
54
- * `moveColumnTo`, `setSelectedPreset`).
53
+ * applying drag-drop or a saved datatable-view layout (see
54
+ * `reorderColumn`, `moveColumnTo`, `_applyLayout`).
55
55
  */
56
56
  get columnDefs(): Array<ColumnDef<T>>;
57
57
  set columnDefs(v: Array<ColumnDef<T>>);
@@ -66,9 +66,9 @@ export declare class ColumnLayoutState<T extends object> {
66
66
  set columnSizing(v: ColumnSizingState);
67
67
  /**
68
68
  * Per-column pin state (left / right). Setter is exposed publicly
69
- * for direct assignment by the parent (e.g. `setSelectedPreset` and
70
- * the init flow); the gated `setColumnPinning` updater below honours
71
- * the `allowColumnReordering` master gate.
69
+ * for direct assignment by the parent (e.g. `_applyLayout` and the
70
+ * init flow); the gated `setColumnPinning` updater below honours the
71
+ * `allowColumnReordering` master gate.
72
72
  */
73
73
  get columnPinning(): ColumnPinningState;
74
74
  set columnPinning(v: ColumnPinningState);
@@ -34,8 +34,8 @@ export class ColumnLayoutState {
34
34
  *
35
35
  * Setter accepts any array of `ColumnDef<T>`; the parent
36
36
  * `TableContext` is responsible for index normalisation when
37
- * applying drag-drop or preset switches (see `reorderColumn`,
38
- * `moveColumnTo`, `setSelectedPreset`).
37
+ * applying drag-drop or a saved datatable-view layout (see
38
+ * `reorderColumn`, `moveColumnTo`, `_applyLayout`).
39
39
  */
40
40
  get columnDefs() {
41
41
  return this._columnDefs;
@@ -58,9 +58,9 @@ export class ColumnLayoutState {
58
58
  }
59
59
  /**
60
60
  * Per-column pin state (left / right). Setter is exposed publicly
61
- * for direct assignment by the parent (e.g. `setSelectedPreset` and
62
- * the init flow); the gated `setColumnPinning` updater below honours
63
- * the `allowColumnReordering` master gate.
61
+ * for direct assignment by the parent (e.g. `_applyLayout` and the
62
+ * init flow); the gated `setColumnPinning` updater below honours the
63
+ * `allowColumnReordering` master gate.
64
64
  */
65
65
  get columnPinning() {
66
66
  return this._columnPinning;