@daltonr/pathwrite-svelte 0.7.0 → 0.9.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 +6 -6
- package/dist/PathShell.svelte +24 -9
- package/dist/PathShell.svelte.d.ts +3 -3
- package/dist/PathShell.svelte.d.ts.map +1 -1
- package/dist/index.css +40 -0
- package/dist/index.svelte.d.ts +41 -6
- package/dist/index.svelte.d.ts.map +1 -1
- package/dist/index.svelte.js +32 -3
- package/package.json +2 -2
- package/src/PathShell.svelte +24 -9
- package/src/index.svelte.ts +45 -8
package/README.md
CHANGED
|
@@ -168,7 +168,7 @@ Create the engine yourself with `restoreOrStart()` and pass it via `engine`. Pat
|
|
|
168
168
|
|
|
169
169
|
```svelte
|
|
170
170
|
<script>
|
|
171
|
-
import { HttpStore, restoreOrStart,
|
|
171
|
+
import { HttpStore, restoreOrStart, persistence } from '@daltonr/pathwrite-store';
|
|
172
172
|
|
|
173
173
|
let engine = $state(null);
|
|
174
174
|
|
|
@@ -179,7 +179,7 @@ Create the engine yourself with `restoreOrStart()` and pass it via `engine`. Pat
|
|
|
179
179
|
path: signupPath,
|
|
180
180
|
initialData: { name: '' },
|
|
181
181
|
observers: [
|
|
182
|
-
|
|
182
|
+
persistence({ store, key: 'user:onboarding', strategy: 'onNext' })
|
|
183
183
|
]
|
|
184
184
|
});
|
|
185
185
|
engine = result.engine;
|
|
@@ -467,13 +467,13 @@ const myPath = {
|
|
|
467
467
|
|
|
468
468
|
## Persistence
|
|
469
469
|
|
|
470
|
-
Use with [@daltonr/pathwrite-store
|
|
470
|
+
Use with [@daltonr/pathwrite-store](../store) for automatic state persistence:
|
|
471
471
|
|
|
472
472
|
```svelte
|
|
473
473
|
<script lang="ts">
|
|
474
474
|
import { onMount } from 'svelte';
|
|
475
475
|
import { PathShell } from '@daltonr/pathwrite-svelte';
|
|
476
|
-
import { HttpStore, restoreOrStart,
|
|
476
|
+
import { HttpStore, restoreOrStart, persistence } from '@daltonr/pathwrite-store';
|
|
477
477
|
import DetailsForm from './DetailsForm.svelte';
|
|
478
478
|
import ReviewPanel from './ReviewPanel.svelte';
|
|
479
479
|
|
|
@@ -490,7 +490,7 @@ Use with [@daltonr/pathwrite-store-http](../store-http) for automatic state pers
|
|
|
490
490
|
path: signupPath,
|
|
491
491
|
initialData: { name: '', email: '' },
|
|
492
492
|
observers: [
|
|
493
|
-
|
|
493
|
+
persistence({ store, key, strategy: 'onNext' })
|
|
494
494
|
]
|
|
495
495
|
});
|
|
496
496
|
engine = result.engine;
|
|
@@ -537,7 +537,7 @@ MIT — © 2026 Devjoy Ltd.
|
|
|
537
537
|
## See Also
|
|
538
538
|
|
|
539
539
|
- [@daltonr/pathwrite-core](../core) - Core engine
|
|
540
|
-
- [@daltonr/pathwrite-store
|
|
540
|
+
- [@daltonr/pathwrite-store](../store) - HTTP persistence
|
|
541
541
|
- [Documentation](../../docs/guides/DEVELOPER_GUIDE.md)
|
|
542
542
|
|
|
543
543
|
|
package/dist/PathShell.svelte
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import { usePath, setPathContext } from './index.svelte.js';
|
|
4
4
|
import type { PathDefinition, PathData, PathEngine, PathSnapshot, ProgressLayout } from './index.svelte.js';
|
|
5
|
-
import type { Snippet } from 'svelte';
|
|
5
|
+
import type { Snippet, Component } from 'svelte';
|
|
6
6
|
|
|
7
7
|
/** Converts a camelCase or lowercase field key to a display label. */
|
|
8
8
|
function formatFieldKey(key: string): string {
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
// Optional override snippets for header and footer
|
|
50
50
|
header?: Snippet<[PathSnapshot<any>]>;
|
|
51
51
|
footer?: Snippet<[PathSnapshot<any>, object]>;
|
|
52
|
-
// All other props treated as step
|
|
53
|
-
[key: string]:
|
|
52
|
+
// All other props treated as step components keyed by step ID
|
|
53
|
+
[key: string]: Component<any> | any;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
let {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
hideCancel = false,
|
|
66
66
|
hideProgress = false,
|
|
67
67
|
footerLayout = 'auto',
|
|
68
|
-
validationDisplay = '
|
|
68
|
+
validationDisplay = 'summary',
|
|
69
69
|
progressLayout = 'merged',
|
|
70
70
|
oncomplete,
|
|
71
71
|
oncancel,
|
|
@@ -185,19 +185,23 @@
|
|
|
185
185
|
{/if}
|
|
186
186
|
{/if}
|
|
187
187
|
|
|
188
|
-
<!-- Body: current step rendered via named snippet
|
|
188
|
+
<!-- Body: current step rendered via named snippet.
|
|
189
|
+
Prefer formId (inner step id of a StepChoice) so consumers can
|
|
190
|
+
register snippets by inner step ids directly. -->
|
|
189
191
|
<div class="pw-shell__body">
|
|
190
|
-
{#if stepSnippets[snap.
|
|
191
|
-
{
|
|
192
|
+
{#if snap.formId && stepSnippets[snap.formId]}
|
|
193
|
+
<svelte:component this={stepSnippets[snap.formId]} />
|
|
194
|
+
{:else if stepSnippets[snap.stepId]}
|
|
195
|
+
<svelte:component this={stepSnippets[snap.stepId]} />
|
|
192
196
|
{:else}
|
|
193
197
|
<p>No content for step "{snap.stepId}"</p>
|
|
194
198
|
{/if}
|
|
195
199
|
</div>
|
|
196
200
|
|
|
197
201
|
<!-- Validation messages — suppressed when validationDisplay="inline" -->
|
|
198
|
-
{#if validationDisplay !== 'inline' && snap.hasAttemptedNext && Object.keys(snap.
|
|
202
|
+
{#if validationDisplay !== 'inline' && snap.hasAttemptedNext && Object.keys(snap.fieldErrors).length > 0}
|
|
199
203
|
<ul class="pw-shell__validation">
|
|
200
|
-
{#each Object.entries(snap.
|
|
204
|
+
{#each Object.entries(snap.fieldErrors) as [key, msg]}
|
|
201
205
|
<li class="pw-shell__validation-item">
|
|
202
206
|
{#if key !== '_'}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
203
207
|
</li>
|
|
@@ -205,6 +209,17 @@
|
|
|
205
209
|
</ul>
|
|
206
210
|
{/if}
|
|
207
211
|
|
|
212
|
+
<!-- Warning messages — non-blocking, shown immediately (no hasAttemptedNext gate) -->
|
|
213
|
+
{#if validationDisplay !== 'inline' && Object.keys(snap.fieldWarnings).length > 0}
|
|
214
|
+
<ul class="pw-shell__warnings">
|
|
215
|
+
{#each Object.entries(snap.fieldWarnings) as [key, msg]}
|
|
216
|
+
<li class="pw-shell__warnings-item">
|
|
217
|
+
{#if key !== '_'}<span class="pw-shell__warnings-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
218
|
+
</li>
|
|
219
|
+
{/each}
|
|
220
|
+
</ul>
|
|
221
|
+
{/if}
|
|
222
|
+
|
|
208
223
|
<!-- Footer: navigation buttons (overridable via footer snippet) -->
|
|
209
224
|
{#if footer}
|
|
210
225
|
{@render footer(snap, actions)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PathDefinition, PathData, PathEngine, PathSnapshot, ProgressLayout } from './index.svelte.js';
|
|
2
|
-
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { Snippet, Component } from 'svelte';
|
|
3
3
|
interface Props {
|
|
4
4
|
path?: PathDefinition<any>;
|
|
5
5
|
engine?: PathEngine;
|
|
@@ -38,9 +38,9 @@ interface Props {
|
|
|
38
38
|
onevent?: (event: any) => void;
|
|
39
39
|
header?: Snippet<[PathSnapshot<any>]>;
|
|
40
40
|
footer?: Snippet<[PathSnapshot<any>, object]>;
|
|
41
|
-
[key: string]:
|
|
41
|
+
[key: string]: Component<any> | any;
|
|
42
42
|
}
|
|
43
|
-
declare const PathShell:
|
|
43
|
+
declare const PathShell: Component<Props, {
|
|
44
44
|
restart: () => Promise<void>;
|
|
45
45
|
}, "">;
|
|
46
46
|
type PathShell = ReturnType<typeof PathShell>;
|
|
@@ -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,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC5G,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,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,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAC5G,OAAO,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAG/C,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;IAC1C;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAC;IAClD;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,cAAc,CAAC;IAEhC,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,SAAS,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC;CACrC;AAyNH,QAAA,MAAM,SAAS;mBAlIQ,QAAQ,IAAI,CAAC;MAkImB,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
package/dist/index.css
CHANGED
|
@@ -246,6 +246,9 @@
|
|
|
246
246
|
--pw-color-error: #dc2626;
|
|
247
247
|
--pw-color-error-bg: #fef2f2;
|
|
248
248
|
--pw-color-error-border: #fecaca;
|
|
249
|
+
--pw-color-warning: #d97706;
|
|
250
|
+
--pw-color-warning-bg: #fffbeb;
|
|
251
|
+
--pw-color-warning-border: #fde68a;
|
|
249
252
|
}
|
|
250
253
|
|
|
251
254
|
.pw-shell__validation {
|
|
@@ -282,6 +285,43 @@
|
|
|
282
285
|
content: ":";
|
|
283
286
|
}
|
|
284
287
|
|
|
288
|
+
/* ------------------------------------------------------------------ */
|
|
289
|
+
/* Warning messages */
|
|
290
|
+
/* ------------------------------------------------------------------ */
|
|
291
|
+
.pw-shell__warnings {
|
|
292
|
+
list-style: none;
|
|
293
|
+
margin: 0;
|
|
294
|
+
padding: 12px 16px;
|
|
295
|
+
background: var(--pw-color-warning-bg);
|
|
296
|
+
border: 1px solid var(--pw-color-warning-border);
|
|
297
|
+
border-radius: var(--pw-shell-radius);
|
|
298
|
+
display: flex;
|
|
299
|
+
flex-direction: column;
|
|
300
|
+
gap: 4px;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
.pw-shell__warnings-item {
|
|
304
|
+
font-size: 13px;
|
|
305
|
+
color: var(--pw-color-warning);
|
|
306
|
+
padding-left: 16px;
|
|
307
|
+
position: relative;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.pw-shell__warnings-item::before {
|
|
311
|
+
content: "•";
|
|
312
|
+
position: absolute;
|
|
313
|
+
left: 4px;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
.pw-shell__warnings-label {
|
|
317
|
+
font-weight: 600;
|
|
318
|
+
margin-right: 3px;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.pw-shell__warnings-label::after {
|
|
322
|
+
content: ":";
|
|
323
|
+
}
|
|
324
|
+
|
|
285
325
|
/* ------------------------------------------------------------------ */
|
|
286
326
|
/* Footer — navigation buttons */
|
|
287
327
|
/* ------------------------------------------------------------------ */
|
package/dist/index.svelte.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type { PathData, FieldErrors, PathDefinition, PathEngine, PathEvent, Path
|
|
|
3
3
|
export interface UsePathOptions {
|
|
4
4
|
/**
|
|
5
5
|
* An externally-managed `PathEngine` to subscribe to — for example, the engine
|
|
6
|
-
* returned by `restoreOrStart()` from `@daltonr/pathwrite-store
|
|
6
|
+
* returned by `restoreOrStart()` from `@daltonr/pathwrite-store`.
|
|
7
7
|
*
|
|
8
8
|
* When provided:
|
|
9
9
|
* - `usePath` will **not** create its own engine.
|
|
@@ -16,7 +16,12 @@ export interface UsePathOptions {
|
|
|
16
16
|
onEvent?: (event: PathEvent) => void;
|
|
17
17
|
}
|
|
18
18
|
export interface UsePathReturn<TData extends PathData = PathData> {
|
|
19
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Current path snapshot, or `null` when no path is active. Reactive via `$state`.
|
|
21
|
+
*
|
|
22
|
+
* ⚠️ **Do not destructure.** `const { snapshot } = usePath()` captures the value
|
|
23
|
+
* once and loses reactivity. Always access as `path.snapshot`.
|
|
24
|
+
*/
|
|
20
25
|
readonly snapshot: PathSnapshot<TData> | null;
|
|
21
26
|
/** Start (or restart) a path. */
|
|
22
27
|
start: (path: PathDefinition<any>, initialData?: PathData) => Promise<void>;
|
|
@@ -34,20 +39,49 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
34
39
|
goToStepChecked: (stepId: string) => Promise<void>;
|
|
35
40
|
/** Update a single data value; triggers a re-render via stateChanged. When `TData` is specified, `key` and `value` are type-checked against your data shape. */
|
|
36
41
|
setData: <K extends string & keyof TData>(key: K, value: TData[K]) => Promise<void>;
|
|
42
|
+
/** Reset the current step's data to what it was when the step was entered. Useful for "Clear" or "Reset" buttons. */
|
|
43
|
+
resetStep: () => Promise<void>;
|
|
37
44
|
/**
|
|
38
45
|
* Tear down any active path (without firing hooks) and immediately start the
|
|
39
46
|
* given path fresh. Safe to call whether or not a path is currently active.
|
|
40
47
|
* Use for "Start over" / retry flows without remounting the component.
|
|
41
48
|
*/
|
|
42
|
-
restart: (
|
|
49
|
+
restart: () => Promise<void>;
|
|
43
50
|
}
|
|
44
51
|
/**
|
|
45
52
|
* Create a Pathwrite engine with Svelte 5 runes-based reactivity.
|
|
46
53
|
* Call this from inside a Svelte component to get a reactive snapshot.
|
|
47
54
|
* Cleanup is automatic via onDestroy.
|
|
48
55
|
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
56
|
+
* ---
|
|
57
|
+
*
|
|
58
|
+
* ⚠️ **Do not destructure `snapshot`.**
|
|
59
|
+
*
|
|
60
|
+
* `snapshot` is a reactive getter. Destructuring it copies the value once
|
|
61
|
+
* and severs the reactive connection — your component will stop updating.
|
|
62
|
+
*
|
|
63
|
+
* ```svelte
|
|
64
|
+
* // ❌ Broken — snapshot is captured once and never updates
|
|
65
|
+
* const { snapshot, next } = usePath();
|
|
66
|
+
*
|
|
67
|
+
* // ✅ Correct — snapshot is read through the live object on every render
|
|
68
|
+
* const path = usePath();
|
|
69
|
+
* // use path.snapshot in your template
|
|
70
|
+
* ```
|
|
71
|
+
*
|
|
72
|
+
* Other properties (`next`, `previous`, `setData`, etc.) are plain functions
|
|
73
|
+
* and are safe to destructure.
|
|
74
|
+
*
|
|
75
|
+
* If you need a local variable that stays reactive, use `$derived`:
|
|
76
|
+
* ```svelte
|
|
77
|
+
* const path = usePath();
|
|
78
|
+
* const snapshot = $derived(path.snapshot);
|
|
79
|
+
* ```
|
|
80
|
+
*
|
|
81
|
+
* This is expected Svelte 5 behaviour — see the
|
|
82
|
+
* [Svelte $state docs](https://svelte.dev/docs/svelte/$state) for details.
|
|
83
|
+
*
|
|
84
|
+
* ---
|
|
51
85
|
*
|
|
52
86
|
* @example
|
|
53
87
|
* ```svelte
|
|
@@ -70,13 +104,14 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
70
104
|
*/
|
|
71
105
|
export declare function usePath<TData extends PathData = PathData>(options?: UsePathOptions): UsePathReturn<TData>;
|
|
72
106
|
export interface PathContext<TData extends PathData = PathData> {
|
|
73
|
-
readonly snapshot: PathSnapshot<TData
|
|
107
|
+
readonly snapshot: PathSnapshot<TData>;
|
|
74
108
|
next: () => Promise<void>;
|
|
75
109
|
previous: () => Promise<void>;
|
|
76
110
|
cancel: () => Promise<void>;
|
|
77
111
|
goToStep: (stepId: string) => Promise<void>;
|
|
78
112
|
goToStepChecked: (stepId: string) => Promise<void>;
|
|
79
113
|
setData: <K extends string & keyof TData>(key: K, value: TData[K]) => Promise<void>;
|
|
114
|
+
resetStep: () => Promise<void>;
|
|
80
115
|
restart: () => Promise<void>;
|
|
81
116
|
}
|
|
82
117
|
/**
|
|
@@ -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,WAAW,EACX,cAAc,EACd,UAAU,EACV,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,eAAe,EACf,cAAc,EACd,YAAY,EACZ,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
|
|
1
|
+
{"version":3,"file":"index.svelte.d.ts","sourceRoot":"","sources":["../src/index.svelte.ts"],"names":[],"mappings":"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"}
|
package/dist/index.svelte.js
CHANGED
|
@@ -8,8 +8,35 @@ import { PathEngine as PathEngineClass } from "@daltonr/pathwrite-core";
|
|
|
8
8
|
* Call this from inside a Svelte component to get a reactive snapshot.
|
|
9
9
|
* Cleanup is automatic via onDestroy.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* ---
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ **Do not destructure `snapshot`.**
|
|
14
|
+
*
|
|
15
|
+
* `snapshot` is a reactive getter. Destructuring it copies the value once
|
|
16
|
+
* and severs the reactive connection — your component will stop updating.
|
|
17
|
+
*
|
|
18
|
+
* ```svelte
|
|
19
|
+
* // ❌ Broken — snapshot is captured once and never updates
|
|
20
|
+
* const { snapshot, next } = usePath();
|
|
21
|
+
*
|
|
22
|
+
* // ✅ Correct — snapshot is read through the live object on every render
|
|
23
|
+
* const path = usePath();
|
|
24
|
+
* // use path.snapshot in your template
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* Other properties (`next`, `previous`, `setData`, etc.) are plain functions
|
|
28
|
+
* and are safe to destructure.
|
|
29
|
+
*
|
|
30
|
+
* If you need a local variable that stays reactive, use `$derived`:
|
|
31
|
+
* ```svelte
|
|
32
|
+
* const path = usePath();
|
|
33
|
+
* const snapshot = $derived(path.snapshot);
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* This is expected Svelte 5 behaviour — see the
|
|
37
|
+
* [Svelte $state docs](https://svelte.dev/docs/svelte/$state) for details.
|
|
38
|
+
*
|
|
39
|
+
* ---
|
|
13
40
|
*
|
|
14
41
|
* @example
|
|
15
42
|
* ```svelte
|
|
@@ -54,7 +81,8 @@ export function usePath(options) {
|
|
|
54
81
|
const goToStep = (stepId) => engine.goToStep(stepId);
|
|
55
82
|
const goToStepChecked = (stepId) => engine.goToStepChecked(stepId);
|
|
56
83
|
const setData = ((key, value) => engine.setData(key, value));
|
|
57
|
-
const
|
|
84
|
+
const resetStep = () => engine.resetStep();
|
|
85
|
+
const restart = () => engine.restart();
|
|
58
86
|
return {
|
|
59
87
|
get snapshot() { return _snapshot; },
|
|
60
88
|
start,
|
|
@@ -65,6 +93,7 @@ export function usePath(options) {
|
|
|
65
93
|
goToStep,
|
|
66
94
|
goToStepChecked,
|
|
67
95
|
setData,
|
|
96
|
+
resetStep,
|
|
68
97
|
restart
|
|
69
98
|
};
|
|
70
99
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@daltonr/pathwrite-svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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.",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"svelte": ">=5.0.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@daltonr/pathwrite-core": "^0.
|
|
55
|
+
"@daltonr/pathwrite-core": "^0.9.0"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
58
|
"@sveltejs/package": "^2.5.7",
|
package/src/PathShell.svelte
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { onMount } from 'svelte';
|
|
3
3
|
import { usePath, setPathContext } from './index.svelte.js';
|
|
4
4
|
import type { PathDefinition, PathData, PathEngine, PathSnapshot, ProgressLayout } from './index.svelte.js';
|
|
5
|
-
import type { Snippet } from 'svelte';
|
|
5
|
+
import type { Snippet, Component } from 'svelte';
|
|
6
6
|
|
|
7
7
|
/** Converts a camelCase or lowercase field key to a display label. */
|
|
8
8
|
function formatFieldKey(key: string): string {
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
// Optional override snippets for header and footer
|
|
50
50
|
header?: Snippet<[PathSnapshot<any>]>;
|
|
51
51
|
footer?: Snippet<[PathSnapshot<any>, object]>;
|
|
52
|
-
// All other props treated as step
|
|
53
|
-
[key: string]:
|
|
52
|
+
// All other props treated as step components keyed by step ID
|
|
53
|
+
[key: string]: Component<any> | any;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
let {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
hideCancel = false,
|
|
66
66
|
hideProgress = false,
|
|
67
67
|
footerLayout = 'auto',
|
|
68
|
-
validationDisplay = '
|
|
68
|
+
validationDisplay = 'summary',
|
|
69
69
|
progressLayout = 'merged',
|
|
70
70
|
oncomplete,
|
|
71
71
|
oncancel,
|
|
@@ -185,19 +185,23 @@
|
|
|
185
185
|
{/if}
|
|
186
186
|
{/if}
|
|
187
187
|
|
|
188
|
-
<!-- Body: current step rendered via named snippet
|
|
188
|
+
<!-- Body: current step rendered via named snippet.
|
|
189
|
+
Prefer formId (inner step id of a StepChoice) so consumers can
|
|
190
|
+
register snippets by inner step ids directly. -->
|
|
189
191
|
<div class="pw-shell__body">
|
|
190
|
-
{#if stepSnippets[snap.
|
|
191
|
-
{
|
|
192
|
+
{#if snap.formId && stepSnippets[snap.formId]}
|
|
193
|
+
<svelte:component this={stepSnippets[snap.formId]} />
|
|
194
|
+
{:else if stepSnippets[snap.stepId]}
|
|
195
|
+
<svelte:component this={stepSnippets[snap.stepId]} />
|
|
192
196
|
{:else}
|
|
193
197
|
<p>No content for step "{snap.stepId}"</p>
|
|
194
198
|
{/if}
|
|
195
199
|
</div>
|
|
196
200
|
|
|
197
201
|
<!-- Validation messages — suppressed when validationDisplay="inline" -->
|
|
198
|
-
{#if validationDisplay !== 'inline' && snap.hasAttemptedNext && Object.keys(snap.
|
|
202
|
+
{#if validationDisplay !== 'inline' && snap.hasAttemptedNext && Object.keys(snap.fieldErrors).length > 0}
|
|
199
203
|
<ul class="pw-shell__validation">
|
|
200
|
-
{#each Object.entries(snap.
|
|
204
|
+
{#each Object.entries(snap.fieldErrors) as [key, msg]}
|
|
201
205
|
<li class="pw-shell__validation-item">
|
|
202
206
|
{#if key !== '_'}<span class="pw-shell__validation-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
203
207
|
</li>
|
|
@@ -205,6 +209,17 @@
|
|
|
205
209
|
</ul>
|
|
206
210
|
{/if}
|
|
207
211
|
|
|
212
|
+
<!-- Warning messages — non-blocking, shown immediately (no hasAttemptedNext gate) -->
|
|
213
|
+
{#if validationDisplay !== 'inline' && Object.keys(snap.fieldWarnings).length > 0}
|
|
214
|
+
<ul class="pw-shell__warnings">
|
|
215
|
+
{#each Object.entries(snap.fieldWarnings) as [key, msg]}
|
|
216
|
+
<li class="pw-shell__warnings-item">
|
|
217
|
+
{#if key !== '_'}<span class="pw-shell__warnings-label">{formatFieldKey(key)}</span>{/if}{msg}
|
|
218
|
+
</li>
|
|
219
|
+
{/each}
|
|
220
|
+
</ul>
|
|
221
|
+
{/if}
|
|
222
|
+
|
|
208
223
|
<!-- Footer: navigation buttons (overridable via footer snippet) -->
|
|
209
224
|
{#if footer}
|
|
210
225
|
{@render footer(snap, actions)}
|
package/src/index.svelte.ts
CHANGED
|
@@ -30,7 +30,7 @@ export type {
|
|
|
30
30
|
export interface UsePathOptions {
|
|
31
31
|
/**
|
|
32
32
|
* An externally-managed `PathEngine` to subscribe to — for example, the engine
|
|
33
|
-
* returned by `restoreOrStart()` from `@daltonr/pathwrite-store
|
|
33
|
+
* returned by `restoreOrStart()` from `@daltonr/pathwrite-store`.
|
|
34
34
|
*
|
|
35
35
|
* When provided:
|
|
36
36
|
* - `usePath` will **not** create its own engine.
|
|
@@ -44,7 +44,12 @@ export interface UsePathOptions {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export interface UsePathReturn<TData extends PathData = PathData> {
|
|
47
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* Current path snapshot, or `null` when no path is active. Reactive via `$state`.
|
|
49
|
+
*
|
|
50
|
+
* ⚠️ **Do not destructure.** `const { snapshot } = usePath()` captures the value
|
|
51
|
+
* once and loses reactivity. Always access as `path.snapshot`.
|
|
52
|
+
*/
|
|
48
53
|
readonly snapshot: PathSnapshot<TData> | null;
|
|
49
54
|
/** Start (or restart) a path. */
|
|
50
55
|
start: (path: PathDefinition<any>, initialData?: PathData) => Promise<void>;
|
|
@@ -62,12 +67,14 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
62
67
|
goToStepChecked: (stepId: string) => Promise<void>;
|
|
63
68
|
/** Update a single data value; triggers a re-render via stateChanged. When `TData` is specified, `key` and `value` are type-checked against your data shape. */
|
|
64
69
|
setData: <K extends string & keyof TData>(key: K, value: TData[K]) => Promise<void>;
|
|
70
|
+
/** Reset the current step's data to what it was when the step was entered. Useful for "Clear" or "Reset" buttons. */
|
|
71
|
+
resetStep: () => Promise<void>;
|
|
65
72
|
/**
|
|
66
73
|
* Tear down any active path (without firing hooks) and immediately start the
|
|
67
74
|
* given path fresh. Safe to call whether or not a path is currently active.
|
|
68
75
|
* Use for "Start over" / retry flows without remounting the component.
|
|
69
76
|
*/
|
|
70
|
-
restart: (
|
|
77
|
+
restart: () => Promise<void>;
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
// ---------------------------------------------------------------------------
|
|
@@ -79,8 +86,35 @@ export interface UsePathReturn<TData extends PathData = PathData> {
|
|
|
79
86
|
* Call this from inside a Svelte component to get a reactive snapshot.
|
|
80
87
|
* Cleanup is automatic via onDestroy.
|
|
81
88
|
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
89
|
+
* ---
|
|
90
|
+
*
|
|
91
|
+
* ⚠️ **Do not destructure `snapshot`.**
|
|
92
|
+
*
|
|
93
|
+
* `snapshot` is a reactive getter. Destructuring it copies the value once
|
|
94
|
+
* and severs the reactive connection — your component will stop updating.
|
|
95
|
+
*
|
|
96
|
+
* ```svelte
|
|
97
|
+
* // ❌ Broken — snapshot is captured once and never updates
|
|
98
|
+
* const { snapshot, next } = usePath();
|
|
99
|
+
*
|
|
100
|
+
* // ✅ Correct — snapshot is read through the live object on every render
|
|
101
|
+
* const path = usePath();
|
|
102
|
+
* // use path.snapshot in your template
|
|
103
|
+
* ```
|
|
104
|
+
*
|
|
105
|
+
* Other properties (`next`, `previous`, `setData`, etc.) are plain functions
|
|
106
|
+
* and are safe to destructure.
|
|
107
|
+
*
|
|
108
|
+
* If you need a local variable that stays reactive, use `$derived`:
|
|
109
|
+
* ```svelte
|
|
110
|
+
* const path = usePath();
|
|
111
|
+
* const snapshot = $derived(path.snapshot);
|
|
112
|
+
* ```
|
|
113
|
+
*
|
|
114
|
+
* This is expected Svelte 5 behaviour — see the
|
|
115
|
+
* [Svelte $state docs](https://svelte.dev/docs/svelte/$state) for details.
|
|
116
|
+
*
|
|
117
|
+
* ---
|
|
84
118
|
*
|
|
85
119
|
* @example
|
|
86
120
|
* ```svelte
|
|
@@ -143,8 +177,9 @@ export function usePath<TData extends PathData = PathData>(
|
|
|
143
177
|
const setData = (<K extends string & keyof TData>(key: K, value: TData[K]): Promise<void> =>
|
|
144
178
|
engine.setData(key, value as unknown)) as UsePathReturn<TData>["setData"];
|
|
145
179
|
|
|
146
|
-
const
|
|
147
|
-
|
|
180
|
+
const resetStep = (): Promise<void> => engine.resetStep();
|
|
181
|
+
|
|
182
|
+
const restart = (): Promise<void> => engine.restart();
|
|
148
183
|
|
|
149
184
|
return {
|
|
150
185
|
get snapshot() { return _snapshot; },
|
|
@@ -156,6 +191,7 @@ export function usePath<TData extends PathData = PathData>(
|
|
|
156
191
|
goToStep,
|
|
157
192
|
goToStepChecked,
|
|
158
193
|
setData,
|
|
194
|
+
resetStep,
|
|
159
195
|
restart
|
|
160
196
|
};
|
|
161
197
|
}
|
|
@@ -167,13 +203,14 @@ export function usePath<TData extends PathData = PathData>(
|
|
|
167
203
|
const PATH_CONTEXT_KEY = Symbol("pathwrite-context");
|
|
168
204
|
|
|
169
205
|
export interface PathContext<TData extends PathData = PathData> {
|
|
170
|
-
readonly snapshot: PathSnapshot<TData
|
|
206
|
+
readonly snapshot: PathSnapshot<TData>;
|
|
171
207
|
next: () => Promise<void>;
|
|
172
208
|
previous: () => Promise<void>;
|
|
173
209
|
cancel: () => Promise<void>;
|
|
174
210
|
goToStep: (stepId: string) => Promise<void>;
|
|
175
211
|
goToStepChecked: (stepId: string) => Promise<void>;
|
|
176
212
|
setData: <K extends string & keyof TData>(key: K, value: TData[K]) => Promise<void>;
|
|
213
|
+
resetStep: () => Promise<void>;
|
|
177
214
|
restart: () => Promise<void>;
|
|
178
215
|
}
|
|
179
216
|
|