@lavalogic/scoria 0.38.10 → 0.38.12

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 (115) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +46 -46
  3. package/dist/Components/AccordionGroupButton.svelte +76 -76
  4. package/dist/Components/Action.svelte +136 -136
  5. package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +702 -702
  6. package/dist/Components/Base/BaseModal.svelte +125 -125
  7. package/dist/Components/Base/ContextWrapper.svelte +72 -72
  8. package/dist/Components/Base/Pagination.svelte +198 -198
  9. package/dist/Components/Base/Snippets.svelte +36 -36
  10. package/dist/Components/BigRadioSet.svelte +38 -38
  11. package/dist/Components/Bubble.svelte +101 -101
  12. package/dist/Components/Button.svelte +209 -209
  13. package/dist/Components/Checkbox.svelte +86 -86
  14. package/dist/Components/Contexts/ContextMenu.svelte +164 -164
  15. package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
  16. package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
  17. package/dist/Components/Contexts/ToastContainer.svelte +65 -65
  18. package/dist/Components/Contexts/Tooltip.svelte +37 -37
  19. package/dist/Components/DataMatrixIcon.svelte +112 -112
  20. package/dist/Components/DateInput.svelte +148 -148
  21. package/dist/Components/DatePicker.svelte +300 -300
  22. package/dist/Components/DesktopModal.svelte +90 -90
  23. package/dist/Components/Dial.svelte +204 -204
  24. package/dist/Components/DimensionInput.svelte +170 -170
  25. package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
  26. package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
  27. package/dist/Components/FileCard.svelte +24 -24
  28. package/dist/Components/FileUpload.svelte +236 -236
  29. package/dist/Components/GridInputSet.svelte +29 -29
  30. package/dist/Components/HorizontalTabGroup.svelte +82 -82
  31. package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
  32. package/dist/Components/Icon.svelte +128 -128
  33. package/dist/Components/Icons.js +357 -357
  34. package/dist/Components/LoadingAnimation.svelte +29 -29
  35. package/dist/Components/LoadingModal.svelte +45 -45
  36. package/dist/Components/LoadingOverlay.svelte +25 -25
  37. package/dist/Components/Modal.svelte +258 -258
  38. package/dist/Components/MultiSelect.svelte +220 -220
  39. package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
  40. package/dist/Components/Nav/NavTab.svelte +62 -62
  41. package/dist/Components/Nav/ServicesNav.svelte +80 -80
  42. package/dist/Components/NumberInput.svelte +157 -157
  43. package/dist/Components/PageHeading.svelte +25 -25
  44. package/dist/Components/PasswordInput.svelte +88 -88
  45. package/dist/Components/Portal.svelte +36 -36
  46. package/dist/Components/QuerySelect.svelte +366 -366
  47. package/dist/Components/SingleSelect.svelte +250 -250
  48. package/dist/Components/StepButton.svelte +70 -70
  49. package/dist/Components/StepForm.svelte +140 -140
  50. package/dist/Components/Switch.svelte +69 -69
  51. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
  52. package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
  53. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
  54. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +120 -120
  55. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
  56. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
  57. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
  58. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
  59. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
  60. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
  61. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
  62. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
  63. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
  64. package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
  65. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
  66. package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
  67. package/dist/Components/Table/Misc/FilterInput.svelte +37 -16
  68. package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
  69. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +71 -0
  70. package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
  71. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
  72. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +225 -0
  73. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte.d.ts +26 -0
  74. package/dist/Components/Table/Misc/UpdateViewOptionsModalProps.d.ts +21 -0
  75. package/dist/Components/Table/Misc/UpdateViewOptionsModalProps.js +1 -0
  76. package/dist/Components/Table/Types/Context/TableContext.svelte.d.ts +49 -1
  77. package/dist/Components/Table/Types/Context/TableContext.svelte.js +142 -1
  78. package/dist/Components/Table/Types/DataRepository/BuiltInRemoteRepository.svelte.d.ts +2 -2
  79. package/dist/Components/Table/Types/DataRepository/BuiltInRemoteRepository.svelte.js +20 -2
  80. package/dist/Components/Table/Types/DataRepository/IDataRepository.d.ts +9 -1
  81. package/dist/Components/Table/Types/DataRepository/ILocalDataRepository.d.ts +1 -1
  82. package/dist/Components/Table/Types/DataRepository/IRemoteDataRepository.d.ts +1 -1
  83. package/dist/Components/Table/Types/DataRepository/TableDataRepository.svelte.d.ts +1 -1
  84. package/dist/Components/Table/Types/Persistence/DatatableView.d.ts +25 -0
  85. package/dist/Components/Test/TestContextConsumer.svelte +13 -13
  86. package/dist/Components/TextArea.svelte +130 -130
  87. package/dist/Components/TextInput.svelte +176 -176
  88. package/dist/Components/ThreeStateRadio.svelte +131 -131
  89. package/dist/Components/Touch/BoolCard.svelte +45 -45
  90. package/dist/Components/Touch/DateModal.svelte +209 -209
  91. package/dist/Components/Touch/EditCard.svelte +59 -59
  92. package/dist/Components/Touch/InfoCard.svelte +31 -31
  93. package/dist/Components/Touch/InfoTextCard.svelte +28 -28
  94. package/dist/Components/Touch/ModalExplanation.svelte +17 -17
  95. package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
  96. package/dist/Components/Touch/NumberModal.svelte +79 -79
  97. package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
  98. package/dist/Components/Touch/SummaryCard.svelte +93 -93
  99. package/dist/Components/Touch/TextAreaModal.svelte +95 -95
  100. package/dist/Components/Touch/TextModal.svelte +106 -106
  101. package/dist/Components/Touch/TouchCard.svelte +33 -33
  102. package/dist/Components/Touch/TouchModal.svelte +274 -274
  103. package/dist/Components/Touch/UtilityButton.svelte +109 -109
  104. package/dist/Components/VerticalTabGroup.svelte +79 -79
  105. package/dist/Components/VerticalTabGroupButton.svelte +58 -58
  106. package/dist/Helpers/Datamatrix.d.ts +19 -19
  107. package/dist/Helpers/Datamatrix.js +291 -291
  108. package/dist/Types/Examples/ExampleModal.svelte +35 -35
  109. package/dist/index.d.ts +1 -1
  110. package/dist/scss/_basics.scss +12 -12
  111. package/dist/scss/_colours.scss +134 -134
  112. package/dist/scss/_motion.scss +57 -57
  113. package/dist/scss/_sizing.scss +85 -85
  114. package/dist/scss/_transitions.scss +8 -8
  115. package/package.json +1 -1
