@lavalogic/scoria 0.40.11 → 0.40.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/CollapsibleCard.svelte +53 -78
- package/dist/Components/CollapsibleMenuGroup.svelte +93 -0
- package/dist/Components/CollapsibleMenuGroup.svelte.d.ts +9 -0
- package/dist/Components/CollapsibleMenuGroupProps.d.ts +14 -0
- package/dist/Components/CollapsibleMenuGroupProps.js +1 -0
- package/dist/Components/LoadingAnimation.svelte +20 -5
- package/dist/Components/LoadingModal.svelte +1 -18
- package/dist/Components/LoadingOverlay.svelte +1 -18
- package/dist/Components/OptionCards.svelte +21 -5
- package/dist/Components/OptionCardsProps.d.ts +11 -2
- package/dist/Components/SidebarPanel.svelte +85 -0
- package/dist/Components/SidebarPanel.svelte.d.ts +9 -0
- package/dist/Components/SidebarPanelProps.d.ts +18 -0
- package/dist/Components/SidebarPanelProps.js +1 -0
- package/dist/Helpers/loaderDefaults.svelte.d.ts +16 -0
- package/dist/Helpers/loaderDefaults.svelte.js +14 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3 -0
- package/package.json +1 -1
|
@@ -9,10 +9,13 @@
|
|
|
9
9
|
|
|
10
10
|
<script lang="ts">
|
|
11
11
|
import { loadingText } from '../Helpers/Helpers.svelte.js';
|
|
12
|
+
import { usePrefersReducedMotion } from '../Helpers/prefersReducedMotion.svelte.js';
|
|
12
13
|
import { Size } from '../Types/Internal/Size.js';
|
|
13
14
|
import { Colour } from '../scss/colours.js';
|
|
14
|
-
import
|
|
15
|
+
import { cubicOut } from 'svelte/easing';
|
|
16
|
+
import { slide } from 'svelte/transition';
|
|
15
17
|
import type { CollapsibleCardProps } from './CollapsibleCardProps.js';
|
|
18
|
+
import Icon from './Icon.svelte';
|
|
16
19
|
|
|
17
20
|
let {
|
|
18
21
|
title,
|
|
@@ -26,6 +29,9 @@
|
|
|
26
29
|
const contentId = `collapsible-card-${generateWeakId()}`;
|
|
27
30
|
|
|
28
31
|
const cardMaxHeight = $derived(maxHeight != null && maxHeight > 0 ? `${maxHeight}px` : undefined);
|
|
32
|
+
|
|
33
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
34
|
+
const slideDuration = $derived(reducedMotion.matches ? 0 : 200);
|
|
29
35
|
</script>
|
|
30
36
|
|
|
31
37
|
<div
|
|
@@ -34,7 +40,16 @@
|
|
|
34
40
|
class:is-collapsed={isCollapsed}
|
|
35
41
|
class:grow
|
|
36
42
|
>
|
|
37
|
-
<
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
class="heading"
|
|
46
|
+
class:is-collapsed={isCollapsed}
|
|
47
|
+
aria-expanded={!isCollapsed}
|
|
48
|
+
aria-controls={contentId}
|
|
49
|
+
onclick={() => {
|
|
50
|
+
isCollapsed = !isCollapsed;
|
|
51
|
+
}}
|
|
52
|
+
>
|
|
38
53
|
<div class="left">
|
|
39
54
|
<h3>{title}</h3>
|
|
40
55
|
{#if subtitle}
|
|
@@ -45,34 +60,26 @@
|
|
|
45
60
|
{/await}
|
|
46
61
|
{/if}
|
|
47
62
|
</div>
|
|
48
|
-
<
|
|
49
|
-
<
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
onclick={() => {
|
|
54
|
-
isCollapsed = !isCollapsed;
|
|
55
|
-
}}
|
|
56
|
-
size={Size.TripleXLarge}
|
|
57
|
-
icon={{
|
|
58
|
-
name: isCollapsed ? 'circle-plus' : 'circle-minus',
|
|
59
|
-
colour: Colour['$ui-blue-5'],
|
|
60
|
-
size: Size.Medium,
|
|
61
|
-
}}
|
|
63
|
+
<span class="indicator">
|
|
64
|
+
<Icon
|
|
65
|
+
name={isCollapsed ? 'circle-plus' : 'circle-minus'}
|
|
66
|
+
colour={Colour['$ui-blue-5']}
|
|
67
|
+
size={Size.Medium}
|
|
62
68
|
/>
|
|
63
|
-
</
|
|
64
|
-
</
|
|
69
|
+
</span>
|
|
70
|
+
</button>
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
{#if !isCollapsed}
|
|
73
|
+
<div
|
|
74
|
+
id={contentId}
|
|
75
|
+
class="content"
|
|
76
|
+
transition:slide={{ duration: slideDuration, easing: cubicOut }}
|
|
77
|
+
>
|
|
78
|
+
{#if children}
|
|
79
|
+
{@render children()}
|
|
80
|
+
{/if}
|
|
81
|
+
</div>
|
|
82
|
+
{/if}
|
|
76
83
|
</div>
|
|
77
84
|
|
|
78
85
|
<style>.collapsible-card {
|
|
@@ -89,35 +96,43 @@
|
|
|
89
96
|
.collapsible-card.grow {
|
|
90
97
|
flex: 1;
|
|
91
98
|
}
|
|
92
|
-
|
|
99
|
+
|
|
100
|
+
.heading {
|
|
93
101
|
display: flex;
|
|
94
102
|
flex-flow: row nowrap;
|
|
95
|
-
|
|
103
|
+
align-items: center;
|
|
104
|
+
width: 100%;
|
|
105
|
+
font: inherit;
|
|
106
|
+
text-align: start;
|
|
107
|
+
cursor: pointer;
|
|
108
|
+
background: transparent;
|
|
109
|
+
border: none;
|
|
96
110
|
box-sizing: border-box;
|
|
97
111
|
padding: 0 0.5rem 0.5rem 0.5rem;
|
|
98
112
|
}
|
|
99
|
-
.
|
|
113
|
+
.heading .left {
|
|
100
114
|
display: flex;
|
|
101
115
|
flex-flow: column nowrap;
|
|
102
116
|
justify-content: center;
|
|
117
|
+
gap: 0.25rem;
|
|
103
118
|
}
|
|
104
|
-
.
|
|
119
|
+
.heading .left h3 {
|
|
105
120
|
margin: 0;
|
|
106
121
|
margin-top: 0.25rem;
|
|
107
122
|
color: #46525b;
|
|
108
123
|
font-weight: 500;
|
|
109
124
|
font-size: 1.125rem;
|
|
110
125
|
}
|
|
111
|
-
.
|
|
112
|
-
margin-top: auto;
|
|
113
|
-
margin-bottom: 0.25rem;
|
|
126
|
+
.heading .left span {
|
|
114
127
|
color: #647d8e;
|
|
115
128
|
font-size: 0.875rem;
|
|
116
129
|
}
|
|
117
|
-
.
|
|
130
|
+
.heading .indicator {
|
|
118
131
|
margin-left: auto;
|
|
132
|
+
display: inline-flex;
|
|
133
|
+
align-items: center;
|
|
119
134
|
}
|
|
120
|
-
.
|
|
135
|
+
.heading:not(.is-collapsed) {
|
|
121
136
|
margin-bottom: 1rem;
|
|
122
137
|
border-bottom: solid 1px #dae0e5;
|
|
123
138
|
}
|
|
@@ -125,49 +140,9 @@
|
|
|
125
140
|
div.content {
|
|
126
141
|
display: flex;
|
|
127
142
|
flex-flow: column nowrap;
|
|
128
|
-
}
|
|
129
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
130
|
-
div.content {
|
|
131
|
-
transition: max-height ease-out 0.15s 0s, opacity ease-out 0.15s 0.1s;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
div.content {
|
|
135
143
|
max-height: var(--card-max-height, 100%);
|
|
136
144
|
height: max-content;
|
|
137
145
|
min-height: 0;
|
|
138
|
-
opacity: 1;
|
|
139
146
|
width: 100%;
|
|
140
|
-
|
|
141
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
142
|
-
div.content.is-collapsed {
|
|
143
|
-
animation: accordion ease-out 0.15s 0s forwards;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
div.content.is-collapsed {
|
|
147
|
-
max-height: 0px;
|
|
148
|
-
opacity: 0;
|
|
149
|
-
overflow: hidden;
|
|
150
|
-
}
|
|
151
|
-
@media (prefers-reduced-motion: no-preference) {
|
|
152
|
-
@keyframes accordion {
|
|
153
|
-
0% {
|
|
154
|
-
opacity: 1;
|
|
155
|
-
max-height: var(--card-max-height, 90vh);
|
|
156
|
-
overflow: auto;
|
|
157
|
-
}
|
|
158
|
-
100% {
|
|
159
|
-
opacity: 0;
|
|
160
|
-
max-height: 0px;
|
|
161
|
-
overflow: hidden;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
@media (prefers-reduced-motion: reduce) {
|
|
167
|
-
.heading,
|
|
168
|
-
div.content,
|
|
169
|
-
div.content.is-collapsed {
|
|
170
|
-
animation-duration: 0.001ms !important;
|
|
171
|
-
transition-duration: 0.001ms !important;
|
|
172
|
-
}
|
|
147
|
+
overflow: auto;
|
|
173
148
|
}</style>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@component
|
|
5
|
+
A labelled, collapsible group for a side menu: a header row (label + chevron)
|
|
6
|
+
that expands and collapses the items supplied as the default snippet. Pair
|
|
7
|
+
several of these in a menu column to build a grouped navigation tree.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import { usePrefersReducedMotion } from '../Helpers/prefersReducedMotion.svelte.js';
|
|
11
|
+
import { Colour } from '../scss/colours.js';
|
|
12
|
+
import { Size } from '../Types/Internal/Size.js';
|
|
13
|
+
import { cubicOut } from 'svelte/easing';
|
|
14
|
+
import { slide } from 'svelte/transition';
|
|
15
|
+
import type { CollapsibleMenuGroupProps } from './CollapsibleMenuGroupProps.js';
|
|
16
|
+
import Icon from './Icon.svelte';
|
|
17
|
+
|
|
18
|
+
let { title, isCollapsed = $bindable(false), children }: CollapsibleMenuGroupProps = $props();
|
|
19
|
+
|
|
20
|
+
const reducedMotion = usePrefersReducedMotion();
|
|
21
|
+
const slideDuration = $derived(reducedMotion.matches ? 0 : 200);
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
<div class="menu-group">
|
|
25
|
+
<button
|
|
26
|
+
type="button"
|
|
27
|
+
class="group-header"
|
|
28
|
+
aria-expanded={!isCollapsed}
|
|
29
|
+
onclick={() => {
|
|
30
|
+
isCollapsed = !isCollapsed;
|
|
31
|
+
}}
|
|
32
|
+
>
|
|
33
|
+
<span class="group-title">{title}</span>
|
|
34
|
+
<span
|
|
35
|
+
class="chevron"
|
|
36
|
+
class:collapsed={isCollapsed}
|
|
37
|
+
>
|
|
38
|
+
<Icon
|
|
39
|
+
name="chevron-down"
|
|
40
|
+
size={Size.Small}
|
|
41
|
+
colour={Colour['$ui-blue-11']}
|
|
42
|
+
/>
|
|
43
|
+
</span>
|
|
44
|
+
</button>
|
|
45
|
+
|
|
46
|
+
{#if !isCollapsed}
|
|
47
|
+
<div
|
|
48
|
+
class="group-items"
|
|
49
|
+
transition:slide={{ duration: slideDuration, easing: cubicOut }}
|
|
50
|
+
>
|
|
51
|
+
{@render children()}
|
|
52
|
+
</div>
|
|
53
|
+
{/if}
|
|
54
|
+
</div>
|
|
55
|
+
|
|
56
|
+
<style>.menu-group {
|
|
57
|
+
border: solid 1px #cbd4da;
|
|
58
|
+
border-radius: 4px;
|
|
59
|
+
background-color: #ffffff;
|
|
60
|
+
overflow: hidden;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.group-header {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-flow: row nowrap;
|
|
66
|
+
align-items: center;
|
|
67
|
+
width: 100%;
|
|
68
|
+
gap: 0.5rem;
|
|
69
|
+
padding: 0.75rem 1.25rem;
|
|
70
|
+
font: inherit;
|
|
71
|
+
font-weight: 500;
|
|
72
|
+
color: #46525b;
|
|
73
|
+
text-align: start;
|
|
74
|
+
background: transparent;
|
|
75
|
+
border: none;
|
|
76
|
+
cursor: pointer;
|
|
77
|
+
}
|
|
78
|
+
.group-header .group-title {
|
|
79
|
+
flex-grow: 1;
|
|
80
|
+
}
|
|
81
|
+
.group-header .chevron {
|
|
82
|
+
display: inline-flex;
|
|
83
|
+
transition: transform 0.15s ease-out;
|
|
84
|
+
}
|
|
85
|
+
.group-header .chevron.collapsed {
|
|
86
|
+
transform: rotate(-90deg);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
.group-items {
|
|
90
|
+
display: flex;
|
|
91
|
+
flex-flow: column nowrap;
|
|
92
|
+
border-top: solid 1px #cbd4da;
|
|
93
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { CollapsibleMenuGroupProps } from './CollapsibleMenuGroupProps.js';
|
|
2
|
+
/**
|
|
3
|
+
* A labelled, collapsible group for a side menu: a header row (label + chevron)
|
|
4
|
+
* that expands and collapses the items supplied as the default snippet. Pair
|
|
5
|
+
* several of these in a menu column to build a grouped navigation tree.
|
|
6
|
+
*/
|
|
7
|
+
declare const CollapsibleMenuGroup: import("svelte").Component<CollapsibleMenuGroupProps, {}, "isCollapsed">;
|
|
8
|
+
type CollapsibleMenuGroup = ReturnType<typeof CollapsibleMenuGroup>;
|
|
9
|
+
export default CollapsibleMenuGroup;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link CollapsibleMenuGroup} — a labelled group in a side menu
|
|
4
|
+
* whose items collapse and expand under a clickable header. The items are
|
|
5
|
+
* supplied as the default snippet.
|
|
6
|
+
*/
|
|
7
|
+
export interface CollapsibleMenuGroupProps {
|
|
8
|
+
/** Header label. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Two-way bindable collapsed state. Defaults to expanded. */
|
|
11
|
+
isCollapsed?: boolean;
|
|
12
|
+
/** The menu items revealed when expanded. */
|
|
13
|
+
children: Snippet;
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -99,6 +99,7 @@
|
|
|
99
99
|
</script>
|
|
100
100
|
|
|
101
101
|
<script lang="ts">
|
|
102
|
+
import { loaderDefaults } from '../Helpers/loaderDefaults.svelte.js';
|
|
102
103
|
import type { LoadingAnimationProps } from './LoadingAnimationProps.js';
|
|
103
104
|
|
|
104
105
|
const {
|
|
@@ -114,7 +115,12 @@
|
|
|
114
115
|
// The word the box spells: the caller's first name, or the product name as a
|
|
115
116
|
// friendly fallback. Block capitals tumble cleanly and dodge lowercase
|
|
116
117
|
// descenders clipping the baseline on landing.
|
|
117
|
-
|
|
118
|
+
// Explicit `name` prop wins; otherwise fall back to the app-wide default
|
|
119
|
+
// (set by the host so internally-rendered loaders also show the user), then
|
|
120
|
+
// finally the product name.
|
|
121
|
+
const word = $derived(
|
|
122
|
+
(name.trim() || loaderDefaults.name.trim() || 'FloWMS').toUpperCase()
|
|
123
|
+
);
|
|
118
124
|
|
|
119
125
|
// Caption text with any trailing dots/ellipsis stripped, since the pill
|
|
120
126
|
// appends its own animated "..." — avoids "moment……" style doubling.
|
|
@@ -1107,21 +1113,30 @@
|
|
|
1107
1113
|
top: 0;
|
|
1108
1114
|
}
|
|
1109
1115
|
.flowms-loader {
|
|
1110
|
-
/* Caption pill: sits above the box and on top of every other layer.
|
|
1116
|
+
/* Caption pill: sits above the box and on top of every other layer. Its
|
|
1117
|
+
bottom edge is anchored just above the opened flaps, so longer messages
|
|
1118
|
+
wrap and grow UPWARD into the headroom rather than down over the box. */
|
|
1111
1119
|
}
|
|
1112
1120
|
.flowms-loader .caption {
|
|
1113
1121
|
position: absolute;
|
|
1114
1122
|
left: 50%;
|
|
1115
|
-
|
|
1123
|
+
bottom: calc(100% - 50px);
|
|
1116
1124
|
transform: translateX(-50%);
|
|
1117
|
-
|
|
1125
|
+
/* `width: max-content` opts out of the half-container shrink that
|
|
1126
|
+
`left: 50%` would otherwise impose, so the pill sizes to its text up
|
|
1127
|
+
to the max-width (then wraps). */
|
|
1128
|
+
width: max-content;
|
|
1129
|
+
max-width: min(380px, 90vw);
|
|
1130
|
+
white-space: normal;
|
|
1131
|
+
text-align: center;
|
|
1132
|
+
text-wrap: balance;
|
|
1118
1133
|
padding: 7px 16px;
|
|
1119
1134
|
border-radius: 999px;
|
|
1120
1135
|
background: rgba(255, 255, 255, 0.82);
|
|
1121
1136
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
|
1122
1137
|
-webkit-backdrop-filter: blur(3px);
|
|
1123
1138
|
backdrop-filter: blur(3px);
|
|
1124
|
-
font: 600 14px/1.
|
|
1139
|
+
font: 600 14px/1.3 system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
1125
1140
|
color: #44515a;
|
|
1126
1141
|
letter-spacing: 0.2px;
|
|
1127
1142
|
}
|
|
@@ -35,13 +35,9 @@
|
|
|
35
35
|
aria-live="polite"
|
|
36
36
|
>
|
|
37
37
|
<div class="modal-root">
|
|
38
|
-
{#if message}
|
|
39
|
-
<p>{message}</p>
|
|
40
|
-
{/if}
|
|
41
38
|
<LoadingAnimation
|
|
42
39
|
{name}
|
|
43
|
-
{caption}
|
|
44
|
-
showText={!message}
|
|
40
|
+
caption={message ?? caption}
|
|
45
41
|
/>
|
|
46
42
|
</div>
|
|
47
43
|
</dialog>
|
|
@@ -310,17 +306,4 @@ dialog .modal-root {
|
|
|
310
306
|
align-items: center;
|
|
311
307
|
background: rgba(0, 0, 0, 0);
|
|
312
308
|
position: relative;
|
|
313
|
-
}
|
|
314
|
-
dialog .modal-root p {
|
|
315
|
-
position: absolute;
|
|
316
|
-
left: 50%;
|
|
317
|
-
top: 50%;
|
|
318
|
-
z-index: 99999;
|
|
319
|
-
translate: -50% -80%;
|
|
320
|
-
user-select: none;
|
|
321
|
-
text-shadow: 0px 0px 3px #000000;
|
|
322
|
-
background-color: rgba(0, 0, 0, 0.4);
|
|
323
|
-
border-radius: 4px;
|
|
324
|
-
padding: 1rem;
|
|
325
|
-
color: #ffffff;
|
|
326
309
|
}</style>
|
|
@@ -17,13 +17,9 @@
|
|
|
17
17
|
? `border-radius: ${borderRadius};`
|
|
18
18
|
: undefined}
|
|
19
19
|
>
|
|
20
|
-
{#if message}
|
|
21
|
-
<p>{message}</p>
|
|
22
|
-
{/if}
|
|
23
20
|
<LoadingAnimation
|
|
24
21
|
{name}
|
|
25
|
-
{caption}
|
|
26
|
-
showText={!message}
|
|
22
|
+
caption={message ?? caption}
|
|
27
23
|
/>
|
|
28
24
|
</div>
|
|
29
25
|
|
|
@@ -43,19 +39,6 @@
|
|
|
43
39
|
flex-flow: column;
|
|
44
40
|
gap: 2rem;
|
|
45
41
|
}
|
|
46
|
-
.loading-overlay p {
|
|
47
|
-
position: absolute;
|
|
48
|
-
left: 50%;
|
|
49
|
-
top: 50%;
|
|
50
|
-
z-index: 99999;
|
|
51
|
-
translate: -50% -80%;
|
|
52
|
-
user-select: none;
|
|
53
|
-
text-shadow: 0px 0px 3px #000000;
|
|
54
|
-
background-color: rgba(0, 0, 0, 0.4);
|
|
55
|
-
border-radius: 4px;
|
|
56
|
-
padding: 1rem;
|
|
57
|
-
color: #ffffff;
|
|
58
|
-
}
|
|
59
42
|
.loading-overlay.absolute {
|
|
60
43
|
position: absolute;
|
|
61
44
|
}</style>
|
|
@@ -19,25 +19,41 @@ native radio group (the radio inputs are visually hidden but focusable).
|
|
|
19
19
|
import type { OptionCardsProps } from './OptionCardsProps.js';
|
|
20
20
|
import Icon from './Icon.svelte';
|
|
21
21
|
|
|
22
|
-
let {
|
|
22
|
+
let {
|
|
23
|
+
name,
|
|
24
|
+
options,
|
|
25
|
+
selected,
|
|
26
|
+
onchange,
|
|
27
|
+
multiple = false,
|
|
28
|
+
selectedValues,
|
|
29
|
+
ontoggle,
|
|
30
|
+
}: OptionCardsProps<T> = $props();
|
|
23
31
|
</script>
|
|
24
32
|
|
|
25
33
|
<div
|
|
26
34
|
class="option-cards"
|
|
27
|
-
role=
|
|
35
|
+
role={multiple ? 'group' : 'radiogroup'}
|
|
28
36
|
>
|
|
29
37
|
{#each options as option (option.value)}
|
|
30
|
-
{@const isSelected =
|
|
38
|
+
{@const isSelected = multiple
|
|
39
|
+
? (selectedValues?.includes(option.value) ?? false)
|
|
40
|
+
: selected === option.value}
|
|
31
41
|
<label
|
|
32
42
|
class="card"
|
|
33
43
|
class:selected={isSelected}
|
|
34
44
|
>
|
|
35
45
|
<input
|
|
36
46
|
class="native"
|
|
37
|
-
type=
|
|
47
|
+
type={multiple ? 'checkbox' : 'radio'}
|
|
38
48
|
{name}
|
|
39
49
|
checked={isSelected}
|
|
40
|
-
onchange={() =>
|
|
50
|
+
onchange={() => {
|
|
51
|
+
if (multiple) {
|
|
52
|
+
ontoggle?.(option.value, !isSelected);
|
|
53
|
+
} else {
|
|
54
|
+
void onchange?.(option.value);
|
|
55
|
+
}
|
|
56
|
+
}}
|
|
41
57
|
/>
|
|
42
58
|
{#if option.icon}
|
|
43
59
|
<Icon
|
|
@@ -12,8 +12,17 @@ export interface OptionCardsProps<T> {
|
|
|
12
12
|
name?: string;
|
|
13
13
|
/** The option set rendered as cards. */
|
|
14
14
|
options: ReadonlyArray<OptionCard<T>>;
|
|
15
|
-
/** Currently-selected value. */
|
|
15
|
+
/** Currently-selected value (single-select mode). */
|
|
16
16
|
selected?: T;
|
|
17
|
-
/** Called when a card is chosen. May be `void` or `Promise<void>`. */
|
|
17
|
+
/** Called when a card is chosen (single-select mode). May be `void` or `Promise<void>`. */
|
|
18
18
|
onchange?: (value: T) => void | Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* Enable multi-select: cards become independent checkboxes. Use with
|
|
21
|
+
* `selectedValues` and `ontoggle` instead of `selected` / `onchange`.
|
|
22
|
+
*/
|
|
23
|
+
multiple?: boolean;
|
|
24
|
+
/** Currently-selected values (multi-select mode). */
|
|
25
|
+
selectedValues?: ReadonlyArray<T>;
|
|
26
|
+
/** Called when a card is toggled on or off (multi-select mode). */
|
|
27
|
+
ontoggle?: (value: T, selected: boolean) => void;
|
|
19
28
|
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
<svelte:options runes />
|
|
2
|
+
|
|
3
|
+
<!--
|
|
4
|
+
@component
|
|
5
|
+
A titled card laid out as a fixed-width menu column beside a main content
|
|
6
|
+
area. Both regions are supplied by the consumer as snippets (`menu` and the
|
|
7
|
+
default `children`), so the component only owns the framing and layout.
|
|
8
|
+
-->
|
|
9
|
+
<script lang="ts">
|
|
10
|
+
import type { SidebarPanelProps } from './SidebarPanelProps.js';
|
|
11
|
+
|
|
12
|
+
let { title, subtitle, menu, children, menuWidth = '14rem' }: SidebarPanelProps = $props();
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<section class="sidebar-panel">
|
|
16
|
+
<header class="heading">
|
|
17
|
+
<h2>{title}</h2>
|
|
18
|
+
{#if subtitle}
|
|
19
|
+
<span class="subtitle">{subtitle}</span>
|
|
20
|
+
{/if}
|
|
21
|
+
</header>
|
|
22
|
+
|
|
23
|
+
<div class="body">
|
|
24
|
+
<aside
|
|
25
|
+
class="menu"
|
|
26
|
+
style="--menu-width: {menuWidth};"
|
|
27
|
+
>
|
|
28
|
+
{@render menu()}
|
|
29
|
+
</aside>
|
|
30
|
+
<div class="main">
|
|
31
|
+
{@render children()}
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<style>.sidebar-panel {
|
|
37
|
+
display: flex;
|
|
38
|
+
flex-flow: column nowrap;
|
|
39
|
+
background-color: #ffffff;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.1333333333);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.heading {
|
|
45
|
+
display: flex;
|
|
46
|
+
flex-flow: column nowrap;
|
|
47
|
+
gap: 0.125rem;
|
|
48
|
+
padding: 1.5rem;
|
|
49
|
+
border-bottom: solid 1px #dae0e5;
|
|
50
|
+
}
|
|
51
|
+
.heading h2 {
|
|
52
|
+
margin: 0;
|
|
53
|
+
color: #46525b;
|
|
54
|
+
font-weight: 500;
|
|
55
|
+
font-size: 1.25rem;
|
|
56
|
+
}
|
|
57
|
+
.heading .subtitle {
|
|
58
|
+
color: #647d8e;
|
|
59
|
+
font-size: 0.875rem;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.body {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-flow: row nowrap;
|
|
65
|
+
align-items: stretch;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.menu {
|
|
69
|
+
width: var(--menu-width, 14rem);
|
|
70
|
+
flex-shrink: 0;
|
|
71
|
+
display: flex;
|
|
72
|
+
flex-flow: column nowrap;
|
|
73
|
+
gap: 0.75rem;
|
|
74
|
+
padding: 1.5rem;
|
|
75
|
+
border-right: solid 1px #dae0e5;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.main {
|
|
79
|
+
flex: 1;
|
|
80
|
+
min-width: 0;
|
|
81
|
+
display: flex;
|
|
82
|
+
flex-flow: column nowrap;
|
|
83
|
+
gap: 1.5rem;
|
|
84
|
+
padding: 1.5rem;
|
|
85
|
+
}</style>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SidebarPanelProps } from './SidebarPanelProps.js';
|
|
2
|
+
/**
|
|
3
|
+
* A titled card laid out as a fixed-width menu column beside a main content
|
|
4
|
+
* area. Both regions are supplied by the consumer as snippets (`menu` and the
|
|
5
|
+
* default `children`), so the component only owns the framing and layout.
|
|
6
|
+
*/
|
|
7
|
+
declare const SidebarPanel: import("svelte").Component<SidebarPanelProps, {}, "">;
|
|
8
|
+
type SidebarPanel = ReturnType<typeof SidebarPanel>;
|
|
9
|
+
export default SidebarPanel;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
/**
|
|
3
|
+
* Props for {@link SidebarPanel} — a titled card with a fixed-width menu
|
|
4
|
+
* column on the left and a main content area on the right. Generic layout
|
|
5
|
+
* shell; the consumer supplies both regions as snippets.
|
|
6
|
+
*/
|
|
7
|
+
export interface SidebarPanelProps {
|
|
8
|
+
/** Heading shown at the top of the panel. */
|
|
9
|
+
title: string;
|
|
10
|
+
/** Optional sub-heading shown under the title. */
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
/** Left-hand menu column. */
|
|
13
|
+
menu: Snippet;
|
|
14
|
+
/** Main content area. */
|
|
15
|
+
children: Snippet;
|
|
16
|
+
/** Width of the menu column. Defaults to `'14rem'`. */
|
|
17
|
+
menuWidth?: string;
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global fallback configuration for all loaders.
|
|
3
|
+
*
|
|
4
|
+
* Loaders rendered *internally* by components like {@link Table},
|
|
5
|
+
* {@link DesktopModal} and {@link AppointmentCalendar} can't be handed a `name`
|
|
6
|
+
* prop at the call site. So the host app sets {@link loaderDefaults.name} once —
|
|
7
|
+
* typically in its root layout inside a browser `$effect` — and every loader
|
|
8
|
+
* (including those internal ones) spells that name. An explicit `name` prop on a
|
|
9
|
+
* specific loader still takes precedence.
|
|
10
|
+
*
|
|
11
|
+
* Only assign this on the client (inside an `$effect` / `onMount`). Assigning it
|
|
12
|
+
* during SSR would share one request's value with concurrent requests.
|
|
13
|
+
*/
|
|
14
|
+
export declare const loaderDefaults: {
|
|
15
|
+
name: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global fallback configuration for all loaders.
|
|
3
|
+
*
|
|
4
|
+
* Loaders rendered *internally* by components like {@link Table},
|
|
5
|
+
* {@link DesktopModal} and {@link AppointmentCalendar} can't be handed a `name`
|
|
6
|
+
* prop at the call site. So the host app sets {@link loaderDefaults.name} once —
|
|
7
|
+
* typically in its root layout inside a browser `$effect` — and every loader
|
|
8
|
+
* (including those internal ones) spells that name. An explicit `name` prop on a
|
|
9
|
+
* specific loader still takes precedence.
|
|
10
|
+
*
|
|
11
|
+
* Only assign this on the client (inside an `$effect` / `onMount`). Assigning it
|
|
12
|
+
* during SSR would share one request's value with concurrent requests.
|
|
13
|
+
*/
|
|
14
|
+
export const loaderDefaults = $state({ name: '' });
|
package/dist/index.d.ts
CHANGED
|
@@ -22,6 +22,10 @@ export { default as Checkbox } from './Components/Checkbox.svelte';
|
|
|
22
22
|
export type { CheckboxProps } from './Components/CheckboxProps.js';
|
|
23
23
|
export { default as CollapsibleCard } from './Components/CollapsibleCard.svelte';
|
|
24
24
|
export type { CollapsibleCardProps } from './Components/CollapsibleCardProps.js';
|
|
25
|
+
export { default as CollapsibleMenuGroup } from './Components/CollapsibleMenuGroup.svelte';
|
|
26
|
+
export type { CollapsibleMenuGroupProps } from './Components/CollapsibleMenuGroupProps.js';
|
|
27
|
+
export { default as SidebarPanel } from './Components/SidebarPanel.svelte';
|
|
28
|
+
export type { SidebarPanelProps } from './Components/SidebarPanelProps.js';
|
|
25
29
|
export { default as ConditionBuilder } from './Components/ConditionBuilder/ConditionBuilder.svelte';
|
|
26
30
|
export type { ConditionBuilderProps } from './Components/ConditionBuilder/ConditionBuilderProps.js';
|
|
27
31
|
export { makeGroup, makeLeaf, type Combinator, type ConditionGroup, type ConditionLeaf, type ConditionNode, } from './Components/ConditionBuilder/ConditionTree.js';
|
|
@@ -64,6 +68,7 @@ export type { InfoAlertProps } from './Components/InfoAlertProps.js';
|
|
|
64
68
|
export type { InputProps } from './Components/InputProps.js';
|
|
65
69
|
export { default as LoadingAnimation } from './Components/LoadingAnimation.svelte';
|
|
66
70
|
export type { LoadingAnimationProps } from './Components/LoadingAnimationProps.js';
|
|
71
|
+
export { loaderDefaults } from './Helpers/loaderDefaults.svelte.js';
|
|
67
72
|
export { default as LoadingModal } from './Components/LoadingModal.svelte';
|
|
68
73
|
export type { LoadingModalProps } from './Components/LoadingModalProps.js';
|
|
69
74
|
export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,8 @@ export {} from './Components/BubbleProps.js';
|
|
|
14
14
|
export { default as Button } from './Components/Button.svelte';
|
|
15
15
|
export { default as Checkbox } from './Components/Checkbox.svelte';
|
|
16
16
|
export { default as CollapsibleCard } from './Components/CollapsibleCard.svelte';
|
|
17
|
+
export { default as CollapsibleMenuGroup } from './Components/CollapsibleMenuGroup.svelte';
|
|
18
|
+
export { default as SidebarPanel } from './Components/SidebarPanel.svelte';
|
|
17
19
|
export { default as ConditionBuilder } from './Components/ConditionBuilder/ConditionBuilder.svelte';
|
|
18
20
|
export { makeGroup, makeLeaf, } from './Components/ConditionBuilder/ConditionTree.js';
|
|
19
21
|
export {} from './Components/Contexts/ContextMenuOptionProps.js';
|
|
@@ -40,6 +42,7 @@ export {} from './Components/IconProps.js';
|
|
|
40
42
|
export { icons } from './Components/Icons.js';
|
|
41
43
|
export { default as InfoAlert } from './Components/InfoAlert.svelte';
|
|
42
44
|
export { default as LoadingAnimation } from './Components/LoadingAnimation.svelte';
|
|
45
|
+
export { loaderDefaults } from './Helpers/loaderDefaults.svelte.js';
|
|
43
46
|
export { default as LoadingModal } from './Components/LoadingModal.svelte';
|
|
44
47
|
export { default as LoadingOverlay } from './Components/LoadingOverlay.svelte';
|
|
45
48
|
export { default as Modal } from './Components/Modal.svelte';
|