@lavalogic/scoria 0.40.13 → 0.40.15

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 (143) hide show
  1. package/README.md +58 -58
  2. package/dist/Components/AccordionGroup.svelte +35 -46
  3. package/dist/Components/AccordionGroupButton.svelte +68 -76
  4. package/dist/Components/Action.svelte +103 -135
  5. package/dist/Components/AlertModal.svelte +171 -190
  6. package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +614 -697
  7. package/dist/Components/Base/BaseModal.svelte +90 -116
  8. package/dist/Components/Base/ContextWrapper.svelte +67 -72
  9. package/dist/Components/Base/Pagination.svelte +173 -196
  10. package/dist/Components/Base/Snippets.svelte +34 -36
  11. package/dist/Components/BigRadioSet.svelte +36 -38
  12. package/dist/Components/Bubble.svelte +78 -101
  13. package/dist/Components/Button.svelte +175 -208
  14. package/dist/Components/Checkbox.svelte +75 -86
  15. package/dist/Components/CollapsibleCard.svelte +72 -87
  16. package/dist/Components/CollapsibleMenuGroup.svelte +53 -57
  17. package/dist/Components/ConditionBuilder/ConditionBuilder.svelte +39 -64
  18. package/dist/Components/Contexts/ContextMenu.svelte +141 -156
  19. package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
  20. package/dist/Components/Contexts/ContextMenuOption.svelte +53 -60
  21. package/dist/Components/Contexts/Toast.svelte +81 -86
  22. package/dist/Components/Contexts/ToastContainer.svelte +58 -62
  23. package/dist/Components/Contexts/Tooltip.svelte +36 -37
  24. package/dist/Components/DataMatrixIcon.svelte +95 -108
  25. package/dist/Components/DateInput.svelte +118 -146
  26. package/dist/Components/DatePicker.svelte +255 -294
  27. package/dist/Components/DesktopModal.svelte +70 -90
  28. package/dist/Components/Dial.svelte +167 -197
  29. package/dist/Components/DimensionInput.svelte +124 -169
  30. package/dist/Components/DragMenu/DragMenuHolder.svelte +98 -114
  31. package/dist/Components/DragMenu/DraggableCard.svelte +64 -76
  32. package/dist/Components/FileCard.svelte +21 -24
  33. package/dist/Components/FileUpload.svelte +190 -225
  34. package/dist/Components/GridInput.svelte +159 -196
  35. package/dist/Components/GridInputSet.svelte +24 -29
  36. package/dist/Components/HorizontalTabGroup.svelte +73 -89
  37. package/dist/Components/HorizontalTabGroupButton.svelte +40 -44
  38. package/dist/Components/Icon.svelte +99 -128
  39. package/dist/Components/Icons.js +357 -357
  40. package/dist/Components/InfoAlert.svelte +35 -38
  41. package/dist/Components/LoadingAnimation.svelte +691 -755
  42. package/dist/Components/LoadingModal.svelte +36 -44
  43. package/dist/Components/LoadingOverlay.svelte +22 -26
  44. package/dist/Components/Modal.svelte +204 -254
  45. package/dist/Components/MultiSelect.svelte +186 -216
  46. package/dist/Components/Nav/Nav.svelte +94 -110
  47. package/dist/Components/Nav/NavServiceMenuList.svelte +57 -61
  48. package/dist/Components/Nav/NavTab.svelte +58 -62
  49. package/dist/Components/Nav/ServiceMenuItem.svelte +103 -111
  50. package/dist/Components/Nav/ServicesNav.svelte +72 -79
  51. package/dist/Components/NumberInput.svelte +121 -155
  52. package/dist/Components/OptionCards.svelte +62 -73
  53. package/dist/Components/PageHeading.svelte +23 -25
  54. package/dist/Components/PasswordInput.svelte +69 -88
  55. package/dist/Components/PopoverButton.svelte +151 -0
  56. package/dist/Components/PopoverButton.svelte.d.ts +9 -0
  57. package/dist/Components/PopoverButtonProps.d.ts +21 -0
  58. package/dist/Components/PopoverButtonProps.js +1 -0
  59. package/dist/Components/Portal.svelte +29 -35
  60. package/dist/Components/ProgressBubble.svelte +91 -106
  61. package/dist/Components/QuerySelect.svelte +260 -346
  62. package/dist/Components/SidebarPanel.svelte +34 -36
  63. package/dist/Components/SingleSelect.svelte +196 -244
  64. package/dist/Components/StepButton.svelte +65 -70
  65. package/dist/Components/StepForm.svelte +115 -135
  66. package/dist/Components/Switch.svelte +57 -69
  67. package/dist/Components/Table/ARCHITECTURE.md +298 -298
  68. package/dist/Components/Table/Body/QuickSearchCell.svelte +578 -655
  69. package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +42 -51
  70. package/dist/Components/Table/Body/Rows/ColumnList.svelte +42 -58
  71. package/dist/Components/Table/Body/Rows/ColumnListRow.svelte +403 -442
  72. package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +51 -78
  73. package/dist/Components/Table/Body/Rows/Columns/ActionsCell.svelte +165 -184
  74. package/dist/Components/Table/Body/Rows/Columns/BubbleCell.svelte +110 -123
  75. package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +134 -160
  76. package/dist/Components/Table/Body/Rows/Columns/FocusableImmutableCell.svelte +190 -231
  77. package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +82 -104
  78. package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +52 -87
  79. package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +122 -150
  80. package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +132 -159
  81. package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +190 -236
  82. package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +173 -211
  83. package/dist/Components/Table/Body/Rows/Columns/TableQuerySelect.svelte +204 -244
  84. package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +140 -177
  85. package/dist/Components/Table/Body/Rows/Columns/TableSelect.svelte +188 -217
  86. package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +173 -193
  87. package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +166 -187
  88. package/dist/Components/Table/Body/Rows/TableRow.svelte +245 -288
  89. package/dist/Components/Table/Body/TableBody.svelte +141 -154
  90. package/dist/Components/Table/Headers/HighlightAllHeader.svelte +35 -41
  91. package/dist/Components/Table/Headers/SelectAllHeader.svelte +24 -30
  92. package/dist/Components/Table/Headers/TableHead.svelte +348 -402
  93. package/dist/Components/Table/Misc/ColumnPanel.svelte +37 -39
  94. package/dist/Components/Table/Misc/ColumnPanelModal.svelte +35 -39
  95. package/dist/Components/Table/Misc/FilterInput.svelte +556 -619
  96. package/dist/Components/Table/Misc/FilterPanel.svelte +170 -183
  97. package/dist/Components/Table/Misc/FilterPanelModal.svelte +25 -31
  98. package/dist/Components/Table/Misc/ShareViewModal.svelte +252 -281
  99. package/dist/Components/Table/Misc/SidePanel.svelte +32 -35
  100. package/dist/Components/Table/Misc/TableConfigurationModal.svelte +574 -629
  101. package/dist/Components/Table/Misc/TableFooter.svelte +326 -332
  102. package/dist/Components/Table/Misc/TableHorizontalBar.svelte +134 -140
  103. package/dist/Components/Table/Misc/TableSidebar.svelte +183 -198
  104. package/dist/Components/Table/Misc/TableViewDropdown.svelte +83 -103
  105. package/dist/Components/Table/Misc/ToolboxPanel.svelte +83 -100
  106. package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +88 -107
  107. package/dist/Components/Table/Misc/UpdateViewOptionsModal.svelte +163 -190
  108. package/dist/Components/Table/NestedTable.svelte +102 -128
  109. package/dist/Components/Table/Table.svelte +531 -663
  110. package/dist/Components/Test/TestContextConsumer.svelte +11 -13
  111. package/dist/Components/TextArea.svelte +95 -130
  112. package/dist/Components/TextInput.svelte +125 -176
  113. package/dist/Components/ThreeStateRadio.svelte +111 -130
  114. package/dist/Components/Touch/BoolCard.svelte +33 -45
  115. package/dist/Components/Touch/DateModal.svelte +164 -204
  116. package/dist/Components/Touch/EditCard.svelte +48 -59
  117. package/dist/Components/Touch/InfoCard.svelte +28 -31
  118. package/dist/Components/Touch/InfoTextCard.svelte +26 -28
  119. package/dist/Components/Touch/ModalExplanation.svelte +15 -17
  120. package/dist/Components/Touch/NumberKeyboard.svelte +181 -218
  121. package/dist/Components/Touch/NumberModal.svelte +59 -79
  122. package/dist/Components/Touch/PlusMinusCard.svelte +43 -46
  123. package/dist/Components/Touch/SelectModal.svelte +111 -126
  124. package/dist/Components/Touch/SummaryCard.svelte +80 -93
  125. package/dist/Components/Touch/TextAreaModal.svelte +72 -94
  126. package/dist/Components/Touch/TextModal.svelte +77 -105
  127. package/dist/Components/Touch/TouchCard.svelte +31 -33
  128. package/dist/Components/Touch/TouchModal.svelte +214 -270
  129. package/dist/Components/Touch/UtilityButton.svelte +88 -109
  130. package/dist/Components/VerticalTabGroup.svelte +60 -78
  131. package/dist/Components/VerticalTabGroupButton.svelte +54 -58
  132. package/dist/Helpers/Datamatrix.d.ts +19 -19
  133. package/dist/Helpers/Datamatrix.js +291 -291
  134. package/dist/Types/Examples/ExampleModal.svelte +31 -35
  135. package/dist/index.d.ts +2 -0
  136. package/dist/index.js +1 -0
  137. package/dist/scss/_basics.scss +12 -12
  138. package/dist/scss/_colours.scss +134 -134
  139. package/dist/scss/_mixins.scss +3392 -3392
  140. package/dist/scss/_motion.scss +57 -57
  141. package/dist/scss/_sizing.scss +85 -85
  142. package/dist/scss/_transitions.scss +8 -8
  143. package/package.json +1 -1
