@daltonr/pathwrite-angular 0.4.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/dist/shell.d.ts CHANGED
@@ -108,13 +108,20 @@ export declare class PathShellComponent implements OnInit, OnDestroy {
108
108
  /** Label for the Next navigation button. */
109
109
  nextLabel: string;
110
110
  /** Label for the Next button when on the last step. */
111
- finishLabel: string;
111
+ completeLabel: string;
112
112
  /** Label for the Cancel button. */
113
113
  cancelLabel: string;
114
114
  /** Hide the Cancel button entirely. */
115
115
  hideCancel: boolean;
116
- /** Hide the step progress indicator in the header. */
116
+ /** Hide the step progress indicator in the header. Also hidden automatically when the path has only one step. */
117
117
  hideProgress: boolean;
118
+ /**
119
+ * Footer layout mode:
120
+ * - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
121
+ * - "wizard": Back button on left, Cancel and Submit together on right.
122
+ * - "form": Cancel on left, Submit alone on right. Back button never shown.
123
+ */
124
+ footerLayout: "wizard" | "form" | "auto";
118
125
  completed: EventEmitter<PathData>;
119
126
  cancelled: EventEmitter<PathData>;
120
127
  pathEvent: EventEmitter<PathEvent>;
@@ -132,6 +139,23 @@ export declare class PathShellComponent implements OnInit, OnDestroy {
132
139
  ngOnInit(): void;
133
140
  ngOnDestroy(): void;
134
141
  doStart(): void;
142
+ /**
143
+ * Restart the active path from step 1 with the original `initialData`,
144
+ * without unmounting the shell. Call this via a `#shell` template reference:
145
+ *
146
+ * ```html
147
+ * <pw-shell #shell [path]="myPath" ...></pw-shell>
148
+ * <button (click)="shell.restart()">Try Again</button>
149
+ * ```
150
+ */
151
+ restart(): Promise<void>;
152
+ /** Returns Object.entries(s.fieldMessages) for use in *ngFor. */
153
+ protected fieldEntries(s: PathSnapshot): [string, string][];
154
+ /** Resolves "auto" footerLayout based on snapshot. Single-step top-level → "form", otherwise → "wizard". */
155
+ protected getResolvedFooterLayout(s: PathSnapshot): "wizard" | "form";
156
+ /** Converts a camelCase or lowercase field key to a display label.
157
+ * e.g. "firstName" → "First Name", "email" → "Email" */
158
+ protected formatFieldKey(key: string): string;
135
159
  static ɵfac: i0.ɵɵFactoryDeclaration<PathShellComponent, never>;
136
- static ɵcmp: i0.ɵɵComponentDeclaration<PathShellComponent, "pw-shell", never, { "path": { "alias": "path"; "required": true; }; "initialData": { "alias": "initialData"; "required": false; }; "autoStart": { "alias": "autoStart"; "required": false; }; "backLabel": { "alias": "backLabel"; "required": false; }; "nextLabel": { "alias": "nextLabel"; "required": false; }; "finishLabel": { "alias": "finishLabel"; "required": false; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; }; "hideCancel": { "alias": "hideCancel"; "required": false; }; "hideProgress": { "alias": "hideProgress"; "required": false; }; }, { "completed": "completed"; "cancelled": "cancelled"; "pathEvent": "pathEvent"; }, ["customHeader", "customFooter", "stepDirectives"], never, true, never>;
160
+ static ɵcmp: i0.ɵɵComponentDeclaration<PathShellComponent, "pw-shell", never, { "path": { "alias": "path"; "required": true; }; "initialData": { "alias": "initialData"; "required": false; }; "autoStart": { "alias": "autoStart"; "required": false; }; "backLabel": { "alias": "backLabel"; "required": false; }; "nextLabel": { "alias": "nextLabel"; "required": false; }; "completeLabel": { "alias": "completeLabel"; "required": false; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; }; "hideCancel": { "alias": "hideCancel"; "required": false; }; "hideProgress": { "alias": "hideProgress"; "required": false; }; "footerLayout": { "alias": "footerLayout"; "required": false; }; }, { "completed": "completed"; "cancelled": "cancelled"; "pathEvent": "pathEvent"; }, ["customHeader", "customFooter", "stepDirectives"], never, true, never>;
137
161
  }
package/dist/shell.js CHANGED
@@ -110,17 +110,24 @@ export class PathShellComponent {
110
110
  /** Start the path automatically on ngOnInit. Set to false to call doStart() manually. */
111
111
  this.autoStart = true;
112
112
  /** Label for the Back navigation button. */
113
- this.backLabel = "Back";
113
+ this.backLabel = "Previous";
114
114
  /** Label for the Next navigation button. */
115
115
  this.nextLabel = "Next";
116
116
  /** Label for the Next button when on the last step. */
117
- this.finishLabel = "Finish";
117
+ this.completeLabel = "Complete";
118
118
  /** Label for the Cancel button. */
119
119
  this.cancelLabel = "Cancel";
120
120
  /** Hide the Cancel button entirely. */
121
121
  this.hideCancel = false;
122
- /** Hide the step progress indicator in the header. */
122
+ /** Hide the step progress indicator in the header. Also hidden automatically when the path has only one step. */
123
123
  this.hideProgress = false;
124
+ /**
125
+ * Footer layout mode:
126
+ * - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
127
+ * - "wizard": Back button on left, Cancel and Submit together on right.
128
+ * - "form": Cancel on left, Submit alone on right. Back button never shown.
129
+ */
130
+ this.footerLayout = "auto";
124
131
  this.completed = new EventEmitter();
125
132
  this.cancelled = new EventEmitter();
126
133
  this.pathEvent = new EventEmitter();
@@ -161,8 +168,35 @@ export class PathShellComponent {
161
168
  this.started = true;
162
169
  this.facade.start(this.path, this.initialData);
163
170
  }
171
+ /**
172
+ * Restart the active path from step 1 with the original `initialData`,
173
+ * without unmounting the shell. Call this via a `#shell` template reference:
174
+ *
175
+ * ```html
176
+ * <pw-shell #shell [path]="myPath" ...></pw-shell>
177
+ * <button (click)="shell.restart()">Try Again</button>
178
+ * ```
179
+ */
180
+ restart() {
181
+ return this.facade.restart(this.path, this.initialData);
182
+ }
183
+ /** Returns Object.entries(s.fieldMessages) for use in *ngFor. */
184
+ fieldEntries(s) {
185
+ return Object.entries(s.fieldMessages);
186
+ }
187
+ /** Resolves "auto" footerLayout based on snapshot. Single-step top-level → "form", otherwise → "wizard". */
188
+ getResolvedFooterLayout(s) {
189
+ return this.footerLayout === "auto"
190
+ ? (s.stepCount === 1 && s.nestingLevel === 0 ? "form" : "wizard")
191
+ : this.footerLayout;
192
+ }
193
+ /** Converts a camelCase or lowercase field key to a display label.
194
+ * e.g. "firstName" → "First Name", "email" → "Email" */
195
+ formatFieldKey(key) {
196
+ return key.replace(/([A-Z])/g, " $1").replace(/^./, c => c.toUpperCase()).trim();
197
+ }
164
198
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PathShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
165
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PathShellComponent, isStandalone: true, selector: "pw-shell", inputs: { path: "path", initialData: "initialData", autoStart: "autoStart", backLabel: "backLabel", nextLabel: "nextLabel", finishLabel: "finishLabel", cancelLabel: "cancelLabel", hideCancel: "hideCancel", hideProgress: "hideProgress" }, outputs: { completed: "completed", cancelled: "cancelled", pathEvent: "pathEvent" }, providers: [PathFacade], queries: [{ propertyName: "customHeader", first: true, predicate: PathShellHeaderDirective, descendants: true }, { propertyName: "customFooter", first: true, predicate: PathShellFooterDirective, descendants: true }, { propertyName: "stepDirectives", predicate: PathStepDirective }], ngImport: i0, template: `
199
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PathShellComponent, isStandalone: true, selector: "pw-shell", inputs: { path: "path", initialData: "initialData", autoStart: "autoStart", backLabel: "backLabel", nextLabel: "nextLabel", completeLabel: "completeLabel", cancelLabel: "cancelLabel", hideCancel: "hideCancel", hideProgress: "hideProgress", footerLayout: "footerLayout" }, outputs: { completed: "completed", cancelled: "cancelled", pathEvent: "pathEvent" }, providers: [PathFacade], queries: [{ propertyName: "customHeader", first: true, predicate: PathShellHeaderDirective, descendants: true }, { propertyName: "customFooter", first: true, predicate: PathShellFooterDirective, descendants: true }, { propertyName: "stepDirectives", predicate: PathStepDirective }], ngImport: i0, template: `
166
200
  <!-- Empty state -->
167
201
  <div class="pw-shell" *ngIf="!(facade.state$ | async)">
168
202
  <div class="pw-shell__empty" *ngIf="!started">
@@ -178,7 +212,7 @@ export class PathShellComponent {
178
212
  <ng-container *ngTemplateOutlet="customHeader.templateRef; context: { $implicit: s }"></ng-container>
179
213
  </ng-container>
180
214
  <ng-template #defaultHeader>
181
- <div class="pw-shell__header" *ngIf="!hideProgress">
215
+ <div class="pw-shell__header" *ngIf="!hideProgress && (s.stepCount > 1 || s.nestingLevel > 0)">
182
216
  <div class="pw-shell__steps">
183
217
  <div
184
218
  *ngFor="let step of s.steps; let i = index"
@@ -204,9 +238,11 @@ export class PathShellComponent {
204
238
  </ng-container>
205
239
  </div>
206
240
 
207
- <!-- Validation messages -->
208
- <ul class="pw-shell__validation" *ngIf="s.validationMessages.length > 0">
209
- <li *ngFor="let msg of s.validationMessages" class="pw-shell__validation-item">{{ msg }}</li>
241
+ <!-- Validation messages — labeled by field name -->
242
+ <ul class="pw-shell__validation" *ngIf="s.hasAttemptedNext && fieldEntries(s).length > 0">
243
+ <li *ngFor="let entry of fieldEntries(s)" class="pw-shell__validation-item">
244
+ <span *ngIf="entry[0] !== '_'" class="pw-shell__validation-label">{{ formatFieldKey(entry[0]) }}</span>{{ entry[1] }}
245
+ </li>
210
246
  </ul>
211
247
 
212
248
  <!-- Footer — custom or default navigation buttons -->
@@ -216,8 +252,17 @@ export class PathShellComponent {
216
252
  <ng-template #defaultFooter>
217
253
  <div class="pw-shell__footer">
218
254
  <div class="pw-shell__footer-left">
255
+ <!-- Form mode: Cancel on the left -->
256
+ <button
257
+ *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
258
+ type="button"
259
+ class="pw-shell__btn pw-shell__btn--cancel"
260
+ [disabled]="s.isNavigating"
261
+ (click)="facade.cancel()"
262
+ >{{ cancelLabel }}</button>
263
+ <!-- Wizard mode: Back on the left -->
219
264
  <button
220
- *ngIf="!s.isFirstStep"
265
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
221
266
  type="button"
222
267
  class="pw-shell__btn pw-shell__btn--back"
223
268
  [disabled]="s.isNavigating || !s.canMovePrevious"
@@ -225,19 +270,21 @@ export class PathShellComponent {
225
270
  >{{ backLabel }}</button>
226
271
  </div>
227
272
  <div class="pw-shell__footer-right">
273
+ <!-- Wizard mode: Cancel on the right -->
228
274
  <button
229
- *ngIf="!hideCancel"
275
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
230
276
  type="button"
231
277
  class="pw-shell__btn pw-shell__btn--cancel"
232
278
  [disabled]="s.isNavigating"
233
279
  (click)="facade.cancel()"
234
280
  >{{ cancelLabel }}</button>
281
+ <!-- Both modes: Submit on the right -->
235
282
  <button
236
283
  type="button"
237
284
  class="pw-shell__btn pw-shell__btn--next"
238
- [disabled]="s.isNavigating || !s.canMoveNext"
285
+ [disabled]="s.isNavigating"
239
286
  (click)="facade.next()"
240
- >{{ s.isLastStep ? finishLabel : nextLabel }}</button>
287
+ >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
241
288
  </div>
242
289
  </div>
243
290
  </ng-template>
@@ -268,7 +315,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
268
315
  <ng-container *ngTemplateOutlet="customHeader.templateRef; context: { $implicit: s }"></ng-container>
269
316
  </ng-container>
270
317
  <ng-template #defaultHeader>
271
- <div class="pw-shell__header" *ngIf="!hideProgress">
318
+ <div class="pw-shell__header" *ngIf="!hideProgress && (s.stepCount > 1 || s.nestingLevel > 0)">
272
319
  <div class="pw-shell__steps">
273
320
  <div
274
321
  *ngFor="let step of s.steps; let i = index"
@@ -294,9 +341,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
294
341
  </ng-container>
295
342
  </div>
296
343
 
297
- <!-- Validation messages -->
298
- <ul class="pw-shell__validation" *ngIf="s.validationMessages.length > 0">
299
- <li *ngFor="let msg of s.validationMessages" class="pw-shell__validation-item">{{ msg }}</li>
344
+ <!-- Validation messages — labeled by field name -->
345
+ <ul class="pw-shell__validation" *ngIf="s.hasAttemptedNext && fieldEntries(s).length > 0">
346
+ <li *ngFor="let entry of fieldEntries(s)" class="pw-shell__validation-item">
347
+ <span *ngIf="entry[0] !== '_'" class="pw-shell__validation-label">{{ formatFieldKey(entry[0]) }}</span>{{ entry[1] }}
348
+ </li>
300
349
  </ul>
301
350
 
302
351
  <!-- Footer — custom or default navigation buttons -->
@@ -306,8 +355,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
306
355
  <ng-template #defaultFooter>
307
356
  <div class="pw-shell__footer">
308
357
  <div class="pw-shell__footer-left">
358
+ <!-- Form mode: Cancel on the left -->
309
359
  <button
310
- *ngIf="!s.isFirstStep"
360
+ *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
361
+ type="button"
362
+ class="pw-shell__btn pw-shell__btn--cancel"
363
+ [disabled]="s.isNavigating"
364
+ (click)="facade.cancel()"
365
+ >{{ cancelLabel }}</button>
366
+ <!-- Wizard mode: Back on the left -->
367
+ <button
368
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
311
369
  type="button"
312
370
  class="pw-shell__btn pw-shell__btn--back"
313
371
  [disabled]="s.isNavigating || !s.canMovePrevious"
@@ -315,19 +373,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
315
373
  >{{ backLabel }}</button>
316
374
  </div>
317
375
  <div class="pw-shell__footer-right">
376
+ <!-- Wizard mode: Cancel on the right -->
318
377
  <button
319
- *ngIf="!hideCancel"
378
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
320
379
  type="button"
321
380
  class="pw-shell__btn pw-shell__btn--cancel"
322
381
  [disabled]="s.isNavigating"
323
382
  (click)="facade.cancel()"
324
383
  >{{ cancelLabel }}</button>
384
+ <!-- Both modes: Submit on the right -->
325
385
  <button
326
386
  type="button"
327
387
  class="pw-shell__btn pw-shell__btn--next"
328
- [disabled]="s.isNavigating || !s.canMoveNext"
388
+ [disabled]="s.isNavigating"
329
389
  (click)="facade.next()"
330
- >{{ s.isLastStep ? finishLabel : nextLabel }}</button>
390
+ >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
331
391
  </div>
332
392
  </div>
333
393
  </ng-template>
@@ -345,7 +405,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
345
405
  type: Input
346
406
  }], nextLabel: [{
347
407
  type: Input
348
- }], finishLabel: [{
408
+ }], completeLabel: [{
349
409
  type: Input
350
410
  }], cancelLabel: [{
351
411
  type: Input
@@ -353,6 +413,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
353
413
  type: Input
354
414
  }], hideProgress: [{
355
415
  type: Input
416
+ }], footerLayout: [{
417
+ type: Input
356
418
  }], completed: [{
357
419
  type: Output
358
420
  }], cancelled: [{
package/dist/shell.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"shell.js","sourceRoot":"","sources":["../src/shell.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daltonr/pathwrite-angular",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Angular adapter for @daltonr/pathwrite-core — RxJS observables, signal-friendly, with optional <pw-shell> default UI.",
@@ -29,7 +29,8 @@
29
29
  "types": "./dist/shell.d.ts",
30
30
  "import": "./dist/shell.js"
31
31
  },
32
- "./styles.css": "./dist/index.css"
32
+ "./styles.css": "./dist/index.css",
33
+ "./dist/index.css": "./dist/index.css"
33
34
  },
34
35
  "main": "dist/index.js",
35
36
  "types": "dist/index.d.ts",
@@ -60,7 +61,7 @@
60
61
  "@angular/compiler-cli": "^17.0.0"
61
62
  },
62
63
  "dependencies": {
63
- "@daltonr/pathwrite-core": "^0.4.0"
64
+ "@daltonr/pathwrite-core": "^0.6.0"
64
65
  },
65
66
  "publishConfig": {
66
67
  "access": "public"
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Injectable, OnDestroy, DestroyRef, signal, Signal } from "@angular/core";
1
+ import { Injectable, OnDestroy, DestroyRef, signal, Signal, inject } from "@angular/core";
2
2
  import { BehaviorSubject, Observable, Subject } from "rxjs";
3
3
  import {
4
4
  PathData,
@@ -24,10 +24,10 @@ import {
24
24
  */
25
25
  @Injectable()
26
26
  export class PathFacade<TData extends PathData = PathData> implements OnDestroy {
27
- private readonly engine = new PathEngine();
27
+ private _engine = new PathEngine();
28
28
  private readonly _state$ = new BehaviorSubject<PathSnapshot<TData> | null>(null);
29
29
  private readonly _events$ = new Subject<PathEvent>();
30
- private readonly unsubscribeFromEngine: () => void;
30
+ private _unsubscribeFromEngine: () => void = () => {};
31
31
  private readonly _stateSignal = signal<PathSnapshot<TData> | null>(null);
32
32
 
33
33
  public readonly state$: Observable<PathSnapshot<TData> | null> = this._state$.asObservable();
@@ -36,7 +36,37 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
36
36
  public readonly stateSignal: Signal<PathSnapshot<TData> | null> = this._stateSignal.asReadonly();
37
37
 
38
38
  public constructor() {
39
- this.unsubscribeFromEngine = this.engine.subscribe((event) => {
39
+ this.connectEngine(this._engine);
40
+ }
41
+
42
+ /**
43
+ * Adopt an externally-managed `PathEngine` — for example, the engine returned
44
+ * by `restoreOrStart()` from `@daltonr/pathwrite-store-http`.
45
+ *
46
+ * The facade immediately reflects the engine's current state and forwards all
47
+ * subsequent events. The **caller** is responsible for the engine's lifecycle
48
+ * (starting, cleanup); the facade only subscribes to it.
49
+ *
50
+ * ```typescript
51
+ * const { engine } = await restoreOrStart({ store, key, path, initialData, observers: [...] });
52
+ * facade.adoptEngine(engine);
53
+ * ```
54
+ */
55
+ public adoptEngine(engine: PathEngine): void {
56
+ // Disconnect from whatever engine we're currently listening to
57
+ this._unsubscribeFromEngine();
58
+ this._engine = engine;
59
+ this.connectEngine(engine);
60
+ }
61
+
62
+ private connectEngine(engine: PathEngine): void {
63
+ // Seed state immediately — critical when restoring a persisted path since
64
+ // the engine is already running before the facade connects to it.
65
+ const current = engine.snapshot() as PathSnapshot<TData> | null;
66
+ this._state$.next(current);
67
+ this._stateSignal.set(current);
68
+
69
+ this._unsubscribeFromEngine = engine.subscribe((event) => {
40
70
  this._events$.next(event);
41
71
  if (event.type === "stateChanged" || event.type === "resumed") {
42
72
  this._state$.next(event.snapshot as PathSnapshot<TData>);
@@ -49,13 +79,13 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
49
79
  }
50
80
 
51
81
  public ngOnDestroy(): void {
52
- this.unsubscribeFromEngine();
82
+ this._unsubscribeFromEngine();
53
83
  this._events$.complete();
54
84
  this._state$.complete();
55
85
  }
56
86
 
57
87
  public start(path: PathDefinition<any>, initialData: PathData = {}): Promise<void> {
58
- return this.engine.start(path, initialData);
88
+ return this._engine.start(path, initialData);
59
89
  }
60
90
 
61
91
  /**
@@ -65,38 +95,38 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
65
95
  * component that provides this facade.
66
96
  */
67
97
  public restart(path: PathDefinition<any>, initialData: PathData = {}): Promise<void> {
68
- return this.engine.restart(path, initialData);
98
+ return this._engine.restart(path, initialData);
69
99
  }
70
100
 
71
101
  public startSubPath(path: PathDefinition<any>, initialData: PathData = {}, meta?: Record<string, unknown>): Promise<void> {
72
- return this.engine.startSubPath(path, initialData, meta);
102
+ return this._engine.startSubPath(path, initialData, meta);
73
103
  }
74
104
 
75
105
  public next(): Promise<void> {
76
- return this.engine.next();
106
+ return this._engine.next();
77
107
  }
78
108
 
79
109
  public previous(): Promise<void> {
80
- return this.engine.previous();
110
+ return this._engine.previous();
81
111
  }
82
112
 
83
113
  public cancel(): Promise<void> {
84
- return this.engine.cancel();
114
+ return this._engine.cancel();
85
115
  }
86
116
 
87
117
  public setData<K extends string & keyof TData>(key: K, value: TData[K]): Promise<void> {
88
- return this.engine.setData(key, value as unknown);
118
+ return this._engine.setData(key, value as unknown);
89
119
  }
90
120
 
91
121
  public goToStep(stepId: string): Promise<void> {
92
- return this.engine.goToStep(stepId);
122
+ return this._engine.goToStep(stepId);
93
123
  }
94
124
 
95
125
  /** Jump to a step by ID, checking the current step's canMoveNext (forward) or
96
126
  * canMovePrevious (backward) guard first. Navigation is blocked if the guard
97
127
  * returns false. Throws if the step ID does not exist. */
98
128
  public goToStepChecked(stepId: string): Promise<void> {
99
- return this.engine.goToStepChecked(stepId);
129
+ return this._engine.goToStepChecked(stepId);
100
130
  }
101
131
 
102
132
  public snapshot(): PathSnapshot<TData> | null {
@@ -104,6 +134,101 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
104
134
  }
105
135
  }
106
136
 
137
+ // ---------------------------------------------------------------------------
138
+ // injectPath() - Signal-based path access
139
+ // ---------------------------------------------------------------------------
140
+
141
+ /**
142
+ * Return type of `injectPath()`. Provides signal-based reactive access to the
143
+ * path state and strongly-typed navigation actions. Mirrors React's `usePathContext()`
144
+ * return type for consistency across adapters.
145
+ */
146
+ export interface InjectPathReturn<TData extends PathData = PathData> {
147
+ /** Current path snapshot as a signal. Returns `null` when no path is active. */
148
+ snapshot: Signal<PathSnapshot<TData> | null>;
149
+ /** Start (or restart) a path. */
150
+ start: (path: PathDefinition<any>, initialData?: PathData) => Promise<void>;
151
+ /** Push a sub-path onto the stack. */
152
+ startSubPath: (path: PathDefinition<any>, initialData?: PathData, meta?: Record<string, unknown>) => Promise<void>;
153
+ /** Advance one step. Completes the path on the last step. */
154
+ next: () => Promise<void>;
155
+ /** Go back one step. No-op when already on the first step. */
156
+ previous: () => Promise<void>;
157
+ /** Cancel the active path (or sub-path). */
158
+ cancel: () => Promise<void>;
159
+ /** Update a single data field. */
160
+ setData: <K extends string & keyof TData>(key: K, value: TData[K]) => Promise<void>;
161
+ /** Jump to a step by ID without checking guards. */
162
+ goToStep: (stepId: string) => Promise<void>;
163
+ /** Jump to a step by ID, checking guards first. */
164
+ goToStepChecked: (stepId: string) => Promise<void>;
165
+ /**
166
+ * Tears down any active path and immediately starts the given path fresh.
167
+ * Use for "Start over" / retry flows.
168
+ */
169
+ restart: (path: PathDefinition<any>, initialData?: PathData) => Promise<void>;
170
+ }
171
+
172
+ /**
173
+ * Inject a PathFacade and return a signal-based API for use in Angular components.
174
+ * Requires `PathFacade` to be provided in the component's injector tree (either via
175
+ * `providers: [PathFacade]` in the component or a parent component).
176
+ *
177
+ * **This is the recommended way to consume Pathwrite in Angular components** — it
178
+ * provides the same ergonomic, framework-native API that React's `usePathContext()`
179
+ * and Vue's `usePath()` offer. No template references or manual facade injection needed.
180
+ *
181
+ * The optional generic `TData` narrows `snapshot().data` and `setData()` to your
182
+ * data shape. It is a **type-level assertion**, not a runtime guarantee.
183
+ *
184
+ * @example
185
+ * ```typescript
186
+ * @Component({
187
+ * selector: 'app-contact-step',
188
+ * standalone: true,
189
+ * providers: [PathFacade], // ← Provide at this component or a parent
190
+ * template: `
191
+ * @if (path.snapshot(); as s) {
192
+ * <div>Step: {{ s.activeStep?.title }}</div>
193
+ * <button (click)="path.next()">Next</button>
194
+ * }
195
+ * `
196
+ * })
197
+ * export class ContactStepComponent {
198
+ * protected readonly path = injectPath<ContactData>();
199
+ *
200
+ * updateName(name: string) {
201
+ * this.path.setData('name', name);
202
+ * }
203
+ * }
204
+ * ```
205
+ *
206
+ * @throws Error if PathFacade is not provided in the injector tree
207
+ */
208
+ export function injectPath<TData extends PathData = PathData>(): InjectPathReturn<TData> {
209
+ const facade = inject(PathFacade, { optional: true }) as PathFacade<TData> | null;
210
+
211
+ if (!facade) {
212
+ throw new Error(
213
+ "injectPath() requires PathFacade to be provided. " +
214
+ "Add 'providers: [PathFacade]' to your component or a parent component."
215
+ );
216
+ }
217
+
218
+ return {
219
+ snapshot: facade.stateSignal,
220
+ start: (path, initialData = {}) => facade.start(path, initialData),
221
+ startSubPath: (path, initialData = {}, meta) => facade.startSubPath(path, initialData, meta),
222
+ next: () => facade.next(),
223
+ previous: () => facade.previous(),
224
+ cancel: () => facade.cancel(),
225
+ setData: (key, value) => facade.setData(key, value),
226
+ goToStep: (stepId) => facade.goToStep(stepId),
227
+ goToStepChecked: (stepId) => facade.goToStepChecked(stepId),
228
+ restart: (path, initialData = {}) => facade.restart(path, initialData),
229
+ };
230
+ }
231
+
107
232
  // ---------------------------------------------------------------------------
108
233
  // Forms integration
109
234
  // ---------------------------------------------------------------------------
@@ -176,6 +301,7 @@ export function syncFormGroup<TData extends PathData = PathData>(
176
301
  // Re-export core types for convenience (users don't need to import from @daltonr/pathwrite-core)
177
302
  export type {
178
303
  PathData,
304
+ FieldErrors,
179
305
  PathDefinition,
180
306
  PathEvent,
181
307
  PathSnapshot,
@@ -184,3 +310,5 @@ export type {
184
310
  SerializedPathState
185
311
  } from "@daltonr/pathwrite-core";
186
312
 
313
+ export { PathEngine } from "@daltonr/pathwrite-core";
314
+