@streamscloud/kit 0.9.4 → 0.9.6
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/ui/avatar/cmp.avatar.svelte +6 -0
- package/dist/ui/avatar/cmp.avatar.svelte.d.ts +2 -0
- package/dist/ui/date-picker/cmp.date-picker.svelte +2 -1
- package/dist/ui/select/cmp.singleselect-async.svelte +1 -0
- package/dist/ui/select/multiselect-base.svelte +1 -0
- package/dist/ui/select/select-core.svelte.d.ts +1 -0
- package/dist/ui/select/select-core.svelte.js +9 -0
- package/dist/ui/select/select-listbox.svelte +5 -1
- package/dist/ui/stepper-dialog-layout/cmp.stepper-dialog-layout.svelte +197 -0
- package/dist/ui/stepper-dialog-layout/cmp.stepper-dialog-layout.svelte.d.ts +66 -0
- package/dist/ui/stepper-dialog-layout/index.d.ts +1 -0
- package/dist/ui/stepper-dialog-layout/index.js +1 -0
- package/dist/ui/{stepper-dialog/stepper-dialog-localization.d.ts → stepper-dialog-layout/stepper-dialog-layout-localization.d.ts} +1 -1
- package/dist/ui/{stepper-dialog/stepper-dialog-localization.js → stepper-dialog-layout/stepper-dialog-layout-localization.js} +14 -14
- package/package.json +4 -4
- package/dist/ui/stepper-dialog/cmp.stepper-dialog.svelte +0 -221
- package/dist/ui/stepper-dialog/cmp.stepper-dialog.svelte.d.ts +0 -17
- package/dist/ui/stepper-dialog/index.d.ts +0 -2
- package/dist/ui/stepper-dialog/index.js +0 -1
- package/dist/ui/stepper-dialog/stepper-dialog.d.ts +0 -22
- package/dist/ui/stepper-dialog/stepper-dialog.js +0 -26
- package/dist/ui/stepper-dialog/types.d.ts +0 -34
- package/dist/ui/stepper-dialog/types.js +0 -1
|
@@ -72,6 +72,8 @@ load / error / stub state machine.
|
|
|
72
72
|
| `--sc-kit--avatar--badge--size` | Badge mini-avatar box | 45% of the avatar diameter |
|
|
73
73
|
| `--sc-kit--avatar--badge--ring-color` | Badge separation ring (override on active/hover row backgrounds to blend) | `--sc-kit--color--bg--panel` |
|
|
74
74
|
| `--sc-kit--avatar--badge--ring-width` | Badge separation ring thickness | `1.5px` |
|
|
75
|
+
| `--sc-kit--avatar--badge--initials--background` | Badge initials chip background (contrast element of org-with-user) | `--sc-kit--color--accent` |
|
|
76
|
+
| `--sc-kit--avatar--badge--initials--color` | Badge initials chip text color | `--sc-kit--color--text--on-accent` |
|
|
75
77
|
-->
|
|
76
78
|
|
|
77
79
|
<style>.avatar {
|
|
@@ -86,6 +88,8 @@ load / error / stub state machine.
|
|
|
86
88
|
--_avatar--badge-size: var(--sc-kit--avatar--badge--size, calc(var(--_avatar--size) * 0.45));
|
|
87
89
|
--_avatar--badge-ring-color: var(--sc-kit--avatar--badge--ring-color, var(--sc-kit--color--bg--panel));
|
|
88
90
|
--_avatar--badge-ring-width: var(--sc-kit--avatar--badge--ring-width, 1.5px);
|
|
91
|
+
--_avatar--badge-initials-background: var(--sc-kit--avatar--badge--initials--background, var(--sc-kit--color--accent));
|
|
92
|
+
--_avatar--badge-initials-color: var(--sc-kit--avatar--badge--initials--color, var(--sc-kit--color--text--on-accent));
|
|
89
93
|
--_avatar--border-radius: var(--sc-kit--avatar--border-radius, 50%);
|
|
90
94
|
--sc-kit--image--border-radius: var(--_avatar--border-radius);
|
|
91
95
|
--sc-kit--image--background: var(--_avatar--background);
|
|
@@ -133,6 +137,8 @@ load / error / stub state machine.
|
|
|
133
137
|
.avatar__initials--badge {
|
|
134
138
|
border-radius: var(--sc-kit--radius--sm);
|
|
135
139
|
font-size: calc(var(--_avatar--badge-size) * 0.5);
|
|
140
|
+
background: var(--_avatar--badge-initials-background);
|
|
141
|
+
color: var(--_avatar--badge-initials-color);
|
|
136
142
|
}
|
|
137
143
|
.avatar__badge {
|
|
138
144
|
position: absolute;
|
|
@@ -35,6 +35,8 @@ type Props = {
|
|
|
35
35
|
* | `--sc-kit--avatar--badge--size` | Badge mini-avatar box | 45% of the avatar diameter |
|
|
36
36
|
* | `--sc-kit--avatar--badge--ring-color` | Badge separation ring (override on active/hover row backgrounds to blend) | `--sc-kit--color--bg--panel` |
|
|
37
37
|
* | `--sc-kit--avatar--badge--ring-width` | Badge separation ring thickness | `1.5px` |
|
|
38
|
+
* | `--sc-kit--avatar--badge--initials--background` | Badge initials chip background (contrast element of org-with-user) | `--sc-kit--color--accent` |
|
|
39
|
+
* | `--sc-kit--avatar--badge--initials--color` | Badge initials chip text color | `--sc-kit--color--text--on-accent` |
|
|
38
40
|
*/
|
|
39
41
|
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
40
42
|
type Cmp = ReturnType<typeof Cmp>;
|
|
@@ -156,7 +156,8 @@ $effect(() => {
|
|
|
156
156
|
{/if}
|
|
157
157
|
|
|
158
158
|
{#if open}
|
|
159
|
-
<
|
|
159
|
+
<!-- preventDefault cancels FormField's <label> activation — a dead-space click inside the panel would forward to the trigger and close the calendar. -->
|
|
160
|
+
<div bind:this={panelEl} class="date-picker__panel" role="presentation" onclick={(e) => e.preventDefault()} onkeydown={() => undefined}>
|
|
160
161
|
<DatePickerCalendar
|
|
161
162
|
selectedDate={selectedDate}
|
|
162
163
|
minDate={minDate}
|
|
@@ -74,6 +74,7 @@ const showClear = $derived(mode.clearable && hasValue && isInteractive && !core.
|
|
|
74
74
|
class:singleselect--inert={inert}
|
|
75
75
|
class:singleselect--borderless={borderless}
|
|
76
76
|
onclick={core.handleClick}
|
|
77
|
+
onmousedown={core.handleMousedown}
|
|
77
78
|
onkeydown={() => undefined}
|
|
78
79
|
role="presentation">
|
|
79
80
|
{#if icon}
|
|
@@ -185,6 +185,7 @@ const handleDndFinalize = (e) => {
|
|
|
185
185
|
class:multiselect-trigger--inert={inert}
|
|
186
186
|
class:multiselect-trigger--borderless={borderless}
|
|
187
187
|
onclick={core.handleClick}
|
|
188
|
+
onmousedown={core.handleMousedown}
|
|
188
189
|
onkeydown={() => undefined}
|
|
189
190
|
role="presentation">
|
|
190
191
|
{#if icon}
|
|
@@ -73,6 +73,7 @@ export type SelectCore<T> = {
|
|
|
73
73
|
readonly interactionLocked: boolean;
|
|
74
74
|
handleFocus: () => void;
|
|
75
75
|
handleClick: (e: MouseEvent) => void;
|
|
76
|
+
handleMousedown: (e: MouseEvent) => void;
|
|
76
77
|
handleInput: (e: Event) => void;
|
|
77
78
|
handleKeydown: (e: KeyboardEvent) => void;
|
|
78
79
|
openPopover: () => void;
|
|
@@ -250,6 +250,8 @@ export function createSelectCore(config) {
|
|
|
250
250
|
};
|
|
251
251
|
const handleFocus = () => undefined;
|
|
252
252
|
const handleClick = (e) => {
|
|
253
|
+
// Cancels FormField's <label> activation — else its synthetic click re-toggles the popover on every pick.
|
|
254
|
+
e.preventDefault();
|
|
253
255
|
if (!config.getInteractive() || isFiltering) {
|
|
254
256
|
return;
|
|
255
257
|
}
|
|
@@ -268,6 +270,12 @@ export function createSelectCore(config) {
|
|
|
268
270
|
openPopover();
|
|
269
271
|
}
|
|
270
272
|
};
|
|
273
|
+
const handleMousedown = (e) => {
|
|
274
|
+
// Keeps the inner input focused — default mousedown on the chevron/padding blurs it (focus-ring flicker).
|
|
275
|
+
if (e.target !== config.getInputEl()) {
|
|
276
|
+
e.preventDefault();
|
|
277
|
+
}
|
|
278
|
+
};
|
|
271
279
|
const handleInput = (e) => {
|
|
272
280
|
if (!config.getInteractive()) {
|
|
273
281
|
return;
|
|
@@ -357,6 +365,7 @@ export function createSelectCore(config) {
|
|
|
357
365
|
},
|
|
358
366
|
handleFocus,
|
|
359
367
|
handleClick,
|
|
368
|
+
handleMousedown,
|
|
360
369
|
handleInput,
|
|
361
370
|
handleKeydown,
|
|
362
371
|
openPopover,
|
|
@@ -116,7 +116,11 @@ $effect(() => {
|
|
|
116
116
|
role="listbox"
|
|
117
117
|
id={listboxId}
|
|
118
118
|
tabindex="-1"
|
|
119
|
-
onclick={(e) =>
|
|
119
|
+
onclick={(e) => {
|
|
120
|
+
// preventDefault cancels FormField's <label> activation — stopPropagation alone doesn't.
|
|
121
|
+
e.preventDefault();
|
|
122
|
+
e.stopPropagation();
|
|
123
|
+
}}
|
|
120
124
|
onmousedown={(e) => e.stopPropagation()}
|
|
121
125
|
onkeydown={() => undefined}>
|
|
122
126
|
{#if headerSnippet}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
<script lang="ts" generics="TResult = void, TCancelResult = void">import { Button } from '../button';
|
|
2
|
+
import { Dialog, DialogCloseButton } from '../dialog';
|
|
3
|
+
import { IconSlot } from '../icon';
|
|
4
|
+
import { Stepper } from '../stepper';
|
|
5
|
+
import { StepperDialogLayoutLocalization } from './stepper-dialog-layout-localization';
|
|
6
|
+
let { controller, icon, title, subtitle, steps, canAdvance = () => true, labels, children, on } = $props();
|
|
7
|
+
const localization = new StepperDialogLayoutLocalization();
|
|
8
|
+
let step = $state(0);
|
|
9
|
+
let submitting = $state(false);
|
|
10
|
+
const isFirstStep = $derived(step === 0);
|
|
11
|
+
const isLastStep = $derived(step === steps.length - 1);
|
|
12
|
+
const stepAllowed = $derived(canAdvance(step));
|
|
13
|
+
const showCloseButton = $derived(!controller.settings.nonCancelable);
|
|
14
|
+
const setStep = (next) => {
|
|
15
|
+
step = next;
|
|
16
|
+
on.stepChange?.(next);
|
|
17
|
+
};
|
|
18
|
+
const handleNext = async () => {
|
|
19
|
+
if (!stepAllowed) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (on.beforeNext && (await on.beforeNext(step)) === false) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
setStep(step + 1);
|
|
26
|
+
};
|
|
27
|
+
const handleBack = () => {
|
|
28
|
+
if (step > 0) {
|
|
29
|
+
setStep(step - 1);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const handleFinish = async () => {
|
|
33
|
+
if (!stepAllowed || submitting) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
submitting = true;
|
|
37
|
+
try {
|
|
38
|
+
await on.finish();
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('StepperDialogLayout: on.finish threw — surface errors inside the handler', error);
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
submitting = false;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const handleStepClick = (clicked) => {
|
|
48
|
+
if (clicked < step) {
|
|
49
|
+
setStep(clicked);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<div class="stepper-dialog-layout">
|
|
55
|
+
<Dialog controller={controller}>
|
|
56
|
+
{#snippet headerSection()}
|
|
57
|
+
<div class="stepper-dialog-layout__header">
|
|
58
|
+
{#if icon}
|
|
59
|
+
<span class="stepper-dialog-layout__icon" aria-hidden="true"><IconSlot icon={icon} /></span>
|
|
60
|
+
{/if}
|
|
61
|
+
<div class="stepper-dialog-layout__title-block">
|
|
62
|
+
<div class="stepper-dialog-layout__title">{title}</div>
|
|
63
|
+
{#if subtitle}
|
|
64
|
+
<div class="stepper-dialog-layout__subtitle">{subtitle}</div>
|
|
65
|
+
{/if}
|
|
66
|
+
</div>
|
|
67
|
+
{#if showCloseButton}
|
|
68
|
+
<DialogCloseButton controller={controller} />
|
|
69
|
+
{/if}
|
|
70
|
+
</div>
|
|
71
|
+
{/snippet}
|
|
72
|
+
|
|
73
|
+
{#snippet bodySection()}
|
|
74
|
+
<div class="stepper-dialog-layout__layout">
|
|
75
|
+
<div class="stepper-dialog-layout__sidebar">
|
|
76
|
+
<Stepper orientation="vertical" completed="checkmark" current={step} steps={steps} on={{ stepClick: handleStepClick }} />
|
|
77
|
+
</div>
|
|
78
|
+
<div class="stepper-dialog-layout__main">
|
|
79
|
+
<div class="stepper-dialog-layout__body">{@render children(step)}</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
{/snippet}
|
|
83
|
+
|
|
84
|
+
{#snippet footer()}
|
|
85
|
+
<Button type="button" variant="secondary" on={{ click: () => controller.cancel() }}>{labels?.cancel ?? localization.cancel}</Button>
|
|
86
|
+
{#if !isFirstStep}
|
|
87
|
+
<Button type="button" variant="ghost" on={{ click: handleBack }}>{labels?.back ?? localization.back}</Button>
|
|
88
|
+
{/if}
|
|
89
|
+
{#if isLastStep}
|
|
90
|
+
<Button type="button" variant="primary" loading={submitting} disabled={!stepAllowed || submitting} on={{ click: () => void handleFinish() }}
|
|
91
|
+
>{labels?.finish ?? localization.finish}</Button>
|
|
92
|
+
{:else}
|
|
93
|
+
<Button type="button" variant="primary" disabled={!stepAllowed} on={{ click: () => void handleNext() }}>{labels?.next ?? localization.next}</Button>
|
|
94
|
+
{/if}
|
|
95
|
+
{/snippet}
|
|
96
|
+
</Dialog>
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<!--
|
|
100
|
+
@component
|
|
101
|
+
StepperDialogLayout — layout scaffold for multi-step wizard dialog views: header (icon / title /
|
|
102
|
+
subtitle / close), vertical Stepper sidebar, scrollable body, and a Cancel / Back / Next / Finish
|
|
103
|
+
footer. Render it inside your own dialog view opened via `Dialogs.open()` — the layout owns step
|
|
104
|
+
navigation and the submitting state; your `on.finish` owns closing (`controller.ok(...)`).
|
|
105
|
+
Completed sidebar steps are clickable to go back; `canAdvance(step)` gates Next / Finish.
|
|
106
|
+
|
|
107
|
+
### CSS Custom Properties
|
|
108
|
+
| Property | Description | Default |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `--sc-kit--stepper-dialog-layout--sidebar--width` | Stepper sidebar width | `20rem` (320px) |
|
|
111
|
+
| `--sc-kit--stepper-dialog-layout--sidebar--background` | Sidebar background | `--sc-kit--color--bg--field-alt` |
|
|
112
|
+
| `--sc-kit--stepper-dialog-layout--height` | Dialog height (forwarded to `--sc-kit--dialog--height`) | `auto` |
|
|
113
|
+
-->
|
|
114
|
+
|
|
115
|
+
<style>.stepper-dialog-layout {
|
|
116
|
+
--_sdl--sidebar-width: var(--sc-kit--stepper-dialog-layout--sidebar--width, functions.to-rem(320));
|
|
117
|
+
--_sdl--sidebar-background: var(--sc-kit--stepper-dialog-layout--sidebar--background, var(--sc-kit--color--bg--field-alt));
|
|
118
|
+
--sc-kit--dialog--height: var(--sc-kit--stepper-dialog-layout--height, auto);
|
|
119
|
+
--sc-kit--dialog--body--overflow-y: auto;
|
|
120
|
+
display: contents;
|
|
121
|
+
}
|
|
122
|
+
.stepper-dialog-layout__header {
|
|
123
|
+
display: flex;
|
|
124
|
+
align-items: flex-start;
|
|
125
|
+
gap: var(--sc-kit--space--3);
|
|
126
|
+
padding: var(--sc-kit--space--5) var(--sc-kit--space--6);
|
|
127
|
+
border-block-end: 1px solid var(--sc-kit--color--border);
|
|
128
|
+
}
|
|
129
|
+
.stepper-dialog-layout__icon {
|
|
130
|
+
--sc-kit--icon--size: 1.5rem;
|
|
131
|
+
display: inline-flex;
|
|
132
|
+
align-items: center;
|
|
133
|
+
justify-content: center;
|
|
134
|
+
flex-shrink: 0;
|
|
135
|
+
inline-size: 2.5rem;
|
|
136
|
+
block-size: 2.5rem;
|
|
137
|
+
background: var(--sc-kit--color--bg--field-alt);
|
|
138
|
+
color: var(--sc-kit--color--text--primary);
|
|
139
|
+
border-radius: var(--sc-kit--radius--md);
|
|
140
|
+
}
|
|
141
|
+
.stepper-dialog-layout__title-block {
|
|
142
|
+
flex: 1;
|
|
143
|
+
min-inline-size: 0;
|
|
144
|
+
display: flex;
|
|
145
|
+
flex-direction: column;
|
|
146
|
+
gap: 0.125rem;
|
|
147
|
+
}
|
|
148
|
+
.stepper-dialog-layout__title {
|
|
149
|
+
font-size: var(--sc-kit--font-size--lg);
|
|
150
|
+
font-weight: var(--sc-kit--font-weight--semibold);
|
|
151
|
+
line-height: var(--sc-kit--leading--tight);
|
|
152
|
+
color: var(--sc-kit--color--text--primary);
|
|
153
|
+
}
|
|
154
|
+
.stepper-dialog-layout__subtitle {
|
|
155
|
+
font-size: var(--sc-kit--font-size--sm);
|
|
156
|
+
color: var(--sc-kit--color--text--muted);
|
|
157
|
+
line-height: var(--sc-kit--leading--normal);
|
|
158
|
+
}
|
|
159
|
+
.stepper-dialog-layout__layout {
|
|
160
|
+
display: flex;
|
|
161
|
+
flex: 1;
|
|
162
|
+
min-block-size: 0;
|
|
163
|
+
block-size: 100%;
|
|
164
|
+
}
|
|
165
|
+
@media screen and (max-width: 768px) {
|
|
166
|
+
.stepper-dialog-layout__layout {
|
|
167
|
+
flex-direction: column;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
.stepper-dialog-layout__sidebar {
|
|
171
|
+
flex: 0 0 var(--_sdl--sidebar-width);
|
|
172
|
+
padding: var(--sc-kit--space--5) var(--sc-kit--space--4);
|
|
173
|
+
background: var(--_sdl--sidebar-background);
|
|
174
|
+
border-inline-end: 1px solid var(--sc-kit--color--border);
|
|
175
|
+
overflow-y: auto;
|
|
176
|
+
min-block-size: 0;
|
|
177
|
+
}
|
|
178
|
+
@media screen and (max-width: 768px) {
|
|
179
|
+
.stepper-dialog-layout__sidebar {
|
|
180
|
+
flex: 0 0 auto;
|
|
181
|
+
border-inline-end: none;
|
|
182
|
+
border-block-end: 1px solid var(--sc-kit--color--border);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
.stepper-dialog-layout__main {
|
|
186
|
+
flex: 1 1 auto;
|
|
187
|
+
display: flex;
|
|
188
|
+
flex-direction: column;
|
|
189
|
+
min-inline-size: 0;
|
|
190
|
+
min-block-size: 0;
|
|
191
|
+
}
|
|
192
|
+
.stepper-dialog-layout__body {
|
|
193
|
+
flex: 1;
|
|
194
|
+
min-block-size: 0;
|
|
195
|
+
overflow-y: auto;
|
|
196
|
+
padding: var(--sc-kit--space--5) var(--sc-kit--space--6);
|
|
197
|
+
}</style>
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type DialogController } from '../dialog';
|
|
2
|
+
import { type IconProp } from '../icon';
|
|
3
|
+
import { type Step } from '../stepper';
|
|
4
|
+
import type { Snippet } from 'svelte';
|
|
5
|
+
declare function $$render<TResult = void, TCancelResult = void>(): {
|
|
6
|
+
props: {
|
|
7
|
+
controller: DialogController<TResult, TCancelResult>;
|
|
8
|
+
/** Leading header icon — string SVG source, `{ src, color?, size? }` object, or custom snippet. */
|
|
9
|
+
icon?: IconProp;
|
|
10
|
+
title: string;
|
|
11
|
+
subtitle?: string;
|
|
12
|
+
steps: Step[];
|
|
13
|
+
/** Reactive gate for Next / Finish per step index. @default () => true */
|
|
14
|
+
canAdvance?: (step: number) => boolean;
|
|
15
|
+
/** Per-button overrides of the localized footer labels. */
|
|
16
|
+
labels?: {
|
|
17
|
+
cancel?: string;
|
|
18
|
+
back?: string;
|
|
19
|
+
next?: string;
|
|
20
|
+
finish?: string;
|
|
21
|
+
};
|
|
22
|
+
/** Body content; receives the current step index. */
|
|
23
|
+
children: Snippet<[number]>;
|
|
24
|
+
on: {
|
|
25
|
+
/** Owns closing: call `controller.ok(...)` on success; return without closing to keep the dialog open. Surface errors inside the handler — rejections are absorbed (logged), not rethrown. */
|
|
26
|
+
finish: () => void | Promise<void>;
|
|
27
|
+
beforeNext?: (step: number) => boolean | void | Promise<boolean | void>;
|
|
28
|
+
stepChange?: (step: number) => void;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
exports: {};
|
|
32
|
+
bindings: "";
|
|
33
|
+
slots: {};
|
|
34
|
+
events: {};
|
|
35
|
+
};
|
|
36
|
+
declare class __sveltets_Render<TResult = void, TCancelResult = void> {
|
|
37
|
+
props(): ReturnType<typeof $$render<TResult, TCancelResult>>['props'];
|
|
38
|
+
events(): ReturnType<typeof $$render<TResult, TCancelResult>>['events'];
|
|
39
|
+
slots(): ReturnType<typeof $$render<TResult, TCancelResult>>['slots'];
|
|
40
|
+
bindings(): "";
|
|
41
|
+
exports(): {};
|
|
42
|
+
}
|
|
43
|
+
interface $$IsomorphicComponent {
|
|
44
|
+
new <TResult = void, TCancelResult = void>(options: import('svelte').ComponentConstructorOptions<ReturnType<__sveltets_Render<TResult, TCancelResult>['props']>>): import('svelte').SvelteComponent<ReturnType<__sveltets_Render<TResult, TCancelResult>['props']>, ReturnType<__sveltets_Render<TResult, TCancelResult>['events']>, ReturnType<__sveltets_Render<TResult, TCancelResult>['slots']>> & {
|
|
45
|
+
$$bindings?: ReturnType<__sveltets_Render<TResult, TCancelResult>['bindings']>;
|
|
46
|
+
} & ReturnType<__sveltets_Render<TResult, TCancelResult>['exports']>;
|
|
47
|
+
<TResult = void, TCancelResult = void>(internal: unknown, props: ReturnType<__sveltets_Render<TResult, TCancelResult>['props']> & {}): ReturnType<__sveltets_Render<TResult, TCancelResult>['exports']>;
|
|
48
|
+
z_$$bindings?: ReturnType<__sveltets_Render<any, any>['bindings']>;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* StepperDialogLayout — layout scaffold for multi-step wizard dialog views: header (icon / title /
|
|
52
|
+
* subtitle / close), vertical Stepper sidebar, scrollable body, and a Cancel / Back / Next / Finish
|
|
53
|
+
* footer. Render it inside your own dialog view opened via `Dialogs.open()` — the layout owns step
|
|
54
|
+
* navigation and the submitting state; your `on.finish` owns closing (`controller.ok(...)`).
|
|
55
|
+
* Completed sidebar steps are clickable to go back; `canAdvance(step)` gates Next / Finish.
|
|
56
|
+
*
|
|
57
|
+
* ### CSS Custom Properties
|
|
58
|
+
* | Property | Description | Default |
|
|
59
|
+
* |---|---|---|
|
|
60
|
+
* | `--sc-kit--stepper-dialog-layout--sidebar--width` | Stepper sidebar width | `20rem` (320px) |
|
|
61
|
+
* | `--sc-kit--stepper-dialog-layout--sidebar--background` | Sidebar background | `--sc-kit--color--bg--field-alt` |
|
|
62
|
+
* | `--sc-kit--stepper-dialog-layout--height` | Dialog height (forwarded to `--sc-kit--dialog--height`) | `auto` |
|
|
63
|
+
*/
|
|
64
|
+
declare const Cmp: $$IsomorphicComponent;
|
|
65
|
+
type Cmp<TResult = void, TCancelResult = void> = InstanceType<typeof Cmp<TResult, TCancelResult>>;
|
|
66
|
+
export default Cmp;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StepperDialogLayout } from './cmp.stepper-dialog-layout.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as StepperDialogLayout } from './cmp.stepper-dialog-layout.svelte';
|
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import { AppLocale } from '../../core/locale';
|
|
2
|
-
export class StepperDialogLocalization {
|
|
3
|
-
get cancel() {
|
|
4
|
-
return loc.cancel[AppLocale.current];
|
|
5
|
-
}
|
|
6
|
-
get back() {
|
|
7
|
-
return loc.back[AppLocale.current];
|
|
8
|
-
}
|
|
9
|
-
get next() {
|
|
10
|
-
return loc.next[AppLocale.current];
|
|
11
|
-
}
|
|
12
|
-
get finish() {
|
|
13
|
-
return loc.finish[AppLocale.current];
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
2
|
const loc = {
|
|
17
3
|
cancel: {
|
|
18
4
|
en: 'Cancel',
|
|
@@ -31,3 +17,17 @@ const loc = {
|
|
|
31
17
|
no: 'Fullfør'
|
|
32
18
|
}
|
|
33
19
|
};
|
|
20
|
+
export class StepperDialogLayoutLocalization {
|
|
21
|
+
get cancel() {
|
|
22
|
+
return loc.cancel[AppLocale.current];
|
|
23
|
+
}
|
|
24
|
+
get back() {
|
|
25
|
+
return loc.back[AppLocale.current];
|
|
26
|
+
}
|
|
27
|
+
get next() {
|
|
28
|
+
return loc.next[AppLocale.current];
|
|
29
|
+
}
|
|
30
|
+
get finish() {
|
|
31
|
+
return loc.finish[AppLocale.current];
|
|
32
|
+
}
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamscloud/kit",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"author": "StreamsCloud",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -359,9 +359,9 @@
|
|
|
359
359
|
"types": "./dist/ui/stepper/index.d.ts",
|
|
360
360
|
"svelte": "./dist/ui/stepper/index.js"
|
|
361
361
|
},
|
|
362
|
-
"./ui/stepper-dialog": {
|
|
363
|
-
"types": "./dist/ui/stepper-dialog/index.d.ts",
|
|
364
|
-
"svelte": "./dist/ui/stepper-dialog/index.js"
|
|
362
|
+
"./ui/stepper-dialog-layout": {
|
|
363
|
+
"types": "./dist/ui/stepper-dialog-layout/index.d.ts",
|
|
364
|
+
"svelte": "./dist/ui/stepper-dialog-layout/index.js"
|
|
365
365
|
},
|
|
366
366
|
"./ui/swipe-indicator": {
|
|
367
367
|
"types": "./dist/ui/swipe-indicator/index.d.ts",
|
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
<script lang="ts">import { Button } from '../button';
|
|
2
|
-
import { Dialog, DialogCloseButton } from '../dialog';
|
|
3
|
-
import { Icon } from '../icon';
|
|
4
|
-
import { Stepper } from '../stepper';
|
|
5
|
-
import { StepperDialogLocalization } from './stepper-dialog-localization';
|
|
6
|
-
let { controller, data } = $props();
|
|
7
|
-
const localization = new StepperDialogLocalization();
|
|
8
|
-
let step = $state(0);
|
|
9
|
-
let submitting = $state(false);
|
|
10
|
-
const isFirstStep = $derived(step === 0);
|
|
11
|
-
const isLastStep = $derived(step === data.steps.length - 1);
|
|
12
|
-
const canAdvance = $derived(data.canAdvance(step));
|
|
13
|
-
const showCloseButton = $derived(!controller.settings.nonCancelable);
|
|
14
|
-
const setStep = (i) => {
|
|
15
|
-
step = i;
|
|
16
|
-
data.on.stepChange?.(i);
|
|
17
|
-
};
|
|
18
|
-
const handleNext = async () => {
|
|
19
|
-
if (!canAdvance) {
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
if (data.on.beforeNext) {
|
|
23
|
-
const result = await data.on.beforeNext(step);
|
|
24
|
-
if (result === false) {
|
|
25
|
-
return;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
setStep(step + 1);
|
|
29
|
-
};
|
|
30
|
-
const handleBack = () => {
|
|
31
|
-
if (step > 0) {
|
|
32
|
-
setStep(step - 1);
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
const handleFinish = async () => {
|
|
36
|
-
if (!canAdvance || submitting) {
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
39
|
-
submitting = true;
|
|
40
|
-
try {
|
|
41
|
-
await data.on.finish();
|
|
42
|
-
controller.ok();
|
|
43
|
-
}
|
|
44
|
-
finally {
|
|
45
|
-
submitting = false;
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
const handleStepClick = (i) => {
|
|
49
|
-
if (i < step) {
|
|
50
|
-
setStep(i);
|
|
51
|
-
}
|
|
52
|
-
};
|
|
53
|
-
</script>
|
|
54
|
-
|
|
55
|
-
{#snippet footerButtons()}
|
|
56
|
-
<Button type="button" variant="secondary" on={{ click: () => controller.cancel() }}>{data.cancelLabel ?? localization.cancel}</Button>
|
|
57
|
-
{#if !isFirstStep}
|
|
58
|
-
<Button type="button" variant="ghost" on={{ click: handleBack }}>{data.backLabel ?? localization.back}</Button>
|
|
59
|
-
{/if}
|
|
60
|
-
{#if isLastStep}
|
|
61
|
-
<Button type="button" variant="primary" loading={submitting} disabled={!canAdvance || submitting} on={{ click: handleFinish }}
|
|
62
|
-
>{data.finishLabel ?? localization.finish}</Button>
|
|
63
|
-
{:else}
|
|
64
|
-
<Button type="button" variant="primary" disabled={!canAdvance} on={{ click: handleNext }}>{data.nextLabel ?? localization.next}</Button>
|
|
65
|
-
{/if}
|
|
66
|
-
{/snippet}
|
|
67
|
-
|
|
68
|
-
{#snippet titleRow()}
|
|
69
|
-
<div class="stepper-dialog__title-row">
|
|
70
|
-
{#if data.icon}<span class="stepper-dialog__icon" aria-hidden="true"><Icon src={data.icon} /></span>{/if}
|
|
71
|
-
<div class="stepper-dialog__title-block">
|
|
72
|
-
<div class="stepper-dialog__title">{data.title}</div>
|
|
73
|
-
{#if data.subtitle}<div class="stepper-dialog__subtitle">{data.subtitle}</div>{/if}
|
|
74
|
-
</div>
|
|
75
|
-
{#if showCloseButton}<DialogCloseButton controller={controller} />{/if}
|
|
76
|
-
</div>
|
|
77
|
-
{/snippet}
|
|
78
|
-
|
|
79
|
-
<div
|
|
80
|
-
class="stepper-dialog stepper-dialog--{data.layout}"
|
|
81
|
-
style:--sc-kit--dialog--height={data.height}
|
|
82
|
-
style:--sc-kit--dialog--body--overflow-y={data.height ? 'auto' : null}
|
|
83
|
-
style:--_stepper-dialog--sidebar-background={data.sidebarBackground}>
|
|
84
|
-
{#if data.layout === 'side'}
|
|
85
|
-
<Dialog controller={controller}>
|
|
86
|
-
{#snippet headerSection()}
|
|
87
|
-
<div class="stepper-dialog__header stepper-dialog__header--side">
|
|
88
|
-
{@render titleRow()}
|
|
89
|
-
</div>
|
|
90
|
-
{/snippet}
|
|
91
|
-
{#snippet bodySection()}
|
|
92
|
-
<div class="stepper-dialog__layout">
|
|
93
|
-
<div class="stepper-dialog__sidebar">
|
|
94
|
-
<Stepper orientation="vertical" completed="checkmark" current={step} steps={data.steps} on={{ stepClick: handleStepClick }} />
|
|
95
|
-
</div>
|
|
96
|
-
<div class="stepper-dialog__main">
|
|
97
|
-
<div class="stepper-dialog__body">{@render data.content(step)}</div>
|
|
98
|
-
</div>
|
|
99
|
-
</div>
|
|
100
|
-
{/snippet}
|
|
101
|
-
{#snippet footer()}{@render footerButtons()}{/snippet}
|
|
102
|
-
</Dialog>
|
|
103
|
-
{:else}
|
|
104
|
-
<Dialog controller={controller}>
|
|
105
|
-
{#snippet headerSection()}
|
|
106
|
-
<div class="stepper-dialog__header stepper-dialog__header--top">
|
|
107
|
-
{@render titleRow()}
|
|
108
|
-
<div class="stepper-dialog__stepper">
|
|
109
|
-
<Stepper orientation="horizontal-inline" size="sm" completed="checkmark" current={step} steps={data.steps} on={{ stepClick: handleStepClick }} />
|
|
110
|
-
</div>
|
|
111
|
-
</div>
|
|
112
|
-
{/snippet}
|
|
113
|
-
{#snippet body()}{@render data.content(step)}{/snippet}
|
|
114
|
-
{#snippet footer()}{@render footerButtons()}{/snippet}
|
|
115
|
-
</Dialog>
|
|
116
|
-
{/if}
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<!--
|
|
120
|
-
@component
|
|
121
|
-
Internal view for `openStepperDialog`. Renders a multi-step dialog: header (icon + title + subtitle + close),
|
|
122
|
-
a stepper band (top layout) or vertical sidebar (side layout), the current step's body, and a Cancel / Back /
|
|
123
|
-
Next / Finish footer. The dialog owns step navigation and the submitting spinner; the consumer owns step
|
|
124
|
-
content (via `content` snippet) and the finish handler.
|
|
125
|
-
|
|
126
|
-
Never render this view directly — use the imperative `openStepperDialog(...)` from the module barrel.
|
|
127
|
-
-->
|
|
128
|
-
|
|
129
|
-
<style>.stepper-dialog {
|
|
130
|
-
--_stepper-dialog--sidebar-width: 20rem;
|
|
131
|
-
--_stepper-dialog--sidebar-background: var(--sc-kit--color--bg--field-alt);
|
|
132
|
-
--_stepper-dialog--header-background: var(--sc-kit--color--bg--field-alt);
|
|
133
|
-
--_stepper-dialog--header-border-color: var(--sc-kit--color--border);
|
|
134
|
-
display: contents;
|
|
135
|
-
}
|
|
136
|
-
.stepper-dialog__header {
|
|
137
|
-
display: flex;
|
|
138
|
-
flex-direction: column;
|
|
139
|
-
}
|
|
140
|
-
.stepper-dialog__header--side {
|
|
141
|
-
border-bottom: 1px solid var(--_stepper-dialog--header-border-color);
|
|
142
|
-
}
|
|
143
|
-
.stepper-dialog__title-row {
|
|
144
|
-
display: flex;
|
|
145
|
-
align-items: flex-start;
|
|
146
|
-
gap: var(--sc-kit--space--3);
|
|
147
|
-
padding: var(--sc-kit--space--5) var(--sc-kit--space--6);
|
|
148
|
-
}
|
|
149
|
-
.stepper-dialog__icon {
|
|
150
|
-
display: inline-flex;
|
|
151
|
-
align-items: center;
|
|
152
|
-
justify-content: center;
|
|
153
|
-
flex-shrink: 0;
|
|
154
|
-
width: 2.5rem;
|
|
155
|
-
height: 2.5rem;
|
|
156
|
-
background: var(--sc-kit--color--bg--field-alt);
|
|
157
|
-
color: var(--sc-kit--color--text--primary);
|
|
158
|
-
border-radius: var(--sc-kit--radius--md);
|
|
159
|
-
--sc-kit--icon--size: 1.5rem;
|
|
160
|
-
}
|
|
161
|
-
.stepper-dialog__title-block {
|
|
162
|
-
flex: 1;
|
|
163
|
-
min-width: 0;
|
|
164
|
-
display: flex;
|
|
165
|
-
flex-direction: column;
|
|
166
|
-
gap: 0.125rem;
|
|
167
|
-
}
|
|
168
|
-
.stepper-dialog__title {
|
|
169
|
-
font-size: var(--sc-kit--font-size--lg);
|
|
170
|
-
font-weight: var(--sc-kit--font-weight--semibold);
|
|
171
|
-
line-height: var(--sc-kit--leading--tight);
|
|
172
|
-
color: var(--sc-kit--color--text--primary);
|
|
173
|
-
}
|
|
174
|
-
.stepper-dialog__subtitle {
|
|
175
|
-
font-size: var(--sc-kit--font-size--sm);
|
|
176
|
-
color: var(--sc-kit--color--text--muted);
|
|
177
|
-
line-height: var(--sc-kit--leading--snug);
|
|
178
|
-
}
|
|
179
|
-
.stepper-dialog__stepper {
|
|
180
|
-
padding: var(--sc-kit--space--2) var(--sc-kit--space--6);
|
|
181
|
-
background: var(--_stepper-dialog--header-background);
|
|
182
|
-
}
|
|
183
|
-
.stepper-dialog__layout {
|
|
184
|
-
display: flex;
|
|
185
|
-
flex: 1;
|
|
186
|
-
min-height: 0;
|
|
187
|
-
height: 100%;
|
|
188
|
-
}
|
|
189
|
-
@media screen and (max-width: 768px) {
|
|
190
|
-
.stepper-dialog__layout {
|
|
191
|
-
flex-direction: column;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
.stepper-dialog__sidebar {
|
|
195
|
-
flex: 0 0 var(--_stepper-dialog--sidebar-width);
|
|
196
|
-
padding: var(--sc-kit--space--5) var(--sc-kit--space--4);
|
|
197
|
-
background: var(--_stepper-dialog--sidebar-background);
|
|
198
|
-
border-right: 1px solid var(--sc-kit--color--border);
|
|
199
|
-
overflow-y: auto;
|
|
200
|
-
min-height: 0;
|
|
201
|
-
}
|
|
202
|
-
@media screen and (max-width: 768px) {
|
|
203
|
-
.stepper-dialog__sidebar {
|
|
204
|
-
flex: 0 0 auto;
|
|
205
|
-
border-right: none;
|
|
206
|
-
border-bottom: 1px solid var(--sc-kit--color--border);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
.stepper-dialog__main {
|
|
210
|
-
flex: 1 1 auto;
|
|
211
|
-
display: flex;
|
|
212
|
-
flex-direction: column;
|
|
213
|
-
min-width: 0;
|
|
214
|
-
min-height: 0;
|
|
215
|
-
}
|
|
216
|
-
.stepper-dialog__body {
|
|
217
|
-
flex: 1;
|
|
218
|
-
min-height: 0;
|
|
219
|
-
overflow-y: auto;
|
|
220
|
-
padding: var(--sc-kit--space--5) var(--sc-kit--space--6);
|
|
221
|
-
}</style>
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { type DialogController } from '../dialog';
|
|
2
|
-
import type { StepperDialogData } from './types';
|
|
3
|
-
type Props = {
|
|
4
|
-
controller: DialogController;
|
|
5
|
-
data: StepperDialogData;
|
|
6
|
-
};
|
|
7
|
-
/**
|
|
8
|
-
* Internal view for `openStepperDialog`. Renders a multi-step dialog: header (icon + title + subtitle + close),
|
|
9
|
-
* a stepper band (top layout) or vertical sidebar (side layout), the current step's body, and a Cancel / Back /
|
|
10
|
-
* Next / Finish footer. The dialog owns step navigation and the submitting spinner; the consumer owns step
|
|
11
|
-
* content (via `content` snippet) and the finish handler.
|
|
12
|
-
*
|
|
13
|
-
* Never render this view directly — use the imperative `openStepperDialog(...)` from the module barrel.
|
|
14
|
-
*/
|
|
15
|
-
declare const Cmp: import("svelte").Component<Props, {}, "">;
|
|
16
|
-
type Cmp = ReturnType<typeof Cmp>;
|
|
17
|
-
export default Cmp;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { openStepperDialog } from './stepper-dialog';
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { type DialogResult } from '../dialog';
|
|
2
|
-
import type { Step } from '../stepper';
|
|
3
|
-
import type { StepperDialogData, StepperDialogLayout } from './types';
|
|
4
|
-
import type { Snippet } from 'svelte';
|
|
5
|
-
type StepperDialogInit = {
|
|
6
|
-
icon?: string;
|
|
7
|
-
title: string;
|
|
8
|
-
subtitle?: string;
|
|
9
|
-
steps: Step[];
|
|
10
|
-
layout?: StepperDialogLayout;
|
|
11
|
-
sidebarBackground?: string;
|
|
12
|
-
canAdvance?: (step: number) => boolean;
|
|
13
|
-
cancelLabel?: string;
|
|
14
|
-
backLabel?: string;
|
|
15
|
-
nextLabel?: string;
|
|
16
|
-
finishLabel?: string;
|
|
17
|
-
height?: string;
|
|
18
|
-
content: Snippet<[number]>;
|
|
19
|
-
on: StepperDialogData['on'];
|
|
20
|
-
};
|
|
21
|
-
export declare const openStepperDialog: (init: StepperDialogInit) => Promise<DialogResult<void>>;
|
|
22
|
-
export {};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { Dialogs } from '../dialog';
|
|
2
|
-
import StepperDialogView from './cmp.stepper-dialog.svelte';
|
|
3
|
-
export const openStepperDialog = (init) => {
|
|
4
|
-
const data = {
|
|
5
|
-
icon: init.icon,
|
|
6
|
-
title: init.title,
|
|
7
|
-
subtitle: init.subtitle,
|
|
8
|
-
steps: init.steps,
|
|
9
|
-
layout: init.layout ?? 'top',
|
|
10
|
-
sidebarBackground: init.sidebarBackground,
|
|
11
|
-
canAdvance: init.canAdvance ?? (() => true),
|
|
12
|
-
cancelLabel: init.cancelLabel,
|
|
13
|
-
backLabel: init.backLabel,
|
|
14
|
-
nextLabel: init.nextLabel,
|
|
15
|
-
finishLabel: init.finishLabel,
|
|
16
|
-
height: init.height,
|
|
17
|
-
content: init.content,
|
|
18
|
-
on: init.on
|
|
19
|
-
};
|
|
20
|
-
return Dialogs.open({
|
|
21
|
-
view: StepperDialogView,
|
|
22
|
-
data,
|
|
23
|
-
explicitSettings: { closeOnEsc: true, closeOnClickOutside: false },
|
|
24
|
-
containerSettings: { size: data.layout === 'side' ? 'large' : 'medium' }
|
|
25
|
-
});
|
|
26
|
-
};
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { Step } from '../stepper';
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
3
|
-
export type StepperDialogLayout = 'top' | 'side';
|
|
4
|
-
export type StepperDialogData = {
|
|
5
|
-
icon?: string;
|
|
6
|
-
title: string;
|
|
7
|
-
subtitle?: string;
|
|
8
|
-
steps: Step[];
|
|
9
|
-
layout: StepperDialogLayout;
|
|
10
|
-
/** Side-layout sidebar background — any valid CSS `background` shorthand. No-op in `'top'` layout. */
|
|
11
|
-
sidebarBackground?: string;
|
|
12
|
-
/** Reactive: close over reactive state to enable/disable Next / Finish per step. @default () => true */
|
|
13
|
-
canAdvance: (step: number) => boolean;
|
|
14
|
-
/** Override the localized Cancel label. */
|
|
15
|
-
cancelLabel?: string;
|
|
16
|
-
/** Override the localized Back label. */
|
|
17
|
-
backLabel?: string;
|
|
18
|
-
/** Override the localized Next label. */
|
|
19
|
-
nextLabel?: string;
|
|
20
|
-
/** Override the localized Finish label. */
|
|
21
|
-
finishLabel?: string;
|
|
22
|
-
/** Fixed dialog height (any CSS length). When set, body scrolls inside the fixed frame. */
|
|
23
|
-
height?: string;
|
|
24
|
-
/** Body content — receives the current step index. */
|
|
25
|
-
content: Snippet<[number]>;
|
|
26
|
-
on: {
|
|
27
|
-
/** Called when the user clicks Finish from the last step. Dialog auto-closes with `controller.ok()` after this resolves; throw to keep it open. */
|
|
28
|
-
finish: () => void | Promise<void>;
|
|
29
|
-
/** Pre-advance hook. Return `false` to block Next. */
|
|
30
|
-
beforeNext?: (step: number) => boolean | void | Promise<boolean | void>;
|
|
31
|
-
/** Fires whenever the internal step index changes. */
|
|
32
|
-
stepChange?: (step: number) => void;
|
|
33
|
-
};
|
|
34
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|