@@ -1,64 +1,60 @@
1
- <svelte:options runes />
2
-
3
- <script lang="ts">
4
- import { page } from '$app/state';
5
- import { Colour } from '../../scss/colours.js';
6
- import { Size } from '../../Types/Internal/Size.js';
7
- import Icon from '../Icon.svelte';
8
- import { Dimensions } from './Dimensions.js';
9
- import type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
10
- import ServiceMenuItem from './ServiceMenuItem.svelte';
11
-
12
- const { title, items, icon }: NavServiceMenuListProps = $props();
13
-
14
- // Only the single most-specific item is active: the longest item path that
15
- // the current URL matches. Without this a nested route (e.g.
16
- // /reports/manage-reports) would also light up its parent item (/reports).
17
- const activePath = $derived.by(() => {
18
- const pathname = page.url.pathname;
19
- let best: string | null = null;
20
- for (const { item } of items) {
21
- const path = item.path;
22
- if (path && pathname.includes(path) && (best === null || path.length > best.length)) {
23
- best = path;
24
- }
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">import { page } from "$app/state";
4
+ import { Colour } from "../../scss/colours.js";
5
+ import { Size } from "../../Types/Internal/Size.js";
6
+ import Icon from "../Icon.svelte";
7
+ import { Dimensions } from "./Dimensions.js";
8
+ import ServiceMenuItem from "./ServiceMenuItem.svelte";
9
+ const { title, items, icon } = $props();
10
+ // Only the single most-specific item is active: the longest item path that
11
+ // the current URL matches. Without this a nested route (e.g.
12
+ // /reports/manage-reports) would also light up its parent item (/reports).
13
+ const activePath = $derived.by(() => {
14
+ const pathname = page.url.pathname;
15
+ let best = null;
16
+ for (const { item } of items) {
17
+ const path = item.path;
18
+ if (path && pathname.includes(path) && (best === null || path.length > best.length)) {
19
+ best = path;
25
20
  }
26
- return best;
27
- });
28
- </script>
29
-
30
- <div
31
- class="wrapper"
32
- style="--icon-column-width: {Dimensions.smallPanelWidth}rem"
33
- role="group"
34
- aria-label={title}
35
- >
36
- <h3>
37
- <div class="icon">
38
- {#if icon}
39
- <Icon
40
- size={Size.Large}
41
- colour={Colour['$font-primary']}
42
- {...icon}
43
- />
44
- {/if}
45
- </div>
46
- {title}
47
- </h3>
48
-
49
- <div
50
- class="items"
51
- role="list"
52
- >
53
- {#each items as { item } (item.label)}
54
- <ServiceMenuItem
55
- {item}
56
- selected={item.path != null && item.path === activePath}
57
- />
58
- {/each}
59
- </div>
60
- </div>
61
-
21
+ }
22
+ return best;
23
+ });
24
+ </script>
25
+
26
+ <div
27
+ class="wrapper"
28
+ style="--icon-column-width: {Dimensions.smallPanelWidth}rem"
29
+ role="group"
30
+ aria-label={title}
31
+ >
32
+ <h3>
33
+ <div class="icon">
34
+ {#if icon}
35
+ <Icon
36
+ size={Size.Large}
37
+ colour={Colour['$font-primary']}
38
+ {...icon}
39
+ />
40
+ {/if}
41
+ </div>
42
+ {title}
43
+ </h3>
44
+
45
+ <div
46
+ class="items"
47
+ role="list"
48
+ >
49
+ {#each items as { item } (item.label)}
50
+ <ServiceMenuItem
51
+ {item}
52
+ selected={item.path != null && item.path === activePath}
53
+ />
54
+ {/each}
55
+ </div>
56
+ </div>
57
+
62
58
  <style>h3 {
63
59
  font-size: 1.5rem;
64
60
  display: flex;
@@ -82,4 +78,4 @@ h3 .icon {
82
78
  .wrapper .items {
83
79
  flex-shrink: 1;
84
80
  overflow: auto;
85
- }</style>
81
+ }</style>
@@ -1,64 +1,60 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- generics="Args"
6
- >
7
- import { useAsyncFlag } from '../../Helpers/AsyncFlag.svelte.js';
8
- import { Colour } from '../../scss/colours.js';
9
- import { Size } from '../../Types/Internal/Size.js';
10
- import Icon from '../Icon.svelte';
11
- import type { NavTabProps } from './NavTabProps.js';
12
-
13
- // `Args` defaults to `unknown` on the props interface, but we still
14
- // pass the generic explicitly so consumers using the component as
15
- // `<NavTab>` inside a typed snippet receive the same `Args` rune.
16
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
17
- let { option, selected }: NavTabProps<Args> = $props();
18
-
19
- const isValid = useAsyncFlag(() => option.isValid, true);
20
- const disabled = useAsyncFlag(() => option.disabled, false);
21
- const visible = useAsyncFlag(() => option.visible, true);
22
- </script>
23
-
24
- <button
25
- class:selected
26
- onclick={() => {
27
- option.onclick();
28
- }}
29
- type="button"
30
- class="tab"
31
- role="tab"
32
- aria-selected={selected}
33
- aria-label={option.label}
34
- disabled={disabled.value}
35
- class:invalid={!isValid.value}
36
- class:hidden={!visible.value}
37
- >
38
- <Icon
39
- name="nothing"
40
- colour={Colour['$brand-primary']}
41
- size={Size.MediumLarge}
42
- {...option.iconLeft}
43
- />
44
-
45
- {option.label}
46
- {#if option.isValid === false}
47
- <Icon
48
- name="error"
49
- colour={Colour['$error-border']}
50
- size={Size.MediumLarge}
51
- />
52
- {:else}
53
- <Icon
54
- name="nothing"
55
- colour={Colour['$brand-primary']}
56
- size={Size.MediumLarge}
57
- {...option.iconRight}
58
- />
59
- {/if}
60
- </button>
61
-
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="Args"
6
+ >import { useAsyncFlag } from "../../Helpers/AsyncFlag.svelte.js";
7
+ import { Colour } from "../../scss/colours.js";
8
+ import { Size } from "../../Types/Internal/Size.js";
9
+ import Icon from "../Icon.svelte";
10
+ // `Args` defaults to `unknown` on the props interface, but we still
11
+ // pass the generic explicitly so consumers using the component as
12
+ // `<NavTab>` inside a typed snippet receive the same `Args` rune.
13
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
14
+ let { option, selected } = $props();
15
+ const isValid = useAsyncFlag(() => option.isValid, true);
16
+ const disabled = useAsyncFlag(() => option.disabled, false);
17
+ const visible = useAsyncFlag(() => option.visible, true);
18
+ </script>
19
+
20
+ <button
21
+ class:selected
22
+ onclick={() => {
23
+ option.onclick();
24
+ }}
25
+ type="button"
26
+ class="tab"
27
+ role="tab"
28
+ aria-selected={selected}
29
+ aria-label={option.label}
30
+ disabled={disabled.value}
31
+ class:invalid={!isValid.value}
32
+ class:hidden={!visible.value}
33
+ >
34
+ <Icon
35
+ name="nothing"
36
+ colour={Colour['$brand-primary']}
37
+ size={Size.MediumLarge}
38
+ {...option.iconLeft}
39
+ />
40
+
41
+ {option.label}
42
+ {#if option.isValid === false}
43
+ <Icon
44
+ name="error"
45
+ colour={Colour['$error-border']}
46
+ size={Size.MediumLarge}
47
+ />
48
+ {:else}
49
+ <Icon
50
+ name="nothing"
51
+ colour={Colour['$brand-primary']}
52
+ size={Size.MediumLarge}
53
+ {...option.iconRight}
54
+ />
55
+ {/if}
56
+ </button>
57
+
62
58
  <style>button.tab.hidden {
63
59
  display: none !important;
64
60
  }
@@ -91,4 +87,4 @@
91
87
  }
92
88
  .tab.invalid {
93
89
  color: #aa1414;
94
- }</style>
90
+ }</style>
@@ -1,114 +1,106 @@
1
- <svelte:options runes />
2
-
3
- <script lang="ts">
4
- import { dev } from '$app/environment';
5
- import { resolve } from '$app/paths';
6
- import { page } from '$app/state';
7
- import { useAsyncFlag } from '../../Helpers/AsyncFlag.svelte.js';
8
- import { Size } from '../../Types/Internal/Size.js';
9
- import Icon from '../Icon.svelte';
10
- import { Dimensions } from './Dimensions.js';
11
- import type { SideMenuItemProps } from './SideMenuItemProps.js';
12
-
13
- const { item, selected: maybeSelected }: SideMenuItemProps = $props();
14
-
15
- // `page.url.pathname` carries the configured base path; `item.path`
16
- // does not. Match with `includes` so the comparison holds whether or
17
- // not a base path (e.g. a `URI_PREFIX` build arg) is set.
18
- const selected = $derived(
19
- maybeSelected ?? (item.path ? page.url.pathname.includes(item.path) : false)
20
- );
21
-
22
- const visible = useAsyncFlag(() => item.visible, true);
23
-
24
- /**
25
- * Constrain the rendered `href` to safe, relative paths only, then
26
- * prefix the SvelteKit `base` path.
27
- *
28
- * `item.path` is typed as the SvelteKit `Pathname` brand at the
29
- * boundary, but the property is still a `string` at runtime. To
30
- * keep the trust boundary tight we reject anything that contains
31
- * a URL scheme (anything matching `^[a-z][a-z0-9+.-]*:`) and any
32
- * input that does not start with `/`. Disallowed values resolve
33
- * to `#` so the link is harmless if a future consumer supplies an
34
- * unsafe value.
35
- *
36
- * The validated path is run through `resolve` so the rendered `href`
37
- * honours a configured base path (e.g. a `URI_PREFIX` build arg).
38
- * `item.path` is the bare app-relative route; without the prefix the
39
- * `<a href>` would point outside the deployed sub-path.
40
- */
41
- function safeHref(path: string): string {
42
- if (typeof path !== 'string' || path.length === 0) {
43
- return '#';
44
- }
45
- if (!path.startsWith('/')) {
46
- return '#';
47
- }
48
- if (/^[a-z][a-z0-9+.-]*:/i.test(path)) {
49
- return '#';
50
- }
51
- return resolve(path as '/');
1
+ <svelte:options runes />
2
+
3
+ <script lang="ts">import { dev } from "$app/environment";
4
+ import { resolve } from "$app/paths";
5
+ import { page } from "$app/state";
6
+ import { useAsyncFlag } from "../../Helpers/AsyncFlag.svelte.js";
7
+ import { Size } from "../../Types/Internal/Size.js";
8
+ import Icon from "../Icon.svelte";
9
+ import { Dimensions } from "./Dimensions.js";
10
+ const { item, selected: maybeSelected } = $props();
11
+ // `page.url.pathname` carries the configured base path; `item.path`
12
+ // does not. Match with `includes` so the comparison holds whether or
13
+ // not a base path (e.g. a `URI_PREFIX` build arg) is set.
14
+ const selected = $derived(maybeSelected ?? (item.path ? page.url.pathname.includes(item.path) : false));
15
+ const visible = useAsyncFlag(() => item.visible, true);
16
+ /**
17
+ * Constrain the rendered `href` to safe, relative paths only, then
18
+ * prefix the SvelteKit `base` path.
19
+ *
20
+ * `item.path` is typed as the SvelteKit `Pathname` brand at the
21
+ * boundary, but the property is still a `string` at runtime. To
22
+ * keep the trust boundary tight we reject anything that contains
23
+ * a URL scheme (anything matching `^[a-z][a-z0-9+.-]*:`) and any
24
+ * input that does not start with `/`. Disallowed values resolve
25
+ * to `#` so the link is harmless if a future consumer supplies an
26
+ * unsafe value.
27
+ *
28
+ * The validated path is run through `resolve` so the rendered `href`
29
+ * honours a configured base path (e.g. a `URI_PREFIX` build arg).
30
+ * `item.path` is the bare app-relative route; without the prefix the
31
+ * `<a href>` would point outside the deployed sub-path.
32
+ */
33
+ function safeHref(path) {
34
+ if (typeof path !== "string" || path.length === 0) {
35
+ return "#";
52
36
  }
53
- </script>
54
-
55
- {#if item.callback}
56
- <button
57
- onclick={() => {
58
- item.callback();
59
- }}
60
- style="--iconWidth: {Dimensions.smallPanelWidth}rem"
61
- class:selected
62
- class:hidden={!visible.value}
63
- >
64
- <!-- <div class="selected-marker"></div> -->
65
- <div class="icon-wrapper">
66
- {#if item.iconName}
67
- <Icon
68
- name={item.iconName}
69
- size={Size.Medium}
70
- label={item.label}
71
- />
72
- {/if}
73
- </div>
74
- <div class="spacer"></div>
75
- <div class="text">
76
- <div class="label">{item.label}</div>
77
- {#if item.subtitle}
78
- <div class="subtitle">{item.subtitle}</div>
79
- {/if}
80
- </div></button
81
- >
82
- {:else if item.path}
83
- <a
84
- href={safeHref(item.path)}
85
- style="--iconWidth: {Dimensions.smallPanelWidth}rem"
86
- aria-current={selected ? 'page' : undefined}
87
- class:selected
88
- class:hidden={!visible.value}
89
- >
90
- <!-- <div class="selected-marker"></div> -->
91
- <div class="icon-wrapper">
92
- {#if item.iconName}
93
- <Icon
94
- name={item.iconName}
95
- size={Size.Medium}
96
- label={item.label}
97
- />
98
- {/if}
99
- </div>
100
- <div class="spacer"></div>
101
- <div class="text">
102
- <div class="label">{item.label}</div>
103
- {#if item.subtitle}
104
- <div class="subtitle">{item.subtitle}</div>
105
- {/if}
106
- </div></a
107
- >
108
- {:else if dev}
109
- Error: Callback had neither callback nor path
110
- {/if}
111
-
37
+ if (!path.startsWith("/")) {
38
+ return "#";
39
+ }
40
+ if (/^[a-z][a-z0-9+.-]*:/i.test(path)) {
41
+ return "#";
42
+ }
43
+ return resolve(path);
44
+ }
45
+ </script>
46
+
47
+ {#if item.callback}
48
+ <button
49
+ onclick={() => {
50
+ item.callback();
51
+ }}
52
+ style="--iconWidth: {Dimensions.smallPanelWidth}rem"
53
+ class:selected
54
+ class:hidden={!visible.value}
55
+ >
56
+ <!-- <div class="selected-marker"></div> -->
57
+ <div class="icon-wrapper">
58
+ {#if item.iconName}
59
+ <Icon
60
+ name={item.iconName}
61
+ size={Size.Medium}
62
+ label={item.label}
63
+ />
64
+ {/if}
65
+ </div>
66
+ <div class="spacer"></div>
67
+ <div class="text">
68
+ <div class="label">{item.label}</div>
69
+ {#if item.subtitle}
70
+ <div class="subtitle">{item.subtitle}</div>
71
+ {/if}
72
+ </div></button
73
+ >
74
+ {:else if item.path}
75
+ <a
76
+ href={safeHref(item.path)}
77
+ style="--iconWidth: {Dimensions.smallPanelWidth}rem"
78
+ aria-current={selected ? 'page' : undefined}
79
+ class:selected
80
+ class:hidden={!visible.value}
81
+ >
82
+ <!-- <div class="selected-marker"></div> -->
83
+ <div class="icon-wrapper">
84
+ {#if item.iconName}
85
+ <Icon
86
+ name={item.iconName}
87
+ size={Size.Medium}
88
+ label={item.label}
89
+ />
90
+ {/if}
91
+ </div>
92
+ <div class="spacer"></div>
93
+ <div class="text">
94
+ <div class="label">{item.label}</div>
95
+ {#if item.subtitle}
96
+ <div class="subtitle">{item.subtitle}</div>
97
+ {/if}
98
+ </div></a
99
+ >
100
+ {:else if dev}
101
+ Error: Callback had neither callback nor path
102
+ {/if}
103
+
112
104
  <style>button.hidden,
113
105
  a.hidden {
114
106
  display: none !important;
@@ -174,4 +166,4 @@ a:hover {
174
166
  button .spacer,
175
167
  a .spacer {
176
168
  width: 0.5rem;
177
- }</style>
169
+ }</style>
@@ -1,82 +1,75 @@
1
- <svelte:options runes />
2
-
3
- <script
4
- lang="ts"
5
- generics="Args"
6
- >
7
- import { devCatch } from '../../Helpers/Helpers.svelte.js';
8
- import type { TabOption } from '../../Types/Internal/TabOption.js';
9
- import HorizontalTabGroup from '../HorizontalTabGroup.svelte';
10
- import NavTab from './NavTab.svelte';
11
- import type { ServicesNavProps } from './ServicesNavProps.js';
12
-
13
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
14
- const { onclose, serviceTabs, selectedServiceTab }: ServicesNavProps<Args> = $props();
15
-
16
- let dialogRef: HTMLDialogElement | undefined = $state.raw();
17
-
18
- function fireClose(): void {
19
- const result = onclose();
20
- if (result instanceof Promise) {
21
- result.catch((e: unknown) => {
22
- devCatch(e);
23
- });
24
- }
1
+ <svelte:options runes />
2
+
3
+ <script
4
+ lang="ts"
5
+ generics="Args"
6
+ >import { devCatch } from "../../Helpers/Helpers.svelte.js";
7
+ import HorizontalTabGroup from "../HorizontalTabGroup.svelte";
8
+ import NavTab from "./NavTab.svelte";
9
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-arguments
10
+ const { onclose, serviceTabs, selectedServiceTab } = $props();
11
+ let dialogRef = $state.raw();
12
+ function fireClose() {
13
+ const result = onclose();
14
+ if (result instanceof Promise) {
15
+ result.catch((e) => {
16
+ devCatch(e);
17
+ });
25
18
  }
26
-
27
- // Open the dialog as soon as we mount; native showModal() gives us a
28
- // real focus trap, Escape-to-close, and inert background without any
29
- // manual aria-modal / tabindex bookkeeping.
30
- $effect(() => {
31
- const dialog = dialogRef;
32
- if (dialog && !dialog.open) {
33
- dialog.showModal();
34
- }
35
- });
36
- </script>
37
-
38
- <dialog
39
- bind:this={dialogRef}
40
- aria-label="Services"
41
- class="overlay"
42
- oncancel={(e) => {
43
- // Escape fires `cancel` first; prevent the native close so we
44
- // fully delegate to the parent (it owns the visibility flag).
45
- e.preventDefault();
46
- fireClose();
47
- }}
48
- onclick={(e) => {
49
- // Native <dialog> click target is the dialog element itself when
50
- // the user clicks the backdrop; clicks on inner content bubble
51
- // up with a different target.
52
- if (dialogRef === e.target) {
53
- fireClose();
54
- }
55
- }}
56
- >
57
- <div
58
- role="navigation"
59
- aria-label="Services tabs"
60
- class="services"
61
- >
62
- <HorizontalTabGroup
63
- name="nav-services"
64
- tabs={serviceTabs}
65
- selected={selectedServiceTab}
66
- tabComponent={navTab}
67
- noRadius
68
- grow
69
- />
70
- </div>
71
- </dialog>
72
-
73
- {#snippet navTab(option: TabOption<Args>, selected: boolean)}
74
- <NavTab
75
- {option}
76
- {selected}
77
- />
78
- {/snippet}
79
-
19
+ }
20
+ // Open the dialog as soon as we mount; native showModal() gives us a
21
+ // real focus trap, Escape-to-close, and inert background without any
22
+ // manual aria-modal / tabindex bookkeeping.
23
+ $effect(() => {
24
+ const dialog = dialogRef;
25
+ if (dialog && !dialog.open) {
26
+ dialog.showModal();
27
+ }
28
+ });
29
+ </script>
30
+
31
+ <dialog
32
+ bind:this={dialogRef}
33
+ aria-label="Services"
34
+ class="overlay"
35
+ oncancel={(e) => {
36
+ // Escape fires `cancel` first; prevent the native close so we
37
+ // fully delegate to the parent (it owns the visibility flag).
38
+ e.preventDefault();
39
+ fireClose();
40
+ }}
41
+ onclick={(e) => {
42
+ // Native <dialog> click target is the dialog element itself when
43
+ // the user clicks the backdrop; clicks on inner content bubble
44
+ // up with a different target.
45
+ if (dialogRef === e.target) {
46
+ fireClose();
47
+ }
48
+ }}
49
+ >
50
+ <div
51
+ role="navigation"
52
+ aria-label="Services tabs"
53
+ class="services"
54
+ >
55
+ <HorizontalTabGroup
56
+ name="nav-services"
57
+ tabs={serviceTabs}
58
+ selected={selectedServiceTab}
59
+ tabComponent={navTab}
60
+ noRadius
61
+ grow
62
+ />
63
+ </div>
64
+ </dialog>
65
+
66
+ {#snippet navTab(option: TabOption<Args>, selected: boolean)}
67
+ <NavTab
68
+ {option}
69
+ {selected}
70
+ />
71
+ {/snippet}
72
+
80
73
  <style>dialog.overlay {
81
74
  position: fixed;
82
75
  top: calc(3.25rem + var(--env-border-height, 0));
@@ -115,4 +108,4 @@ div.services {
115
108
  justify-content: stretch;
116
109
  height: var(--services-nav-height, min(710px, 80%));
117
110
  width: var(--services-nav-width, 800px);
118
- }</style>
111
+ }</style>