@lavalogic/scoria 0.38.12 → 0.38.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Components/AccordionGroup.svelte +46 -46
- package/dist/Components/AccordionGroupButton.svelte +76 -76
- package/dist/Components/Action.svelte +136 -136
- package/dist/Components/AppointmentCalendar/AppointmentCalendar.svelte +712 -702
- package/dist/Components/Base/BaseModal.svelte +125 -125
- package/dist/Components/Base/ContextWrapper.svelte +72 -72
- package/dist/Components/Base/Pagination.svelte +198 -198
- package/dist/Components/Base/Snippets.svelte +36 -36
- package/dist/Components/BigRadioSet.svelte +38 -38
- package/dist/Components/Bubble.svelte +101 -101
- package/dist/Components/Button.svelte +209 -209
- package/dist/Components/Checkbox.svelte +86 -86
- package/dist/Components/Contexts/ContextMenu.svelte +164 -164
- package/dist/Components/Contexts/ContextMenuDivider.svelte +15 -15
- package/dist/Components/Contexts/ContextMenuOption.svelte +60 -60
- package/dist/Components/Contexts/ToastContainer.svelte +65 -65
- package/dist/Components/Contexts/Tooltip.svelte +37 -37
- package/dist/Components/DataMatrixIcon.svelte +112 -112
- package/dist/Components/DateInput.svelte +148 -148
- package/dist/Components/DatePicker.svelte +300 -300
- package/dist/Components/DesktopModal.svelte +90 -90
- package/dist/Components/Dial.svelte +204 -204
- package/dist/Components/DimensionInput.svelte +170 -170
- package/dist/Components/DragMenu/DragMenuHolder.svelte +119 -119
- package/dist/Components/DragMenu/DraggableCard.svelte +77 -77
- package/dist/Components/FileCard.svelte +24 -24
- package/dist/Components/FileUpload.svelte +236 -236
- package/dist/Components/GridInputSet.svelte +29 -29
- package/dist/Components/HorizontalTabGroup.svelte +82 -82
- package/dist/Components/HorizontalTabGroupButton.svelte +44 -44
- package/dist/Components/Icon.svelte +128 -128
- package/dist/Components/Icons.js +357 -357
- package/dist/Components/LoadingAnimation.svelte +29 -29
- package/dist/Components/LoadingModal.svelte +45 -45
- package/dist/Components/LoadingOverlay.svelte +25 -25
- package/dist/Components/Modal.svelte +258 -258
- package/dist/Components/MultiSelect.svelte +220 -220
- package/dist/Components/Nav/NavServiceMenuList.svelte +43 -43
- package/dist/Components/Nav/NavTab.svelte +62 -62
- package/dist/Components/Nav/ServicesNav.svelte +80 -80
- package/dist/Components/NumberInput.svelte +157 -157
- package/dist/Components/PageHeading.svelte +25 -25
- package/dist/Components/PasswordInput.svelte +88 -88
- package/dist/Components/Portal.svelte +36 -36
- package/dist/Components/QuerySelect.svelte +366 -366
- package/dist/Components/SingleSelect.svelte +250 -250
- package/dist/Components/StepButton.svelte +70 -70
- package/dist/Components/StepForm.svelte +140 -140
- package/dist/Components/Switch.svelte +69 -69
- package/dist/Components/Table/Body/Rows/ColumnHighlight.svelte +51 -51
- package/dist/Components/Table/Body/Rows/ColumnList.svelte +59 -59
- package/dist/Components/Table/Body/Rows/Columns/AccessorComponent.svelte +79 -79
- package/dist/Components/Table/Body/Rows/Columns/ExpandCell.svelte +120 -120
- package/dist/Components/Table/Body/Rows/Columns/ProgressCell.svelte +105 -105
- package/dist/Components/Table/Body/Rows/Columns/TableAction.svelte +87 -87
- package/dist/Components/Table/Body/Rows/Columns/TableCheckbox.svelte +126 -126
- package/dist/Components/Table/Body/Rows/Columns/TableColumn.svelte +159 -159
- package/dist/Components/Table/Body/Rows/Columns/TableDatePicker.svelte +246 -246
- package/dist/Components/Table/Body/Rows/Columns/TableNumberInput.svelte +216 -216
- package/dist/Components/Table/Body/Rows/Columns/TableRowSelectionCheckbox.svelte +179 -179
- package/dist/Components/Table/Body/Rows/Columns/TableTextInput.svelte +197 -197
- package/dist/Components/Table/Body/Rows/Columns/ValidityCell.svelte +190 -190
- package/dist/Components/Table/Body/Rows/TableRow.svelte +290 -290
- package/dist/Components/Table/Headers/HighlightAllHeader.svelte +41 -41
- package/dist/Components/Table/Headers/SelectAllHeader.svelte +30 -30
- package/dist/Components/Table/Misc/SidePanel.svelte +35 -35
- package/dist/Components/Table/Misc/ToolboxPanel.svelte +100 -100
- package/dist/Components/Table/Misc/ToolboxPanelModal.svelte +107 -107
- package/dist/Components/TextArea.svelte +130 -130
- package/dist/Components/TextInput.svelte +176 -176
- package/dist/Components/ThreeStateRadio.svelte +131 -131
- package/dist/Components/Touch/BoolCard.svelte +45 -45
- package/dist/Components/Touch/DateModal.svelte +209 -209
- package/dist/Components/Touch/EditCard.svelte +59 -59
- package/dist/Components/Touch/InfoCard.svelte +31 -31
- package/dist/Components/Touch/InfoTextCard.svelte +28 -28
- package/dist/Components/Touch/ModalExplanation.svelte +17 -17
- package/dist/Components/Touch/NumberKeyboard.svelte +225 -225
- package/dist/Components/Touch/NumberModal.svelte +79 -79
- package/dist/Components/Touch/PlusMinusCard.svelte +46 -46
- package/dist/Components/Touch/SummaryCard.svelte +93 -93
- package/dist/Components/Touch/TextAreaModal.svelte +95 -95
- package/dist/Components/Touch/TextModal.svelte +106 -106
- package/dist/Components/Touch/TouchCard.svelte +33 -33
- package/dist/Components/Touch/TouchModal.svelte +274 -274
- package/dist/Components/Touch/UtilityButton.svelte +109 -109
- package/dist/Components/VerticalTabGroup.svelte +79 -79
- package/dist/Components/VerticalTabGroupButton.svelte +58 -58
- package/dist/Helpers/Datamatrix.d.ts +19 -19
- package/dist/Helpers/Datamatrix.js +291 -291
- package/dist/Types/Examples/ExampleModal.svelte +35 -35
- package/dist/scss/_motion.scss +57 -57
- package/dist/scss/_sizing.scss +85 -85
- package/package.json +30 -30
|
@@ -1,222 +1,222 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script
|
|
4
|
-
lang="ts"
|
|
5
|
-
generics="V extends object, VP extends ValueProp<V>"
|
|
6
|
-
>
|
|
7
|
-
import { autoPluralise, generateWeakId, vowels } from '../Helpers/Helpers.svelte.js';
|
|
8
|
-
import { attachDropdownPortal } from '../Helpers/dropdownPortal.js';
|
|
9
|
-
import { Colour, ColourSet } from '../scss/colours.js';
|
|
10
|
-
import type { ValueProp } from '../Types/Internal/Misc.js';
|
|
11
|
-
import { Size } from '../Types/Internal/Size.js';
|
|
12
|
-
import Svelecte from 'svelecte';
|
|
13
|
-
import { SvelteSet } from 'svelte/reactivity';
|
|
14
|
-
import { onMount } from 'svelte';
|
|
15
|
-
import Action from './Action.svelte';
|
|
16
|
-
import Icon from './Icon.svelte';
|
|
17
|
-
import type { MultiSelectProps } from './MultiSelectProps.js';
|
|
18
|
-
|
|
19
|
-
// NOTE selectedValues is mutated by svelecte without asking, so we sometimes have to reverse it without a callback (side effects)
|
|
20
|
-
const {
|
|
21
|
-
label,
|
|
22
|
-
inputId = generateWeakId(),
|
|
23
|
-
options,
|
|
24
|
-
labelProp,
|
|
25
|
-
valueProp,
|
|
26
|
-
width,
|
|
27
|
-
valueAsObject = true,
|
|
28
|
-
height,
|
|
29
|
-
selectedValues,
|
|
30
|
-
onchange,
|
|
31
|
-
collapseSelection = false,
|
|
32
|
-
name = 'Item',
|
|
33
|
-
disabled,
|
|
34
|
-
required,
|
|
35
|
-
invalid,
|
|
36
|
-
}: MultiSelectProps<V, VP> = $props();
|
|
37
|
-
|
|
38
|
-
const an = $derived(name && vowels.has(name.at(0)?.toLocaleLowerCase() ?? '') ? 'an' : 'a');
|
|
39
|
-
|
|
40
|
-
// Pre-computed set of selected primitive keys. Used in the option snippet
|
|
41
|
-
// to avoid an O(n*m) findIndex sweep per render.
|
|
42
|
-
const selectedKeys = $derived.by(() => {
|
|
43
|
-
const s = new SvelteSet<V[VP]>();
|
|
44
|
-
if (valueAsObject) {
|
|
45
|
-
for (const v of selectedValues as Array<V>) {
|
|
46
|
-
s.add(v[valueProp]);
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
for (const v of selectedValues as Array<V[VP]>) {
|
|
50
|
-
s.add(v);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
return s;
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
let isExpanded = $state(false);
|
|
57
|
-
|
|
58
|
-
onMount(() => {
|
|
59
|
-
const input = document.getElementById(inputId);
|
|
60
|
-
const svelecteRoot = input?.closest('div.svelecte') as HTMLElement | null;
|
|
61
|
-
const dropdownRef =
|
|
62
|
-
(svelecteRoot?.querySelector('.sv_dropdown') as HTMLElement | null) ?? undefined;
|
|
63
|
-
|
|
64
|
-
// HACK callback for is-open on/off; drives the chevron rotation
|
|
65
|
-
// in the action snippet further down.
|
|
66
|
-
const observer = new MutationObserver((mutations: Array<MutationRecord>) => {
|
|
67
|
-
for (let i = mutations.length - 1; i > -1; i--) {
|
|
68
|
-
const mutation = mutations[i];
|
|
69
|
-
if (mutation.attributeName === 'class') {
|
|
70
|
-
isExpanded = dropdownRef?.classList.contains('is-open') ?? false;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
if (dropdownRef) {
|
|
76
|
-
observer.observe(dropdownRef, { attributes: true });
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Teleport the Svelecte dropdown to `document.body` while open
|
|
80
|
-
// so it escapes every ancestor `overflow` / `transform` /
|
|
81
|
-
// `z-index` stacking context. See `Helpers/dropdownPortal.ts`
|
|
82
|
-
// for the rationale and behaviour (auto-flip upwards on tight
|
|
83
|
-
// viewports, scroll-aware repositioning, viewport-bounded
|
|
84
|
-
// max-height).
|
|
85
|
-
const detachPortal = attachDropdownPortal({
|
|
86
|
-
getTrigger: () => svelecteRoot,
|
|
87
|
-
getDropdown: () => dropdownRef,
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
return () => {
|
|
91
|
-
observer.disconnect();
|
|
92
|
-
detachPortal();
|
|
93
|
-
};
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
const isInvalid = $derived((required && !selectedValues.length) || invalid);
|
|
97
|
-
</script>
|
|
98
|
-
|
|
99
|
-
<div
|
|
100
|
-
class:invalid={isInvalid}
|
|
101
|
-
class="select-wrapper"
|
|
102
|
-
style={`${width ? `--input-width: ${width};` : ''}${height ? `--input-height: ${height};` : ''}`}
|
|
103
|
-
>
|
|
104
|
-
{#if label}
|
|
105
|
-
<label for={inputId}>
|
|
106
|
-
<span>{label}</span>
|
|
107
|
-
<span class="border">{label}</span>
|
|
108
|
-
</label>
|
|
109
|
-
{/if}
|
|
110
|
-
|
|
111
|
-
<Svelecte
|
|
112
|
-
multiple
|
|
113
|
-
{valueAsObject}
|
|
114
|
-
{disabled}
|
|
115
|
-
{inputId}
|
|
116
|
-
valueField={valueProp}
|
|
117
|
-
labelField={labelProp}
|
|
118
|
-
{options}
|
|
119
|
-
searchable
|
|
120
|
-
value={selectedValues}
|
|
121
|
-
onChange={(e: Array<V>) => {
|
|
122
|
-
// svelecte always emits the full option objects; normalise to the
|
|
123
|
-
// shape the variant promised: primitives for AsPrimitive,
|
|
124
|
-
// objects for AsObject. This is what the discriminated
|
|
125
|
-
// `onchange` typing now guarantees to consumers.
|
|
126
|
-
if (valueAsObject) {
|
|
127
|
-
void (onchange as (v: Array<V>) => void | Promise<void>)(e);
|
|
128
|
-
} else {
|
|
129
|
-
const primitives = e.map((it) => it[valueProp]);
|
|
130
|
-
void (onchange as (v: Array<V[VP]>) => void | Promise<void>)(primitives);
|
|
131
|
-
}
|
|
132
|
-
}}
|
|
133
|
-
{name}
|
|
134
|
-
selectOnTab
|
|
135
|
-
keepSelectionInList
|
|
136
|
-
searchProps={{ keepSelectionInList: true }}
|
|
137
|
-
creatable={false}
|
|
138
|
-
collapseSelection={collapseSelection ? 'always' : null}
|
|
139
|
-
deselectMode="toggle"
|
|
140
|
-
highlightFirstItem={false}
|
|
141
|
-
placeholder="Please Select {name ? `${an} ${name}` : ''}"
|
|
142
|
-
selection={collapseSelection ? selection : undefined}
|
|
143
|
-
>
|
|
144
|
-
{#snippet option(opt)}
|
|
145
|
-
{@const option = opt as V}
|
|
146
|
-
<span class="dropdown-item">
|
|
147
|
-
<div
|
|
148
|
-
class="toggle-icon"
|
|
149
|
-
class:yes={selectedKeys.has(option[valueProp])}
|
|
150
|
-
>
|
|
151
|
-
<Icon
|
|
152
|
-
name="tick"
|
|
153
|
-
size={Size.MediumSmall}
|
|
154
|
-
/>
|
|
155
|
-
</div>
|
|
156
|
-
{option[labelProp]}</span
|
|
157
|
-
>
|
|
158
|
-
{/snippet}
|
|
159
|
-
|
|
160
|
-
{#snippet collapsedSelection(selectedOptions: Array<object> /*_i18n*/)}
|
|
161
|
-
<span class="selection"
|
|
162
|
-
>{#if selectedOptions.length}{selectedOptions.length}
|
|
163
|
-
{#if name}{autoPluralise(name, selectedOptions.length)}{/if} Selected
|
|
164
|
-
{:else}Please Select {#if name}
|
|
165
|
-
{an} {name}{/if}
|
|
166
|
-
{/if}</span
|
|
167
|
-
>
|
|
168
|
-
{/snippet}
|
|
169
|
-
|
|
170
|
-
{#snippet append()}
|
|
171
|
-
<div class="icons">
|
|
172
|
-
<Action
|
|
173
|
-
{disabled}
|
|
174
|
-
size={Size.Small}
|
|
175
|
-
noTabIndex
|
|
176
|
-
colourSet={ColourSet.Auxiliary}
|
|
177
|
-
onclick={(e: MouseEvent) => {
|
|
178
|
-
if (isExpanded) {
|
|
179
|
-
e.stopPropagation();
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (selectedValues.length) {
|
|
183
|
-
// Empty array satisfies both Array<V> and Array<V[VP]>.
|
|
184
|
-
void (onchange as (v: Array<V> | Array<V[VP]>) => void | Promise<void>)([]);
|
|
185
|
-
} else if (valueAsObject) {
|
|
186
|
-
void (onchange as (v: Array<V>) => void | Promise<void>)(options.concat());
|
|
187
|
-
} else {
|
|
188
|
-
const primitives = options.map((it) => it[valueProp]);
|
|
189
|
-
void (onchange as (v: Array<V[VP]>) => void | Promise<void>)(primitives);
|
|
190
|
-
}
|
|
191
|
-
}}
|
|
192
|
-
icon={{
|
|
193
|
-
name: selectedValues.length ? 'close-menu-cross' : 'manage-product-group',
|
|
194
|
-
colour: Colour['$ui-blue-10'],
|
|
195
|
-
}}
|
|
196
|
-
/>
|
|
197
|
-
|
|
198
|
-
<Action
|
|
199
|
-
{disabled}
|
|
200
|
-
size={Size.Small}
|
|
201
|
-
noTabIndex
|
|
202
|
-
colourSet={ColourSet.Auxiliary}
|
|
203
|
-
icon={{
|
|
204
|
-
name: isExpanded ? 'chevron-up' : 'chevron-down',
|
|
205
|
-
colour: Colour['$ui-blue-10'],
|
|
206
|
-
}}
|
|
207
|
-
/>
|
|
208
|
-
</div>
|
|
209
|
-
{/snippet}
|
|
210
|
-
{#snippet toggleIcon()}
|
|
211
|
-
<div class="nothing"></div>
|
|
212
|
-
{/snippet}
|
|
213
|
-
</Svelecte>
|
|
214
|
-
|
|
215
|
-
{#snippet selection(/*_selectedOptions: Array<unknown>*/)}
|
|
216
|
-
<!-- collapsed selection rendered above -->
|
|
217
|
-
{/snippet}
|
|
218
|
-
</div>
|
|
219
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script
|
|
4
|
+
lang="ts"
|
|
5
|
+
generics="V extends object, VP extends ValueProp<V>"
|
|
6
|
+
>
|
|
7
|
+
import { autoPluralise, generateWeakId, vowels } from '../Helpers/Helpers.svelte.js';
|
|
8
|
+
import { attachDropdownPortal } from '../Helpers/dropdownPortal.js';
|
|
9
|
+
import { Colour, ColourSet } from '../scss/colours.js';
|
|
10
|
+
import type { ValueProp } from '../Types/Internal/Misc.js';
|
|
11
|
+
import { Size } from '../Types/Internal/Size.js';
|
|
12
|
+
import Svelecte from 'svelecte';
|
|
13
|
+
import { SvelteSet } from 'svelte/reactivity';
|
|
14
|
+
import { onMount } from 'svelte';
|
|
15
|
+
import Action from './Action.svelte';
|
|
16
|
+
import Icon from './Icon.svelte';
|
|
17
|
+
import type { MultiSelectProps } from './MultiSelectProps.js';
|
|
18
|
+
|
|
19
|
+
// NOTE selectedValues is mutated by svelecte without asking, so we sometimes have to reverse it without a callback (side effects)
|
|
20
|
+
const {
|
|
21
|
+
label,
|
|
22
|
+
inputId = generateWeakId(),
|
|
23
|
+
options,
|
|
24
|
+
labelProp,
|
|
25
|
+
valueProp,
|
|
26
|
+
width,
|
|
27
|
+
valueAsObject = true,
|
|
28
|
+
height,
|
|
29
|
+
selectedValues,
|
|
30
|
+
onchange,
|
|
31
|
+
collapseSelection = false,
|
|
32
|
+
name = 'Item',
|
|
33
|
+
disabled,
|
|
34
|
+
required,
|
|
35
|
+
invalid,
|
|
36
|
+
}: MultiSelectProps<V, VP> = $props();
|
|
37
|
+
|
|
38
|
+
const an = $derived(name && vowels.has(name.at(0)?.toLocaleLowerCase() ?? '') ? 'an' : 'a');
|
|
39
|
+
|
|
40
|
+
// Pre-computed set of selected primitive keys. Used in the option snippet
|
|
41
|
+
// to avoid an O(n*m) findIndex sweep per render.
|
|
42
|
+
const selectedKeys = $derived.by(() => {
|
|
43
|
+
const s = new SvelteSet<V[VP]>();
|
|
44
|
+
if (valueAsObject) {
|
|
45
|
+
for (const v of selectedValues as Array<V>) {
|
|
46
|
+
s.add(v[valueProp]);
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
for (const v of selectedValues as Array<V[VP]>) {
|
|
50
|
+
s.add(v);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return s;
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
let isExpanded = $state(false);
|
|
57
|
+
|
|
58
|
+
onMount(() => {
|
|
59
|
+
const input = document.getElementById(inputId);
|
|
60
|
+
const svelecteRoot = input?.closest('div.svelecte') as HTMLElement | null;
|
|
61
|
+
const dropdownRef =
|
|
62
|
+
(svelecteRoot?.querySelector('.sv_dropdown') as HTMLElement | null) ?? undefined;
|
|
63
|
+
|
|
64
|
+
// HACK callback for is-open on/off; drives the chevron rotation
|
|
65
|
+
// in the action snippet further down.
|
|
66
|
+
const observer = new MutationObserver((mutations: Array<MutationRecord>) => {
|
|
67
|
+
for (let i = mutations.length - 1; i > -1; i--) {
|
|
68
|
+
const mutation = mutations[i];
|
|
69
|
+
if (mutation.attributeName === 'class') {
|
|
70
|
+
isExpanded = dropdownRef?.classList.contains('is-open') ?? false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (dropdownRef) {
|
|
76
|
+
observer.observe(dropdownRef, { attributes: true });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Teleport the Svelecte dropdown to `document.body` while open
|
|
80
|
+
// so it escapes every ancestor `overflow` / `transform` /
|
|
81
|
+
// `z-index` stacking context. See `Helpers/dropdownPortal.ts`
|
|
82
|
+
// for the rationale and behaviour (auto-flip upwards on tight
|
|
83
|
+
// viewports, scroll-aware repositioning, viewport-bounded
|
|
84
|
+
// max-height).
|
|
85
|
+
const detachPortal = attachDropdownPortal({
|
|
86
|
+
getTrigger: () => svelecteRoot,
|
|
87
|
+
getDropdown: () => dropdownRef,
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return () => {
|
|
91
|
+
observer.disconnect();
|
|
92
|
+
detachPortal();
|
|
93
|
+
};
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const isInvalid = $derived((required && !selectedValues.length) || invalid);
|
|
97
|
+
</script>
|
|
98
|
+
|
|
99
|
+
<div
|
|
100
|
+
class:invalid={isInvalid}
|
|
101
|
+
class="select-wrapper"
|
|
102
|
+
style={`${width ? `--input-width: ${width};` : ''}${height ? `--input-height: ${height};` : ''}`}
|
|
103
|
+
>
|
|
104
|
+
{#if label}
|
|
105
|
+
<label for={inputId}>
|
|
106
|
+
<span>{label}</span>
|
|
107
|
+
<span class="border">{label}</span>
|
|
108
|
+
</label>
|
|
109
|
+
{/if}
|
|
110
|
+
|
|
111
|
+
<Svelecte
|
|
112
|
+
multiple
|
|
113
|
+
{valueAsObject}
|
|
114
|
+
{disabled}
|
|
115
|
+
{inputId}
|
|
116
|
+
valueField={valueProp}
|
|
117
|
+
labelField={labelProp}
|
|
118
|
+
{options}
|
|
119
|
+
searchable
|
|
120
|
+
value={selectedValues}
|
|
121
|
+
onChange={(e: Array<V>) => {
|
|
122
|
+
// svelecte always emits the full option objects; normalise to the
|
|
123
|
+
// shape the variant promised: primitives for AsPrimitive,
|
|
124
|
+
// objects for AsObject. This is what the discriminated
|
|
125
|
+
// `onchange` typing now guarantees to consumers.
|
|
126
|
+
if (valueAsObject) {
|
|
127
|
+
void (onchange as (v: Array<V>) => void | Promise<void>)(e);
|
|
128
|
+
} else {
|
|
129
|
+
const primitives = e.map((it) => it[valueProp]);
|
|
130
|
+
void (onchange as (v: Array<V[VP]>) => void | Promise<void>)(primitives);
|
|
131
|
+
}
|
|
132
|
+
}}
|
|
133
|
+
{name}
|
|
134
|
+
selectOnTab
|
|
135
|
+
keepSelectionInList
|
|
136
|
+
searchProps={{ keepSelectionInList: true }}
|
|
137
|
+
creatable={false}
|
|
138
|
+
collapseSelection={collapseSelection ? 'always' : null}
|
|
139
|
+
deselectMode="toggle"
|
|
140
|
+
highlightFirstItem={false}
|
|
141
|
+
placeholder="Please Select {name ? `${an} ${name}` : ''}"
|
|
142
|
+
selection={collapseSelection ? selection : undefined}
|
|
143
|
+
>
|
|
144
|
+
{#snippet option(opt)}
|
|
145
|
+
{@const option = opt as V}
|
|
146
|
+
<span class="dropdown-item">
|
|
147
|
+
<div
|
|
148
|
+
class="toggle-icon"
|
|
149
|
+
class:yes={selectedKeys.has(option[valueProp])}
|
|
150
|
+
>
|
|
151
|
+
<Icon
|
|
152
|
+
name="tick"
|
|
153
|
+
size={Size.MediumSmall}
|
|
154
|
+
/>
|
|
155
|
+
</div>
|
|
156
|
+
{option[labelProp]}</span
|
|
157
|
+
>
|
|
158
|
+
{/snippet}
|
|
159
|
+
|
|
160
|
+
{#snippet collapsedSelection(selectedOptions: Array<object> /*_i18n*/)}
|
|
161
|
+
<span class="selection"
|
|
162
|
+
>{#if selectedOptions.length}{selectedOptions.length}
|
|
163
|
+
{#if name}{autoPluralise(name, selectedOptions.length)}{/if} Selected
|
|
164
|
+
{:else}Please Select {#if name}
|
|
165
|
+
{an} {name}{/if}
|
|
166
|
+
{/if}</span
|
|
167
|
+
>
|
|
168
|
+
{/snippet}
|
|
169
|
+
|
|
170
|
+
{#snippet append()}
|
|
171
|
+
<div class="icons">
|
|
172
|
+
<Action
|
|
173
|
+
{disabled}
|
|
174
|
+
size={Size.Small}
|
|
175
|
+
noTabIndex
|
|
176
|
+
colourSet={ColourSet.Auxiliary}
|
|
177
|
+
onclick={(e: MouseEvent) => {
|
|
178
|
+
if (isExpanded) {
|
|
179
|
+
e.stopPropagation();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (selectedValues.length) {
|
|
183
|
+
// Empty array satisfies both Array<V> and Array<V[VP]>.
|
|
184
|
+
void (onchange as (v: Array<V> | Array<V[VP]>) => void | Promise<void>)([]);
|
|
185
|
+
} else if (valueAsObject) {
|
|
186
|
+
void (onchange as (v: Array<V>) => void | Promise<void>)(options.concat());
|
|
187
|
+
} else {
|
|
188
|
+
const primitives = options.map((it) => it[valueProp]);
|
|
189
|
+
void (onchange as (v: Array<V[VP]>) => void | Promise<void>)(primitives);
|
|
190
|
+
}
|
|
191
|
+
}}
|
|
192
|
+
icon={{
|
|
193
|
+
name: selectedValues.length ? 'close-menu-cross' : 'manage-product-group',
|
|
194
|
+
colour: Colour['$ui-blue-10'],
|
|
195
|
+
}}
|
|
196
|
+
/>
|
|
197
|
+
|
|
198
|
+
<Action
|
|
199
|
+
{disabled}
|
|
200
|
+
size={Size.Small}
|
|
201
|
+
noTabIndex
|
|
202
|
+
colourSet={ColourSet.Auxiliary}
|
|
203
|
+
icon={{
|
|
204
|
+
name: isExpanded ? 'chevron-up' : 'chevron-down',
|
|
205
|
+
colour: Colour['$ui-blue-10'],
|
|
206
|
+
}}
|
|
207
|
+
/>
|
|
208
|
+
</div>
|
|
209
|
+
{/snippet}
|
|
210
|
+
{#snippet toggleIcon()}
|
|
211
|
+
<div class="nothing"></div>
|
|
212
|
+
{/snippet}
|
|
213
|
+
</Svelecte>
|
|
214
|
+
|
|
215
|
+
{#snippet selection(/*_selectedOptions: Array<unknown>*/)}
|
|
216
|
+
<!-- collapsed selection rendered above -->
|
|
217
|
+
{/snippet}
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
220
|
<style>.select-wrapper {
|
|
221
221
|
min-width: var(--input-min-width, 12rem);
|
|
222
222
|
width: var(--input-width, 100%);
|
|
@@ -379,4 +379,4 @@ div.icons {
|
|
|
379
379
|
}
|
|
380
380
|
:global(.dropdown-item) .toggle-icon.yes {
|
|
381
381
|
opacity: 1;
|
|
382
|
-
}</style>
|
|
382
|
+
}</style>
|
|
@@ -1,45 +1,45 @@
|
|
|
1
|
-
<svelte:options runes />
|
|
2
|
-
|
|
3
|
-
<script lang="ts">
|
|
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 type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
|
|
9
|
-
import ServiceMenuItem from './ServiceMenuItem.svelte';
|
|
10
|
-
|
|
11
|
-
const { title, items, icon }: NavServiceMenuListProps = $props();
|
|
12
|
-
</script>
|
|
13
|
-
|
|
14
|
-
<div
|
|
15
|
-
class="wrapper"
|
|
16
|
-
style="--icon-column-width: {Dimensions.smallPanelWidth}rem"
|
|
17
|
-
role="group"
|
|
18
|
-
aria-label={title}
|
|
19
|
-
>
|
|
20
|
-
<h3>
|
|
21
|
-
<div class="icon">
|
|
22
|
-
{#if icon}
|
|
23
|
-
<Icon
|
|
24
|
-
size={Size.Large}
|
|
25
|
-
colour={Colour['$font-primary']}
|
|
26
|
-
{...icon}
|
|
27
|
-
/>
|
|
28
|
-
{/if}
|
|
29
|
-
</div>
|
|
30
|
-
{title}
|
|
31
|
-
</h3>
|
|
32
|
-
|
|
33
|
-
<div
|
|
34
|
-
class="items"
|
|
35
|
-
role="list"
|
|
36
|
-
>
|
|
37
|
-
{#each items as { item } (item.label)}
|
|
38
|
-
<ServiceMenuItem {item} />
|
|
39
|
-
{/each}
|
|
40
|
-
</div>
|
|
41
|
-
</div>
|
|
42
|
-
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<script lang="ts">
|
|
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 type { NavServiceMenuListProps } from './NavServiceMenuListProps.js';
|
|
9
|
+
import ServiceMenuItem from './ServiceMenuItem.svelte';
|
|
10
|
+
|
|
11
|
+
const { title, items, icon }: NavServiceMenuListProps = $props();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<div
|
|
15
|
+
class="wrapper"
|
|
16
|
+
style="--icon-column-width: {Dimensions.smallPanelWidth}rem"
|
|
17
|
+
role="group"
|
|
18
|
+
aria-label={title}
|
|
19
|
+
>
|
|
20
|
+
<h3>
|
|
21
|
+
<div class="icon">
|
|
22
|
+
{#if icon}
|
|
23
|
+
<Icon
|
|
24
|
+
size={Size.Large}
|
|
25
|
+
colour={Colour['$font-primary']}
|
|
26
|
+
{...icon}
|
|
27
|
+
/>
|
|
28
|
+
{/if}
|
|
29
|
+
</div>
|
|
30
|
+
{title}
|
|
31
|
+
</h3>
|
|
32
|
+
|
|
33
|
+
<div
|
|
34
|
+
class="items"
|
|
35
|
+
role="list"
|
|
36
|
+
>
|
|
37
|
+
{#each items as { item } (item.label)}
|
|
38
|
+
<ServiceMenuItem {item} />
|
|
39
|
+
{/each}
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
43
|
<style>h3 {
|
|
44
44
|
font-size: 1.5rem;
|
|
45
45
|
display: flex;
|
|
@@ -63,4 +63,4 @@ h3 .icon {
|
|
|
63
63
|
.wrapper .items {
|
|
64
64
|
flex-shrink: 1;
|
|
65
65
|
overflow: auto;
|
|
66
|
-
}</style>
|
|
66
|
+
}</style>
|
|
@@ -1,64 +1,64 @@
|
|
|
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
|
+
>
|
|
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
|
+
|
|
62
62
|
<style>button.tab.hidden {
|
|
63
63
|
display: none !important;
|
|
64
64
|
}
|
|
@@ -91,4 +91,4 @@
|
|
|
91
91
|
}
|
|
92
92
|
.tab.invalid {
|
|
93
93
|
color: #aa1414;
|
|
94
|
-
}</style>
|
|
94
|
+
}</style>
|