@@ -2191,10 +2191,33 @@ export class TableContext {
2191
2191
  // plain strings - look up cleanly against the live column set.
2192
2192
  // eslint-disable-next-line svelte/prefer-svelte-reactivity
2193
2193
  const liveColumnIds = new Set(this.columnDefs.map((def) => def.id));
2194
+ // 1. Clear the current filter VALUES. Without this, switching
2195
+ // saved filter views would only ADD entries from the new view
2196
+ // on top of whatever the old view set - any column the old
2197
+ // view filtered but the new one does not would keep its old
2198
+ // value and `FilterInput` would still show it.
2194
2199
  if (typeof repo.resetFilters === 'function') {
2195
2200
  repo.resetFilters();
2196
2201
  }
2197
- // Modes first: a value-bearing mode must be set before its value.
2202
+ // 2. Clear the current filter MODES the same way. `resetFilters`
2203
+ // deliberately keeps modes (so a "Clear filters" toolbar
2204
+ // action retains operator selections); but applying a saved
2205
+ // view is a *replace*, so a stale mode (e.g. the old view's
2206
+ // "Empty" operator) leaking into the new view would leave
2207
+ // `FilterInput`'s mode dropdown out of sync with the value it
2208
+ // renders.
2209
+ if (typeof repo.setFilterMode === 'function') {
2210
+ for (const id of [...repo.filterModes.keys()]) {
2211
+ repo.setFilterMode(id, undefined);
2212
+ }
2213
+ }
2214
+ // 3. Clear the QuerySelect typeahead-echo cache so the user
2215
+ // doesn't see a stale label hanging in a `QuerySelect` whose
2216
+ // underlying value just got reset. `_queryValues` is keyed by
2217
+ // `def.id`, the same key space `FilterInput` reads from.
2218
+ this._queryValues.clear();
2219
+ // 4. Apply the saved modes (before values so a value-bearing
2220
+ // mode like "Empty" sees its mode already set).
2198
2221
  if (typeof repo.setFilterMode === 'function') {
2199
2222
  for (const [id, mode] of Object.entries(filter.modes)) {
2200
2223
  // Drop stale column ids and unrecognised mode strings.
@@ -2203,6 +2226,7 @@ export class TableContext {
2203
2226
  }
2204
2227
  }
2205
2228
  }
2229
+ // 5. Apply the saved values.
2206
2230
  if (typeof repo.filterBy === 'function') {
2207
2231
  for (const entry of filter.filters) {
2208
2232
  if (liveColumnIds.has(entry.id)) {
@@ -2498,6 +2522,123 @@ export class TableContext {
2498
2522
  devCatch(e);
2499
2523
  }
2500
2524
  };
2525
+ /**
2526
+ * Save the live state of the table INTO an arbitrary saved view,
2527
+ * overwriting that view's stored body. Unlike `saveActiveView` -
2528
+ * which only works when the target IS the kind's active view -
2529
+ * this accepts any saved view: a "Save Layout" / "Save Filter"
2530
+ * button on a card can overwrite the saved view with the user's
2531
+ * current tweaks regardless of what the active view is.
2532
+ *
2533
+ * Flow:
2534
+ *
2535
+ * 1. capture the live layout / filter (chosen by `view.kind`);
2536
+ * 2. `await remoteLayouts.updateView({ id, body })`;
2537
+ * 3. swap the matching entry in that kind's saved-views list with
2538
+ * the authoritative server return;
2539
+ * 4. if the just-saved view IS the kind's currently active view,
2540
+ * repoint that kind's baseline at the new body and clear the
2541
+ * dirty flag - the dropdown immediately reads "<name>" instead
2542
+ * of "<name> (unsaved)".
2543
+ *
2544
+ * No-op (dev-logged) when remote saved views are disabled. Adapter
2545
+ * rejections are re-thrown so the caller can surface them in the
2546
+ * Table Configuration modal's inline error banner.
2547
+ */
2548
+ saveLiveStateIntoView = async (view) => {
2549
+ const adapter = this.remoteLayouts;
2550
+ if (!adapter) {
2551
+ if (dev) {
2552
+ console.warn('saveLiveStateIntoView: no adapter - cannot save remotely');
2553
+ }
2554
+ return;
2555
+ }
2556
+ const body = view.kind === 'filter'
2557
+ ? { kind: 'filter', filter: this._captureFilter() }
2558
+ : { kind: 'layout', layout: this._captureLayout() };
2559
+ const request = {
2560
+ id: view.id,
2561
+ body,
2562
+ };
2563
+ const updated = await adapter.updateView(request);
2564
+ // Swap the matching entry in the saved-views list so the picker
2565
+ // + modal reflect the new body without an extra refresh round-trip.
2566
+ this._viewState.setSavedViews(view.kind, this._viewState.savedViews(view.kind).map((v) => (v.id === updated.id ? updated : v)));
2567
+ // If the saved-into view IS the active view for its kind,
2568
+ // repoint the baseline so dirty re-compares against the new
2569
+ // body and clears.
2570
+ const active = this._viewState.activeView(view.kind);
2571
+ if (active.kind === 'saved' && active.id === updated.id) {
2572
+ this._viewState.selectSavedView(view.kind, { id: updated.id, name: updated.name });
2573
+ if (view.kind === 'filter') {
2574
+ this._filterBaseline =
2575
+ updated.body.kind === 'filter' ? updated.body.filter : this._captureFilter();
2576
+ this._persistActiveFilterView();
2577
+ }
2578
+ else {
2579
+ this._viewBaseline =
2580
+ updated.body.kind === 'layout' ? updated.body.layout : this._captureLayout();
2581
+ this._persistActiveView();
2582
+ }
2583
+ }
2584
+ };
2585
+ /**
2586
+ * Update a saved view's options - currently its `name` and
2587
+ * `visibility`. The view's body is left untouched (use
2588
+ * `saveLiveStateIntoView` or `saveActiveView` for that). Driven by
2589
+ * the "Update Options" modal on each My-* card.
2590
+ *
2591
+ * Flow:
2592
+ *
2593
+ * 1. `await remoteLayouts.updateView({ id, name?, visibility? })`;
2594
+ * 2. swap the matching entry in that kind's saved-views list with
2595
+ * the authoritative server return so the new name / visibility
2596
+ * are reflected in every reactive surface;
2597
+ * 3. if the renamed view IS the kind's currently active view, push
2598
+ * the new name onto the active-view ref so the quick-switch
2599
+ * dropdown and "active view" label update immediately.
2600
+ *
2601
+ * No-op (dev-logged) when remote saved views are disabled. Adapter
2602
+ * rejections are re-thrown so the caller can surface them in the
2603
+ * modal.
2604
+ */
2605
+ updateViewOptions = async (view, options) => {
2606
+ const adapter = this.remoteLayouts;
2607
+ if (!adapter) {
2608
+ if (dev) {
2609
+ console.warn('updateViewOptions: no adapter - cannot update remotely');
2610
+ }
2611
+ return;
2612
+ }
2613
+ // Skip the network round-trip entirely when nothing actually
2614
+ // changed. Avoids a no-op PATCH that would still bump the
2615
+ // updated_at timestamp on the backend.
2616
+ const nameChanged = options.name !== undefined && options.name !== view.name;
2617
+ const visibilityChanged = options.visibility !== undefined && options.visibility !== view.visibility;
2618
+ if (!nameChanged && !visibilityChanged) {
2619
+ return;
2620
+ }
2621
+ const request = {
2622
+ id: view.id,
2623
+ ...(nameChanged ? { name: options.name } : {}),
2624
+ ...(visibilityChanged ? { visibility: options.visibility } : {}),
2625
+ };
2626
+ const updated = await adapter.updateView(request);
2627
+ this._viewState.setSavedViews(view.kind, this._viewState.savedViews(view.kind).map((v) => (v.id === updated.id ? updated : v)));
2628
+ // If the just-edited view is the active one for its kind, push
2629
+ // the new name onto the active-view ref so the dropdown label
2630
+ // reflects the rename without waiting for a refresh.
2631
+ const active = this._viewState.activeView(view.kind);
2632
+ if (active.kind === 'saved' && active.id === updated.id) {
2633
+ this._viewState.selectSavedView(view.kind, { id: updated.id, name: updated.name });
2634
+ if (view.kind === 'filter') {
2635
+ this._persistActiveFilterView();
2636
+ }
2637
+ else {
2638
+ this._persistActiveView();
2639
+ }
2640
+ }
2641
+ };
2501
2642
  /**
2502
2643
  * Fetch the saved views for this datatable through the injected
2503
2644
  * `remoteLayouts` adapter and publish them into `_viewState`, split by
@@ -65,7 +65,7 @@ export declare class BuiltInRemoteRepository<TRow extends object> extends TableD
65
65
  private _debounceTimer;
66
66
  private _quickFilterFocusCount;
67
67
  constructor(_options: BuiltInRemoteRepositoryOptions<TRow>);
68
- readonly setFilterMode: (id: string, mode: FilterMode) => void;
68
+ readonly setFilterMode: (id: string, mode: FilterMode | undefined) => void;
69
69
  readonly filterBy: (id: string, value: unknown) => void;
70
70
  readonly resetFilters: () => void;
71
71
  /** Jumps to the given 1-based page number, clamped at >= 1. */
@@ -121,7 +121,7 @@ export declare class CustomRemoteRepository<TRow extends object> extends TableDa
121
121
  get filtering(): ColumnFiltersState;
122
122
  private _abortController;
123
123
  constructor(tableName: string, getUserId: () => string | undefined, _fetch: CustomRowSource<TRow>['fetch']);
124
- readonly setFilterMode: (id: string, mode: FilterMode) => void;
124
+ readonly setFilterMode: (id: string, mode: FilterMode | undefined) => void;
125
125
  readonly filterBy: (id: string, value: unknown) => void;
126
126
  readonly resetFilters: () => void;
127
127
  /** Jumps to the given 1-based page number, clamped at >= 1. */
@@ -91,7 +91,16 @@ export class BuiltInRemoteRepository extends TableDataRepository {
91
91
  }
92
92
  // ── IRemoteDataRepository surface ────────────────────────────────
93
93
  setFilterMode = (id, mode) => {
94
- this._filterModes.set(id, mode);
94
+ if (mode === undefined) {
95
+ // Drop the entry entirely so the column falls back to its
96
+ // kind's default mode on the next render. Used by
97
+ // `TableContext._applyFilter` to clear stale modes when a
98
+ // saved filter view is applied.
99
+ this._filterModes.delete(id);
100
+ }
101
+ else {
102
+ this._filterModes.set(id, mode);
103
+ }
95
104
  this.scheduleReload();
96
105
  };
97
106
  filterBy = (id, value) => {
@@ -366,7 +375,16 @@ export class CustomRemoteRepository extends TableDataRepository {
366
375
  this._fetch = _fetch;
367
376
  }
368
377
  setFilterMode = (id, mode) => {
369
- this._filterModes.set(id, mode);
378
+ if (mode === undefined) {
379
+ // Drop the entry entirely so the column falls back to its
380
+ // kind's default mode on the next render. Used by
381
+ // `TableContext._applyFilter` when restoring a saved filter
382
+ // view to clear modes left over from a prior view.
383
+ this._filterModes.delete(id);
384
+ }
385
+ else {
386
+ this._filterModes.set(id, mode);
387
+ }
370
388
  void this.reload();
371
389
  };
372
390
  filterBy = (id, value) => {
@@ -37,7 +37,15 @@ export interface IDataRepository<T extends object> {
37
37
  sorting: SortingState;
38
38
  filtering: ColumnFiltersState;
39
39
  filterModes: SvelteMap<string, FilterMode>;
40
- setFilterMode: (id: string, mode: FilterMode) => void;
40
+ /**
41
+ * Set the filter mode for column `id`. Pass `undefined` to delete the
42
+ * entry entirely (the column then falls back to its kind's default
43
+ * mode when the next `FilterInput` / `QuickSearchCell` renders).
44
+ * Used by `_applyFilter` when restoring a saved filter view, so any
45
+ * mode from a previously-applied view is dropped before the new
46
+ * one's modes are applied.
47
+ */
48
+ setFilterMode: (id: string, mode: FilterMode | undefined) => void;
41
49
  filterBy: (id: string, value: unknown) => void;
42
50
  dateFilter?: FilterFn<T, DateFilterValueType>;
43
51
  numberFilter?: FilterFn<T, NumberFilterValueType>;
@@ -21,7 +21,7 @@ export interface ILocalDataRepository<T extends object> extends IDataRepository<
21
21
  prevPage: () => void;
22
22
  filtering: ColumnFiltersState;
23
23
  filterModes: SvelteMap<string, FilterMode>;
24
- setFilterMode: (id: string, mode: FilterMode) => void;
24
+ setFilterMode: (id: string, mode: FilterMode | undefined) => void;
25
25
  filterBy: (id: string, value: unknown) => void;
26
26
  dateFilter: FilterFn<T, DateFilterValueType>;
27
27
  numberFilter: FilterFn<T, NumberFilterValueType>;
@@ -26,7 +26,7 @@ export interface IRemoteDataRepository<T extends object> extends IDataRepository
26
26
  prevPage: () => void;
27
27
  filtering: ColumnFiltersState;
28
28
  filterModes: SvelteMap<string, FilterMode>;
29
- setFilterMode: (id: string, mode: FilterMode) => void;
29
+ setFilterMode: (id: string, mode: FilterMode | undefined) => void;
30
30
  filterBy: (id: string, value: unknown) => void;
31
31
  resetFilters: () => void;
32
32
  lastPage: number;
@@ -42,7 +42,7 @@ export declare abstract class TableDataRepository<T extends object, out FilterVa
42
42
  */
43
43
  abstract setPageSize: (size: number | string) => void;
44
44
  abstract filtering: ColumnFiltersState<FilterValueType>;
45
- abstract setFilterMode: (id: string, mode: FilterMode) => void;
45
+ abstract setFilterMode: (id: string, mode: FilterMode | undefined) => void;
46
46
  abstract filterBy: (id: string, value: unknown) => void;
47
47
  abstract resetFilters: () => void;
48
48
  abstract setPage: (page: number) => void;
@@ -1,5 +1,15 @@
1
1
  import type { DatatableViewBody } from './DatatableViewEnvelope.js';
2
2
  import type { DatatableViewKind } from './DatatableViewKind.js';
3
+ /**
4
+ * Sharing visibility for a saved view. `'private'` is the default for
5
+ * v1: only the owner and explicit subscribers can see and apply it.
6
+ * `'public'` exposes the view to every user who has access to the
7
+ * datatable - they see it in the "Subscribe to Additional…" picker and
8
+ * can subscribe to it. scoria never interprets this token beyond
9
+ * passing it back through the adapter; the backend enforces the
10
+ * visibility semantics.
11
+ */
12
+ export type DatatableViewVisibility = 'private' | 'public';
3
13
  /**
4
14
  * A saved datatable view - a named, persisted column layout or filter
5
15
  * that round-trips through a `RemoteTableLayoutAdapter`. This is the
@@ -47,6 +57,15 @@ export interface DatatableView {
47
57
  * UI - scoria falls back to `userId` when it is absent.
48
58
  */
49
59
  subscribers?: ReadonlyArray<DatatableViewSubscriber>;
60
+ /**
61
+ * Sharing visibility. `'private'` (default) is only visible to the
62
+ * owner and explicit subscribers; `'public'` is offered to every
63
+ * user with datatable access through the "Subscribe to Additional…"
64
+ * picker. Optional on read for backwards compatibility - hosts that
65
+ * have not surfaced this column yet can omit it, and scoria falls
66
+ * back to private semantics in the UI.
67
+ */
68
+ visibility?: DatatableViewVisibility;
50
69
  }
51
70
  /**
52
71
  * One entry in a `DatatableView.subscribers` roster: an opaque user
@@ -81,6 +100,12 @@ export interface UpdateDatatableViewRequest {
81
100
  name?: string;
82
101
  /** New payload, if the layout/filter is being replaced. */
83
102
  body?: DatatableViewBody;
103
+ /**
104
+ * New sharing visibility, if it is being changed. `'private'` keeps
105
+ * the view scoped to its owner + subscribers; `'public'` exposes it
106
+ * to every user with datatable access.
107
+ */
108
+ visibility?: DatatableViewVisibility;
84
109
  }
85
110
  /**
86
111
  * A subscriber relationship for a shared `DatatableView`. v1 sharing is
@@ -1,13 +1,13 @@
1
- <svelte:options runes />
2
-
3
- <script lang="ts">
4
- import { TestContext } from '../../Contexts/TestContext.svelte.js';
5
- import { getContext } from 'svelte';
6
-
7
- const testContext = getContext<TestContext>(TestContext.identifier);
8
- </script>
9
-
10
- <h1>counter 1: {testContext.value1.value}</h1>
11
- <p>Ref wrapper 1 is incremented every time you click the button</p>
12
- <h2>counter 2: {testContext.value2.value}</h2>
13
- <p>ref wrapper 2 adds ref wrapper 1's value every time ref wrapper 1's value is divisible by 3</p>
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { TestContext } from '../../Contexts/TestContext.svelte.js';
5
+ import { getContext } from 'svelte';
6
+
7
+ const testContext = getContext<TestContext>(TestContext.identifier);
8
+ </script>
9
+
10
+ <h1>counter 1: {testContext.value1.value}</h1>
11
+ <p>Ref wrapper 1 is incremented every time you click the button</p>
12
+ <h2>counter 2: {testContext.value2.value}</h2>
13
+ <p>ref wrapper 2 adds ref wrapper 1's value every time ref wrapper 1's value is divisible by 3</p>
@@ -1,132 +1,132 @@
1
- <svelte:options runes />
2
-
3
- <script lang="ts">
4
- import { cssLength } from '../Helpers/Helpers.svelte.js';
5
- import { InputVariant } from '../Types/Internal/InputVariant.js';
6
- import { Size } from '../Types/Internal/Size.js';
7
- import Icon from './Icon.svelte';
8
- import type { TextAreaProps } from './TextAreaProps.js';
9
-
10
- let {
11
- value = $bindable(''),
12
- labelTop,
13
- labelRight,
14
- placeholder,
15
- maxWidth = false,
16
- required = false,
17
- variant = InputVariant.Default,
18
- selectNone = false,
19
- noresize = false,
20
- uppercase = false,
21
- disabled,
22
- invalid = false,
23
- showErrorColours = true,
24
- optional = false,
25
- leftIcon,
26
- rightIcon,
27
- onLeftIconClick,
28
- onRightIconClick,
29
- rows = 4,
30
-
31
- textAreaRef = $bindable(),
32
- autocomplete,
33
- spellcheck,
34
- onblur,
35
- onfocus,
36
- // No default `onkeydown` - see TextInput.svelte for the rationale.
37
- // The legacy default (`passthrough`) blurred the textarea on every
38
- // alphanumeric keystroke and made the field unusable.
39
- onkeydown,
40
- oninput,
41
- }: TextAreaProps = $props();
42
-
43
- const isInvalid = $derived(showErrorColours && ((required && value.trim() === '') || invalid));
44
- </script>
45
-
46
- <label
47
- class:for-modal={variant === InputVariant.Modal}
48
- class:max-width={maxWidth}
49
- >
50
- {#if labelTop && variant !== InputVariant.Modal}
51
- <span
52
- class="top-label"
53
- class:for-default={variant === InputVariant.Default}
54
- class:invalid={isInvalid}
55
- >{labelTop}{#if optional}<i>&nbsp;- optional</i>{/if}</span
56
- >
57
- {#if variant === InputVariant.Default}
58
- <span
59
- aria-hidden="true"
60
- class="border"
61
- class:invalid={isInvalid}
62
- >{labelTop}{#if optional}<i>&nbsp;- optional</i>{/if}</span
63
- >
64
- {/if}
65
- {/if}
66
- <div
67
- class="icon-container"
68
- class:for-modal={variant == InputVariant.Modal}
69
- >
70
- {#if leftIcon}
71
- <Icon
72
- name={leftIcon.name}
73
- size={leftIcon.size ?? Size.Medium}
74
- colour={leftIcon.colour}
75
- float
76
- left={cssLength('0.75rem')}
77
- top={cssLength('50%')}
78
- anchorX={cssLength('0')}
79
- anchorY={cssLength('-50%')}
80
- clickable={!!onLeftIconClick}
81
- onclick={onLeftIconClick}
82
- />
83
- {/if}
84
- <textarea
85
- class:noresize
86
- class:uppercase
87
- class:invalid={isInvalid}
88
- class:select-none={selectNone}
89
- class:left-pad={leftIcon != undefined}
90
- class:right-pad={rightIcon != undefined}
91
- class:for-modal={variant == InputVariant.Modal}
92
- class:max-width={maxWidth}
93
- autocomplete={autocomplete ?? 'off'}
94
- spellcheck={spellcheck ?? true}
95
- aria-invalid={isInvalid || undefined}
96
- bind:value
97
- {onblur}
98
- {onfocus}
99
- {onkeydown}
100
- {oninput}
101
- {rows}
102
- bind:this={textAreaRef}
103
- {placeholder}
104
- {required}
105
- {disabled}
106
- ></textarea>
107
- {#if rightIcon}
108
- <Icon
109
- name={rightIcon.name}
110
- size={rightIcon.size ?? Size.Medium}
111
- colour={rightIcon.colour}
112
- float
113
- right={cssLength(variant == InputVariant.Modal ? '1rem' : '0.5rem')}
114
- top={cssLength('50%')}
115
- anchorX={cssLength('0')}
116
- anchorY={cssLength('-50%')}
117
- clickable={!!onRightIconClick}
118
- onclick={onRightIconClick}
119
- />
120
- {/if}
121
- {#if labelRight}
122
- <span
123
- class="right-label"
124
- class:for-modal={variant == InputVariant.Modal}>{labelRight}</span
125
- >
126
- {/if}
127
- </div>
128
- </label>
129
-
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">
4
+ import { cssLength } from '../Helpers/Helpers.svelte.js';
5
+ import { InputVariant } from '../Types/Internal/InputVariant.js';
6
+ import { Size } from '../Types/Internal/Size.js';
7
+ import Icon from './Icon.svelte';
8
+ import type { TextAreaProps } from './TextAreaProps.js';
9
+
10
+ let {
11
+ value = $bindable(''),
12
+ labelTop,
13
+ labelRight,
14
+ placeholder,
15
+ maxWidth = false,
16
+ required = false,
17
+ variant = InputVariant.Default,
18
+ selectNone = false,
19
+ noresize = false,
20
+ uppercase = false,
21
+ disabled,
22
+ invalid = false,
23
+ showErrorColours = true,
24
+ optional = false,
25
+ leftIcon,
26
+ rightIcon,
27
+ onLeftIconClick,
28
+ onRightIconClick,
29
+ rows = 4,
30
+
31
+ textAreaRef = $bindable(),
32
+ autocomplete,
33
+ spellcheck,
34
+ onblur,
35
+ onfocus,
36
+ // No default `onkeydown` - see TextInput.svelte for the rationale.
37
+ // The legacy default (`passthrough`) blurred the textarea on every
38
+ // alphanumeric keystroke and made the field unusable.
39
+ onkeydown,
40
+ oninput,
41
+ }: TextAreaProps = $props();
42
+
43
+ const isInvalid = $derived(showErrorColours && ((required && value.trim() === '') || invalid));
44
+ </script>
45
+
46
+ <label
47
+ class:for-modal={variant === InputVariant.Modal}
48
+ class:max-width={maxWidth}
49
+ >
50
+ {#if labelTop && variant !== InputVariant.Modal}
51
+ <span
52
+ class="top-label"
53
+ class:for-default={variant === InputVariant.Default}
54
+ class:invalid={isInvalid}
55
+ >{labelTop}{#if optional}<i>&nbsp;- optional</i>{/if}</span
56
+ >
57
+ {#if variant === InputVariant.Default}
58
+ <span
59
+ aria-hidden="true"
60
+ class="border"
61
+ class:invalid={isInvalid}
62
+ >{labelTop}{#if optional}<i>&nbsp;- optional</i>{/if}</span
63
+ >
64
+ {/if}
65
+ {/if}
66
+ <div
67
+ class="icon-container"
68
+ class:for-modal={variant == InputVariant.Modal}
69
+ >
70
+ {#if leftIcon}
71
+ <Icon
72
+ name={leftIcon.name}
73
+ size={leftIcon.size ?? Size.Medium}
74
+ colour={leftIcon.colour}
75
+ float
76
+ left={cssLength('0.75rem')}
77
+ top={cssLength('50%')}
78
+ anchorX={cssLength('0')}
79
+ anchorY={cssLength('-50%')}
80
+ clickable={!!onLeftIconClick}
81
+ onclick={onLeftIconClick}
82
+ />
83
+ {/if}
84
+ <textarea
85
+ class:noresize
86
+ class:uppercase
87
+ class:invalid={isInvalid}
88
+ class:select-none={selectNone}
89
+ class:left-pad={leftIcon != undefined}
90
+ class:right-pad={rightIcon != undefined}
91
+ class:for-modal={variant == InputVariant.Modal}
92
+ class:max-width={maxWidth}
93
+ autocomplete={autocomplete ?? 'off'}
94
+ spellcheck={spellcheck ?? true}
95
+ aria-invalid={isInvalid || undefined}
96
+ bind:value
97
+ {onblur}
98
+ {onfocus}
99
+ {onkeydown}
100
+ {oninput}
101
+ {rows}
102
+ bind:this={textAreaRef}
103
+ {placeholder}
104
+ {required}
105
+ {disabled}
106
+ ></textarea>
107
+ {#if rightIcon}
108
+ <Icon
109
+ name={rightIcon.name}
110
+ size={rightIcon.size ?? Size.Medium}
111
+ colour={rightIcon.colour}
112
+ float
113
+ right={cssLength(variant == InputVariant.Modal ? '1rem' : '0.5rem')}
114
+ top={cssLength('50%')}
115
+ anchorX={cssLength('0')}
116
+ anchorY={cssLength('-50%')}
117
+ clickable={!!onRightIconClick}
118
+ onclick={onRightIconClick}
119
+ />
120
+ {/if}
121
+ {#if labelRight}
122
+ <span
123
+ class="right-label"
124
+ class:for-modal={variant == InputVariant.Modal}>{labelRight}</span
125
+ >
126
+ {/if}
127
+ </div>
128
+ </label>
129
+
130
130
  <style>textarea {
131
131
  box-sizing: border-box;
132
132
  height: 100%;
@@ -340,4 +340,4 @@ span.border.invalid {
340
340
  .textarea-error-wrapper.for-modal {
341
341
  position: relative;
342
342
  height: 100%;
343
- }</style>
343
+ }</style>