@daltonr/pathwrite-svelte 0.5.0 → 0.6.0
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/README.md +75 -1
- package/dist/PathShell.svelte +56 -8
- package/dist/PathShell.svelte.d.ts +10 -1
- package/dist/PathShell.svelte.d.ts.map +1 -1
- package/dist/index.css +9 -0
- package/dist/index.svelte.d.ts +1 -1
- package/dist/index.svelte.d.ts.map +1 -1
- package/package.json +4 -3
- package/src/PathShell.svelte +56 -8
- package/src/index.svelte.ts +1 -0
package/README.md
CHANGED
|
@@ -105,6 +105,39 @@ npm install @daltonr/pathwrite-svelte
|
|
|
105
105
|
|
|
106
106
|
Each step is a **Svelte 5 snippet** whose name matches the step ID. PathShell collects them automatically and renders the active one.
|
|
107
107
|
|
|
108
|
+
> **⚠️ Important: Snippet Names Must Match Step IDs**
|
|
109
|
+
>
|
|
110
|
+
> When passing step content to `<PathShell>`, each snippet's name **must exactly match** the corresponding step's `id`:
|
|
111
|
+
>
|
|
112
|
+
> ```typescript
|
|
113
|
+
> const myPath = {
|
|
114
|
+
> id: 'signup',
|
|
115
|
+
> steps: [
|
|
116
|
+
> { id: 'details' }, // ← Step ID
|
|
117
|
+
> { id: 'review' } // ← Step ID
|
|
118
|
+
> ]
|
|
119
|
+
> };
|
|
120
|
+
> ```
|
|
121
|
+
>
|
|
122
|
+
> ```svelte
|
|
123
|
+
> <PathShell path={myPath}>
|
|
124
|
+
> {#snippet details()} <!-- ✅ Matches "details" step -->
|
|
125
|
+
> <DetailsForm />
|
|
126
|
+
> {/snippet}
|
|
127
|
+
> {#snippet review()} <!-- ✅ Matches "review" step -->
|
|
128
|
+
> <ReviewPanel />
|
|
129
|
+
> {/snippet}
|
|
130
|
+
> {#snippet foo()} <!-- ❌ No step with id "foo" -->
|
|
131
|
+
> <FooPanel />
|
|
132
|
+
> {/snippet}
|
|
133
|
+
> </PathShell>
|
|
134
|
+
> ```
|
|
135
|
+
>
|
|
136
|
+
> If a snippet name doesn't match any step ID, PathShell will render:
|
|
137
|
+
> **`No content for step "foo"`**
|
|
138
|
+
>
|
|
139
|
+
> **💡 Tip:** Use your IDE's "Go to Definition" on the step ID in your path definition, then copy-paste the exact string when creating the snippet. This ensures perfect matching and avoids typos.
|
|
140
|
+
|
|
108
141
|
---
|
|
109
142
|
|
|
110
143
|
## Simple vs Persisted
|
|
@@ -252,7 +285,8 @@ Default UI shell with progress indicator and navigation buttons.
|
|
|
252
285
|
| `completeLabel` | `string` | `"Complete"` | Complete button label |
|
|
253
286
|
| `cancelLabel` | `string` | `"Cancel"` | Cancel button label |
|
|
254
287
|
| `hideCancel` | `boolean` | `false` | Hide cancel button |
|
|
255
|
-
| `hideProgress` | `boolean` | `false` | Hide progress indicator |
|
|
288
|
+
| `hideProgress` | `boolean` | `false` | Hide progress indicator. Also hidden automatically for single-step top-level paths. |
|
|
289
|
+
| `footerLayout` | `"wizard" \| "form" \| "auto"` | `"auto"` | Footer button layout. `"auto"` uses `"form"` for single-step top-level paths, `"wizard"` otherwise. `"wizard"`: Back on left, Cancel+Submit on right. `"form"`: Cancel on left, Submit on right, no Back button. |
|
|
256
290
|
|
|
257
291
|
> **`path` vs `engine`:** Pass `path` for simple wizards where PathShell manages the engine. Pass `engine` when you create the engine yourself (e.g., via `restoreOrStart()` for persistence). These are mutually exclusive — don't pass both.
|
|
258
292
|
|
|
@@ -302,6 +336,46 @@ You can also override the header and footer:
|
|
|
302
336
|
</PathShell>
|
|
303
337
|
```
|
|
304
338
|
|
|
339
|
+
#### Resetting the path
|
|
340
|
+
|
|
341
|
+
There are two ways to reset `<PathShell>` to step 1.
|
|
342
|
+
|
|
343
|
+
**Option 1 — Toggle mount** (simplest, always correct)
|
|
344
|
+
|
|
345
|
+
Toggle a `$state` rune to destroy and recreate the shell:
|
|
346
|
+
|
|
347
|
+
```svelte
|
|
348
|
+
<script>
|
|
349
|
+
let isActive = $state(true);
|
|
350
|
+
</script>
|
|
351
|
+
|
|
352
|
+
{#if isActive}
|
|
353
|
+
<PathShell path={myPath} oncomplete={() => (isActive = false)}>
|
|
354
|
+
{#snippet details()}<DetailsStep />{/snippet}
|
|
355
|
+
</PathShell>
|
|
356
|
+
{:else}
|
|
357
|
+
<button onclick={() => (isActive = true)}>Try Again</button>
|
|
358
|
+
{/if}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
**Option 2 — Call `restart()` on the shell ref** (in-place, no unmount)
|
|
362
|
+
|
|
363
|
+
Use `bind:this` to get a reference to the shell instance, then call `restart()`:
|
|
364
|
+
|
|
365
|
+
```svelte
|
|
366
|
+
<script>
|
|
367
|
+
let shellRef;
|
|
368
|
+
</script>
|
|
369
|
+
|
|
370
|
+
<PathShell bind:this={shellRef} path={myPath} oncomplete={onDone}>
|
|
371
|
+
{#snippet details()}<DetailsStep />{/snippet}
|
|
372
|
+
</PathShell>
|
|
373
|
+
|
|
374
|
+
<button onclick={() => shellRef.restart()}>Try Again</button>
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
`restart()` resets the path engine to step 1 with the original `initialData` without unmounting the component. Use this when you need to keep the shell mounted — for example, to preserve scroll position or drive a CSS transition.
|
|
378
|
+
|
|
305
379
|
### `getPathContext<TData>()`
|
|
306
380
|
|
|
307
381
|
Get the path context from a parent `<PathShell>`. Use this inside step components.
|
package/dist/PathShell.svelte
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
import type { PathDefinition, PathData, PathEngine, PathSnapshot } from './index.svelte.js';
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
6
6
|
|
|
7
|
+
/** Converts a camelCase or lowercase field key to a display label. */
|
|
8
|
+
function formatFieldKey(key: string): string {
|
|
9
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase()).trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
interface Props {
|
|
8
13
|
path?: PathDefinition<any>;
|
|
9
14
|
engine?: PathEngine;
|
|
@@ -15,6 +20,13 @@
|
|
|
15
20
|
cancelLabel?: string;
|
|
16
21
|
hideCancel?: boolean;
|
|
17
22
|
hideProgress?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Footer layout mode:
|
|
25
|
+
* - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
|
|
26
|
+
* - "wizard": Back button on left, Cancel and Submit together on right.
|
|
27
|
+
* - "form": Cancel on left, Submit alone on right. Back button never shown.
|
|
28
|
+
*/
|
|
29
|
+
footerLayout?: "wizard" | "form" | "auto";
|
|
18
30
|
// Callback props replace event dispatching in Svelte 5
|
|
19
31
|
oncomplete?: (data: PathData) => void;
|
|
20
32
|
oncancel?: (data: PathData) => void;
|
|
@@ -37,6 +49,7 @@
|
|
|
37
49
|
cancelLabel = 'Cancel',
|
|
38
50
|
hideCancel = false,
|
|
39
51
|
hideProgress = false,
|
|
52
|
+
footerLayout = 'auto',
|
|
40
53
|
oncomplete,
|
|
41
54
|
oncancel,
|
|
42
55
|
onevent,
|
|
@@ -80,6 +93,26 @@
|
|
|
80
93
|
|
|
81
94
|
let snap = $derived(pathReturn.snapshot);
|
|
82
95
|
let actions = $derived({ next, previous, cancel, goToStep, goToStepChecked, setData, restart: () => restart(path, initialData) });
|
|
96
|
+
|
|
97
|
+
// Auto-detect footer layout: single-step top-level paths use "form", everything else uses "wizard"
|
|
98
|
+
let resolvedFooterLayout = $derived(
|
|
99
|
+
footerLayout === 'auto' && snap
|
|
100
|
+
? (snap.stepCount === 1 && snap.nestingLevel === 0 ? 'form' : 'wizard')
|
|
101
|
+
: (footerLayout === 'auto' ? 'wizard' : footerLayout)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Restart the active path from step 1 with the original `initialData`,
|
|
106
|
+
* without unmounting the shell. Use with `bind:this`:
|
|
107
|
+
*
|
|
108
|
+
* ```svelte
|
|
109
|
+
* <PathShell bind:this={shellRef} path={myPath} />
|
|
110
|
+
* <button onclick={() => shellRef.restart()}>Try Again</button>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function restart(): Promise<void> {
|
|
114
|
+
return pathReturn.restart(path, initialData);
|
|
115
|
+
}
|
|
83
116
|
</script>
|
|
84
117
|
|
|
85
118
|
<div class="pw-shell">
|
|
@@ -97,7 +130,7 @@
|
|
|
97
130
|
{#if !hideProgress}
|
|
98
131
|
{#if header}
|
|
99
132
|
{@render header(snap)}
|
|
100
|
-
{:else}
|
|
133
|
+
{:else if snap.stepCount > 1 || snap.nestingLevel > 0}
|
|
101
134
|
<div class="pw-shell__header">
|
|
102
135
|
<div class="pw-shell__steps">
|
|
103
136
|
{#each snap.steps as step, i}
|
|
@@ -125,11 +158,13 @@
|
|
|
125
158
|
{/if}
|
|
126
159
|
</div>
|
|
127
160
|
|
|
128
|
-
<!-- Validation messages -->
|
|
129
|
-
{#if snap.
|
|
161
|
+
<!-- Validation messages — labeled by field name -->
|
|
162
|
+
{#if snap.hasAttemptedNext && Object.keys(snap.fieldMessages).length > 0}
|
|
130
163
|
<ul class="pw-shell__validation">
|
|
131
|
-
{#each snap.
|
|
132
|
-
<li class="pw-shell__validation-item">
|
|
164
|
+
{#each Object.entries(snap.fieldMessages) as [key, msg]}
|
|
165
|
+
<li class="pw-shell__validation-item">
|
|
166
|
+
{#if key !== '_'}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
167
|
+
</li>
|
|
133
168
|
{/each}
|
|
134
169
|
</ul>
|
|
135
170
|
{/if}
|
|
@@ -140,7 +175,18 @@
|
|
|
140
175
|
{:else}
|
|
141
176
|
<div class="pw-shell__footer">
|
|
142
177
|
<div class="pw-shell__footer-left">
|
|
143
|
-
{#if !
|
|
178
|
+
{#if resolvedFooterLayout === 'form' && !hideCancel}
|
|
179
|
+
<!-- Form mode: Cancel on the left -->
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
class="pw-shell__btn pw-shell__btn--cancel"
|
|
183
|
+
disabled={snap.isNavigating}
|
|
184
|
+
onclick={cancel}
|
|
185
|
+
>
|
|
186
|
+
{cancelLabel}
|
|
187
|
+
</button>
|
|
188
|
+
{:else if resolvedFooterLayout === 'wizard' && !snap.isFirstStep}
|
|
189
|
+
<!-- Wizard mode: Back on the left -->
|
|
144
190
|
<button
|
|
145
191
|
type="button"
|
|
146
192
|
class="pw-shell__btn pw-shell__btn--back"
|
|
@@ -152,7 +198,8 @@
|
|
|
152
198
|
{/if}
|
|
153
199
|
</div>
|
|
154
200
|
<div class="pw-shell__footer-right">
|
|
155
|
-
{#if !hideCancel}
|
|
201
|
+
{#if resolvedFooterLayout === 'wizard' && !hideCancel}
|
|
202
|
+
<!-- Wizard mode: Cancel on the right -->
|
|
156
203
|
<button
|
|
157
204
|
type="button"
|
|
158
205
|
class="pw-shell__btn pw-shell__btn--cancel"
|
|
@@ -162,10 +209,11 @@
|
|
|
162
209
|
{cancelLabel}
|
|
163
210
|
</button>
|
|
164
211
|
{/if}
|
|
212
|
+
<!-- Both modes: Submit on the right -->
|
|
165
213
|
<button
|
|
166
214
|
type="button"
|
|
167
215
|
class="pw-shell__btn pw-shell__btn--next"
|
|
168
|
-
disabled={snap.isNavigating
|
|
216
|
+
disabled={snap.isNavigating}
|
|
169
217
|
onclick={next}
|
|
170
218
|
>
|
|
171
219
|
{snap.isLastStep ? completeLabel : nextLabel}
|
|
@@ -11,6 +11,13 @@ interface Props {
|
|
|
11
11
|
cancelLabel?: string;
|
|
12
12
|
hideCancel?: boolean;
|
|
13
13
|
hideProgress?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Footer layout mode:
|
|
16
|
+
* - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
|
|
17
|
+
* - "wizard": Back button on left, Cancel and Submit together on right.
|
|
18
|
+
* - "form": Cancel on left, Submit alone on right. Back button never shown.
|
|
19
|
+
*/
|
|
20
|
+
footerLayout?: "wizard" | "form" | "auto";
|
|
14
21
|
oncomplete?: (data: PathData) => void;
|
|
15
22
|
oncancel?: (data: PathData) => void;
|
|
16
23
|
onevent?: (event: any) => void;
|
|
@@ -18,7 +25,9 @@ interface Props {
|
|
|
18
25
|
footer?: Snippet<[PathSnapshot<any>, object]>;
|
|
19
26
|
[key: string]: Snippet | any;
|
|
20
27
|
}
|
|
21
|
-
declare const PathShell: import("svelte").Component<Props, {
|
|
28
|
+
declare const PathShell: import("svelte").Component<Props, {
|
|
29
|
+
restart: () => Promise<void>;
|
|
30
|
+
}, "">;
|
|
22
31
|
type PathShell = ReturnType<typeof PathShell>;
|
|
23
32
|
export default PathShell;
|
|
24
33
|
//# sourceMappingURL=PathShell.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PathShell.svelte.d.ts","sourceRoot":"","sources":["../src/PathShell.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGpC,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"PathShell.svelte.d.ts","sourceRoot":"","sources":["../src/PathShell.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAC5F,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAGpC,UAAU,KAAK;IACb,IAAI,CAAC,EAAE,cAAc,CAAC,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;IAE1C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACtC,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,IAAI,CAAC;IAE/B,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;IAE9C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,GAAG,GAAG,CAAC;CAC9B;AAuLH,QAAA,MAAM,SAAS;mBAlGQ,OAAO,CAAC,IAAI,CAAC;MAkGmB,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
package/dist/index.css
CHANGED
|
@@ -204,6 +204,15 @@
|
|
|
204
204
|
left: 4px;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
+
.pw-shell__validation-label {
|
|
208
|
+
font-weight: 600;
|
|
209
|
+
margin-right: 3px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.pw-shell__validation-label::after {
|
|
213
|
+
content: ":";
|
|
214
|
+
}
|
|
215
|
+
|
|
207
216
|
/* ------------------------------------------------------------------ */
|
|
208
217
|
/* Footer — navigation buttons */
|
|
209
218
|
/* ------------------------------------------------------------------ */
|
package/dist/index.svelte.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PathData, PathDefinition, PathEngine, PathEvent, PathSnapshot } from "@daltonr/pathwrite-core";
|
|
2
|
-
export type { PathData, PathDefinition, PathEngine, PathEvent, PathSnapshot, PathStep, PathStepContext, SerializedPathState } from "@daltonr/pathwrite-core";
|
|
2
|
+
export type { PathData, FieldErrors, PathDefinition, PathEngine, PathEvent, PathSnapshot, PathStep, PathStepContext, SerializedPathState } from "@daltonr/pathwrite-core";
|
|
3
3
|
export interface UsePathOptions {
|
|
4
4
|
/**
|
|
5
5
|
* An externally-managed `PathEngine` to subscribe to — for example, the engine
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/index.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,QAAQ,EACR,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACb,MAAM,yBAAyB,CAAC;AAIjC,YAAY,EACV,QAAQ,EACR,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,mBAAmB,EACpB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,WAAW,cAAc;IAC7B;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,mFAAmF;IACnF,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,KAAK,IAAI,CAAC;CACtC;AAED,MAAM,WAAW,aAAa,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ;IAC9D,sFAAsF;IACtF,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC9C,iCAAiC;IACjC,KAAK,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,6MAA6M;IAC7M,YAAY,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnH,6DAA6D;IAC7D,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,qJAAqJ;IACrJ,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,4CAA4C;IAC5C,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,iGAAiG;IACjG,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,oLAAoL;IACpL,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,gKAAgK;IAChK,OAAO,EAAE,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF;;;;OAIG;IACH,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,EAAE,WAAW,CAAC,EAAE,QAAQ,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,OAAO,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ,EACvD,OAAO,CAAC,EAAE,cAAc,GACvB,aAAa,CAAC,KAAK,CAAC,CAuDtB;AAQD,MAAM,WAAW,WAAW,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ;IAC5D,QAAQ,CAAC,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAC9C,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,eAAe,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,EAAE,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACpF,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ,KAAK,WAAW,CAAC,KAAK,CAAC,CAStF;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,SAAS,QAAQ,GAAG,QAAQ,EAAE,GAAG,EAAE,WAAW,CAAC,KAAK,CAAC,GAAG,IAAI,CAE/F;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,SAAS,QAAQ,EAAE,CAAC,SAAS,MAAM,GAAG,MAAM,KAAK,EAC7E,WAAW,EAAE,MAAM,YAAY,CAAC,KAAK,CAAC,GAAG,IAAI,EAC7C,OAAO,EAAE,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,EACzF,GAAG,EAAE,CAAC,GACL;IAAE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IAAC,GAAG,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAA;CAAE,CAS9D;AAGD,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/index.svelte.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daltonr/pathwrite-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Svelte 5 adapter for @daltonr/pathwrite-core — runes-based reactive bindings and optional PathShell component.",
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"svelte": "./dist/PathShell.svelte",
|
|
32
32
|
"import": "./dist/PathShell.svelte"
|
|
33
33
|
},
|
|
34
|
-
"./styles.css": "./dist/index.css"
|
|
34
|
+
"./styles.css": "./dist/index.css",
|
|
35
|
+
"./dist/index.css": "./dist/index.css"
|
|
35
36
|
},
|
|
36
37
|
"svelte": "./dist/index.svelte.js",
|
|
37
38
|
"main": "dist/index.svelte.js",
|
|
@@ -51,7 +52,7 @@
|
|
|
51
52
|
"svelte": ">=5.0.0"
|
|
52
53
|
},
|
|
53
54
|
"dependencies": {
|
|
54
|
-
"@daltonr/pathwrite-core": "^0.
|
|
55
|
+
"@daltonr/pathwrite-core": "^0.6.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
57
58
|
"@sveltejs/package": "^2.5.7",
|
package/src/PathShell.svelte
CHANGED
|
@@ -4,6 +4,11 @@
|
|
|
4
4
|
import type { PathDefinition, PathData, PathEngine, PathSnapshot } from './index.svelte.js';
|
|
5
5
|
import type { Snippet } from 'svelte';
|
|
6
6
|
|
|
7
|
+
/** Converts a camelCase or lowercase field key to a display label. */
|
|
8
|
+
function formatFieldKey(key: string): string {
|
|
9
|
+
return key.replace(/([A-Z])/g, ' $1').replace(/^./, c => c.toUpperCase()).trim();
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
interface Props {
|
|
8
13
|
path?: PathDefinition<any>;
|
|
9
14
|
engine?: PathEngine;
|
|
@@ -15,6 +20,13 @@
|
|
|
15
20
|
cancelLabel?: string;
|
|
16
21
|
hideCancel?: boolean;
|
|
17
22
|
hideProgress?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Footer layout mode:
|
|
25
|
+
* - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
|
|
26
|
+
* - "wizard": Back button on left, Cancel and Submit together on right.
|
|
27
|
+
* - "form": Cancel on left, Submit alone on right. Back button never shown.
|
|
28
|
+
*/
|
|
29
|
+
footerLayout?: "wizard" | "form" | "auto";
|
|
18
30
|
// Callback props replace event dispatching in Svelte 5
|
|
19
31
|
oncomplete?: (data: PathData) => void;
|
|
20
32
|
oncancel?: (data: PathData) => void;
|
|
@@ -37,6 +49,7 @@
|
|
|
37
49
|
cancelLabel = 'Cancel',
|
|
38
50
|
hideCancel = false,
|
|
39
51
|
hideProgress = false,
|
|
52
|
+
footerLayout = 'auto',
|
|
40
53
|
oncomplete,
|
|
41
54
|
oncancel,
|
|
42
55
|
onevent,
|
|
@@ -80,6 +93,26 @@
|
|
|
80
93
|
|
|
81
94
|
let snap = $derived(pathReturn.snapshot);
|
|
82
95
|
let actions = $derived({ next, previous, cancel, goToStep, goToStepChecked, setData, restart: () => restart(path, initialData) });
|
|
96
|
+
|
|
97
|
+
// Auto-detect footer layout: single-step top-level paths use "form", everything else uses "wizard"
|
|
98
|
+
let resolvedFooterLayout = $derived(
|
|
99
|
+
footerLayout === 'auto' && snap
|
|
100
|
+
? (snap.stepCount === 1 && snap.nestingLevel === 0 ? 'form' : 'wizard')
|
|
101
|
+
: (footerLayout === 'auto' ? 'wizard' : footerLayout)
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Restart the active path from step 1 with the original `initialData`,
|
|
106
|
+
* without unmounting the shell. Use with `bind:this`:
|
|
107
|
+
*
|
|
108
|
+
* ```svelte
|
|
109
|
+
* <PathShell bind:this={shellRef} path={myPath} />
|
|
110
|
+
* <button onclick={() => shellRef.restart()}>Try Again</button>
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
export function restart(): Promise<void> {
|
|
114
|
+
return pathReturn.restart(path, initialData);
|
|
115
|
+
}
|
|
83
116
|
</script>
|
|
84
117
|
|
|
85
118
|
<div class="pw-shell">
|
|
@@ -97,7 +130,7 @@
|
|
|
97
130
|
{#if !hideProgress}
|
|
98
131
|
{#if header}
|
|
99
132
|
{@render header(snap)}
|
|
100
|
-
{:else}
|
|
133
|
+
{:else if snap.stepCount > 1 || snap.nestingLevel > 0}
|
|
101
134
|
<div class="pw-shell__header">
|
|
102
135
|
<div class="pw-shell__steps">
|
|
103
136
|
{#each snap.steps as step, i}
|
|
@@ -125,11 +158,13 @@
|
|
|
125
158
|
{/if}
|
|
126
159
|
</div>
|
|
127
160
|
|
|
128
|
-
<!-- Validation messages -->
|
|
129
|
-
{#if snap.
|
|
161
|
+
<!-- Validation messages — labeled by field name -->
|
|
162
|
+
{#if snap.hasAttemptedNext && Object.keys(snap.fieldMessages).length > 0}
|
|
130
163
|
<ul class="pw-shell__validation">
|
|
131
|
-
{#each snap.
|
|
132
|
-
<li class="pw-shell__validation-item">
|
|
164
|
+
{#each Object.entries(snap.fieldMessages) as [key, msg]}
|
|
165
|
+
<li class="pw-shell__validation-item">
|
|
166
|
+
{#if key !== '_'}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
167
|
+
</li>
|
|
133
168
|
{/each}
|
|
134
169
|
</ul>
|
|
135
170
|
{/if}
|
|
@@ -140,7 +175,18 @@
|
|
|
140
175
|
{:else}
|
|
141
176
|
<div class="pw-shell__footer">
|
|
142
177
|
<div class="pw-shell__footer-left">
|
|
143
|
-
{#if !
|
|
178
|
+
{#if resolvedFooterLayout === 'form' && !hideCancel}
|
|
179
|
+
<!-- Form mode: Cancel on the left -->
|
|
180
|
+
<button
|
|
181
|
+
type="button"
|
|
182
|
+
class="pw-shell__btn pw-shell__btn--cancel"
|
|
183
|
+
disabled={snap.isNavigating}
|
|
184
|
+
onclick={cancel}
|
|
185
|
+
>
|
|
186
|
+
{cancelLabel}
|
|
187
|
+
</button>
|
|
188
|
+
{:else if resolvedFooterLayout === 'wizard' && !snap.isFirstStep}
|
|
189
|
+
<!-- Wizard mode: Back on the left -->
|
|
144
190
|
<button
|
|
145
191
|
type="button"
|
|
146
192
|
class="pw-shell__btn pw-shell__btn--back"
|
|
@@ -152,7 +198,8 @@
|
|
|
152
198
|
{/if}
|
|
153
199
|
</div>
|
|
154
200
|
<div class="pw-shell__footer-right">
|
|
155
|
-
{#if !hideCancel}
|
|
201
|
+
{#if resolvedFooterLayout === 'wizard' && !hideCancel}
|
|
202
|
+
<!-- Wizard mode: Cancel on the right -->
|
|
156
203
|
<button
|
|
157
204
|
type="button"
|
|
158
205
|
class="pw-shell__btn pw-shell__btn--cancel"
|
|
@@ -162,10 +209,11 @@
|
|
|
162
209
|
{cancelLabel}
|
|
163
210
|
</button>
|
|
164
211
|
{/if}
|
|
212
|
+
<!-- Both modes: Submit on the right -->
|
|
165
213
|
<button
|
|
166
214
|
type="button"
|
|
167
215
|
class="pw-shell__btn pw-shell__btn--next"
|
|
168
|
-
disabled={snap.isNavigating
|
|
216
|
+
disabled={snap.isNavigating}
|
|
169
217
|
onclick={next}
|
|
170
218
|
>
|
|
171
219
|
{snap.isLastStep ? completeLabel : nextLabel}
|