@daltonr/pathwrite-angular 0.9.0 → 0.10.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/index.css CHANGED
@@ -285,6 +285,16 @@
285
285
  content: ":";
286
286
  }
287
287
 
288
+ /* ------------------------------------------------------------------ */
289
+ /* Blocking error (guard-level message, not field-attached) */
290
+ /* ------------------------------------------------------------------ */
291
+ .pw-shell__blocking-error {
292
+ margin: 0;
293
+ padding: 8px 16px;
294
+ font-size: 13px;
295
+ color: var(--pw-color-error);
296
+ }
297
+
288
298
  /* ------------------------------------------------------------------ */
289
299
  /* Warning messages */
290
300
  /* ------------------------------------------------------------------ */
@@ -361,6 +371,7 @@
361
371
  background: var(--pw-color-primary);
362
372
  border-color: var(--pw-color-primary);
363
373
  color: #fff;
374
+ position: relative;
364
375
  }
365
376
 
366
377
  .pw-shell__btn--next:hover:not(:disabled) {
@@ -368,6 +379,29 @@
368
379
  border-color: #1d4ed8;
369
380
  }
370
381
 
382
+ @keyframes pw-spin {
383
+ to { transform: rotate(360deg); }
384
+ }
385
+
386
+ .pw-shell__btn--next.pw-shell__btn--loading {
387
+ color: transparent;
388
+ pointer-events: none;
389
+ }
390
+
391
+ .pw-shell__btn--next.pw-shell__btn--loading::after {
392
+ content: "";
393
+ position: absolute;
394
+ top: 50%;
395
+ left: 50%;
396
+ width: 14px;
397
+ height: 14px;
398
+ margin: -7px 0 0 -7px;
399
+ border: 2px solid rgba(255, 255, 255, 0.35);
400
+ border-top-color: #fff;
401
+ border-radius: 50%;
402
+ animation: pw-spin 0.6s linear infinite;
403
+ }
404
+
371
405
  .pw-shell__btn--back {
372
406
  background: transparent;
373
407
  border-color: var(--pw-color-primary);
@@ -388,3 +422,55 @@
388
422
  background: var(--pw-color-primary-light);
389
423
  }
390
424
 
425
+ /* ------------------------------------------------------------------ */
426
+ /* Error panel — replaces footer when status === "error" */
427
+ /* ------------------------------------------------------------------ */
428
+ .pw-shell__error {
429
+ background: var(--pw-color-error-bg);
430
+ border: 1px solid var(--pw-color-error-border);
431
+ border-radius: var(--pw-shell-radius);
432
+ padding: 16px 20px;
433
+ display: flex;
434
+ flex-direction: column;
435
+ gap: 10px;
436
+ }
437
+
438
+ .pw-shell__error-title {
439
+ font-size: 14px;
440
+ font-weight: 600;
441
+ color: var(--pw-color-error);
442
+ }
443
+
444
+ .pw-shell__error-message {
445
+ font-size: 13px;
446
+ color: var(--pw-color-muted);
447
+ }
448
+
449
+ .pw-shell__error-actions {
450
+ display: flex;
451
+ gap: 8px;
452
+ align-items: center;
453
+ margin-top: 2px;
454
+ }
455
+
456
+ .pw-shell__btn--retry {
457
+ background: var(--pw-color-error);
458
+ border-color: var(--pw-color-error);
459
+ color: #fff;
460
+ }
461
+
462
+ .pw-shell__btn--retry:hover:not(:disabled) {
463
+ opacity: 0.9;
464
+ }
465
+
466
+ .pw-shell__btn--suspend {
467
+ color: var(--pw-color-muted);
468
+ border-color: transparent;
469
+ background: transparent;
470
+ font-size: 13px;
471
+ }
472
+
473
+ .pw-shell__btn--suspend:hover:not(:disabled) {
474
+ background: var(--pw-color-primary-light);
475
+ }
476
+
package/dist/index.d.ts CHANGED
@@ -51,6 +51,10 @@ export declare class PathFacade<TData extends PathData = PathData> implements On
51
51
  * component that provides this facade.
52
52
  */
53
53
  restart(): Promise<void>;
54
+ /** Re-runs the operation that set `snapshot().error`. Increments `retryCount` on repeated failure. No-op when there is no pending error. */
55
+ retry(): Promise<void>;
56
+ /** Pauses the path with intent to return. Emits `suspended`. All state is preserved. */
57
+ suspend(): Promise<void>;
54
58
  startSubPath(path: PathDefinition<any>, initialData?: PathData, meta?: Record<string, unknown>): Promise<void>;
55
59
  next(): Promise<void>;
56
60
  previous(): Promise<void>;
@@ -69,11 +73,11 @@ export declare class PathFacade<TData extends PathData = PathData> implements On
69
73
  static ɵprov: i0.ɵɵInjectableDeclaration<PathFacade<any>>;
70
74
  }
71
75
  /**
72
- * Return type of `injectPath()`. Provides signal-based reactive access to the
76
+ * Return type of `usePathContext()`. Provides signal-based reactive access to the
73
77
  * path state and strongly-typed navigation actions. Mirrors React's `usePathContext()`
74
78
  * return type for consistency across adapters.
75
79
  */
76
- export interface InjectPathReturn<TData extends PathData = PathData> {
80
+ export interface UsePathContextReturn<TData extends PathData = PathData> {
77
81
  /** Current path snapshot as a signal. Returns `null` when no path is active. */
78
82
  snapshot: Signal<PathSnapshot<TData> | null>;
79
83
  /** Start (or restart) a path. */
@@ -99,15 +103,19 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
99
103
  * Use for "Start over" / retry flows.
100
104
  */
101
105
  restart: () => Promise<void>;
106
+ /** Re-run the operation that set `snapshot().error`. */
107
+ retry: () => Promise<void>;
108
+ /** Pause with intent to return, preserving all state. Emits `suspended`. */
109
+ suspend: () => Promise<void>;
102
110
  }
103
111
  /**
104
- * Inject a PathFacade and return a signal-based API for use in Angular components.
112
+ * Access the nearest `PathFacade`'s path instance for use in Angular step components.
105
113
  * Requires `PathFacade` to be provided in the component's injector tree (either via
106
114
  * `providers: [PathFacade]` in the component or a parent component).
107
115
  *
108
116
  * **This is the recommended way to consume Pathwrite in Angular components** — it
109
- * provides the same ergonomic, framework-native API that React's `usePathContext()`
110
- * and Vue's `usePath()` offer. No template references or manual facade injection needed.
117
+ * provides the same ergonomic API as React's `usePathContext()` and Vue's `usePathContext()`.
118
+ * No template references or manual facade injection needed.
111
119
  *
112
120
  * The optional generic `TData` narrows `snapshot().data` and `setData()` to your
113
121
  * data shape. It is a **type-level assertion**, not a runtime guarantee.
@@ -126,7 +134,7 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
126
134
  * `
127
135
  * })
128
136
  * export class ContactStepComponent {
129
- * protected readonly path = injectPath<ContactData>();
137
+ * protected readonly path = usePathContext<ContactData>();
130
138
  *
131
139
  * updateName(name: string) {
132
140
  * this.path.setData('name', name);
@@ -136,7 +144,7 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
136
144
  *
137
145
  * @throws Error if PathFacade is not provided in the injector tree
138
146
  */
139
- export declare function injectPath<TData extends PathData = PathData>(): InjectPathReturn<TData>;
147
+ export declare function usePathContext<TData extends PathData = PathData>(): UsePathContextReturn<TData>;
140
148
  /**
141
149
  * Minimal interface describing what syncFormGroup needs from an Angular
142
150
  * FormGroup. Typed as a duck interface so that @angular/forms is not a
package/dist/index.js CHANGED
@@ -83,6 +83,14 @@ export class PathFacade {
83
83
  restart() {
84
84
  return this._engine.restart();
85
85
  }
86
+ /** Re-runs the operation that set `snapshot().error`. Increments `retryCount` on repeated failure. No-op when there is no pending error. */
87
+ retry() {
88
+ return this._engine.retry();
89
+ }
90
+ /** Pauses the path with intent to return. Emits `suspended`. All state is preserved. */
91
+ suspend() {
92
+ return this._engine.suspend();
93
+ }
86
94
  startSubPath(path, initialData = {}, meta) {
87
95
  return this._engine.startSubPath(path, initialData, meta);
88
96
  }
@@ -122,13 +130,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
122
130
  type: Injectable
123
131
  }], ctorParameters: () => [] });
124
132
  /**
125
- * Inject a PathFacade and return a signal-based API for use in Angular components.
133
+ * Access the nearest `PathFacade`'s path instance for use in Angular step components.
126
134
  * Requires `PathFacade` to be provided in the component's injector tree (either via
127
135
  * `providers: [PathFacade]` in the component or a parent component).
128
136
  *
129
137
  * **This is the recommended way to consume Pathwrite in Angular components** — it
130
- * provides the same ergonomic, framework-native API that React's `usePathContext()`
131
- * and Vue's `usePath()` offer. No template references or manual facade injection needed.
138
+ * provides the same ergonomic API as React's `usePathContext()` and Vue's `usePathContext()`.
139
+ * No template references or manual facade injection needed.
132
140
  *
133
141
  * The optional generic `TData` narrows `snapshot().data` and `setData()` to your
134
142
  * data shape. It is a **type-level assertion**, not a runtime guarantee.
@@ -147,7 +155,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
147
155
  * `
148
156
  * })
149
157
  * export class ContactStepComponent {
150
- * protected readonly path = injectPath<ContactData>();
158
+ * protected readonly path = usePathContext<ContactData>();
151
159
  *
152
160
  * updateName(name: string) {
153
161
  * this.path.setData('name', name);
@@ -157,10 +165,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
157
165
  *
158
166
  * @throws Error if PathFacade is not provided in the injector tree
159
167
  */
160
- export function injectPath() {
168
+ export function usePathContext() {
161
169
  const facade = inject(PathFacade, { optional: true });
162
170
  if (!facade) {
163
- throw new Error("injectPath() requires PathFacade to be provided. " +
171
+ throw new Error("usePathContext() requires PathFacade to be provided. " +
164
172
  "Add 'providers: [PathFacade]' to your component or a parent component.");
165
173
  }
166
174
  return {
@@ -175,6 +183,8 @@ export function injectPath() {
175
183
  goToStep: (stepId) => facade.goToStep(stepId),
176
184
  goToStepChecked: (stepId) => facade.goToStepChecked(stepId),
177
185
  restart: () => facade.restart(),
186
+ retry: () => facade.retry(),
187
+ suspend: () => facade.suspend(),
178
188
  };
179
189
  }
180
190
  /**
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/shell.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { TemplateRef, EventEmitter, QueryList, OnInit, OnChanges, OnDestroy, SimpleChanges, Injector } from "@angular/core";
2
- import { PathData, PathDefinition, PathEngine, PathEvent, PathSnapshot, ProgressLayout } from "@daltonr/pathwrite-core";
2
+ import { PathData, PathDefinition, PathEngine, PathEvent, PathSnapshot, ProgressLayout, formatFieldKey, errorPhaseMessage } from "@daltonr/pathwrite-core";
3
3
  import { PathFacade } from "./index";
4
4
  import * as i0 from "@angular/core";
5
5
  /**
@@ -16,6 +16,10 @@ export interface PathShellActions {
16
16
  setData: (key: string, value: unknown) => Promise<void>;
17
17
  /** Restart the shell's current path with its current `initialData`. */
18
18
  restart: () => Promise<void>;
19
+ /** Re-run the operation that set `snapshot.error`. */
20
+ retry: () => Promise<void>;
21
+ /** Pause with intent to return, preserving all state. Emits `suspended`. */
22
+ suspend: () => Promise<void>;
19
23
  }
20
24
  /**
21
25
  * Structural directive that associates a template with a step ID.
@@ -124,6 +128,8 @@ export declare class PathShellComponent implements OnInit, OnChanges, OnDestroy
124
128
  nextLabel: string;
125
129
  /** Label for the Next button when on the last step. */
126
130
  completeLabel: string;
131
+ /** Label shown on the Next/Complete button while an async operation is in progress. When undefined, the button keeps its label and shows a CSS spinner only. */
132
+ loadingLabel?: string;
127
133
  /** Label for the Cancel button. */
128
134
  cancelLabel: string;
129
135
  /** Hide the Cancel button entirely. */
@@ -186,9 +192,8 @@ export declare class PathShellComponent implements OnInit, OnChanges, OnDestroy
186
192
  protected warningEntries(s: PathSnapshot): [string, string][];
187
193
  /** Resolves "auto" footerLayout based on snapshot. Single-step top-level → "form", otherwise → "wizard". */
188
194
  protected getResolvedFooterLayout(s: PathSnapshot): "wizard" | "form";
189
- /** Converts a camelCase or lowercase field key to a display label.
190
- * e.g. "firstName" "First Name", "email" → "Email" */
191
- protected formatFieldKey(key: string): string;
195
+ protected errorPhaseMessage: typeof errorPhaseMessage;
196
+ protected formatFieldKey: typeof formatFieldKey;
192
197
  static ɵfac: i0.ɵɵFactoryDeclaration<PathShellComponent, never>;
193
- static ɵcmp: i0.ɵɵComponentDeclaration<PathShellComponent, "pw-shell", never, { "path": { "alias": "path"; "required": false; }; "engine": { "alias": "engine"; "required": false; }; "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; }; "validationDisplay": { "alias": "validationDisplay"; "required": false; }; "progressLayout": { "alias": "progressLayout"; "required": false; }; }, { "complete": "complete"; "cancel": "cancel"; "event": "event"; }, ["customHeader", "customFooter", "stepDirectives"], never, true, never>;
198
+ static ɵcmp: i0.ɵɵComponentDeclaration<PathShellComponent, "pw-shell", never, { "path": { "alias": "path"; "required": false; }; "engine": { "alias": "engine"; "required": false; }; "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; }; "loadingLabel": { "alias": "loadingLabel"; "required": false; }; "cancelLabel": { "alias": "cancelLabel"; "required": false; }; "hideCancel": { "alias": "hideCancel"; "required": false; }; "hideProgress": { "alias": "hideProgress"; "required": false; }; "footerLayout": { "alias": "footerLayout"; "required": false; }; "validationDisplay": { "alias": "validationDisplay"; "required": false; }; "progressLayout": { "alias": "progressLayout"; "required": false; }; }, { "complete": "complete"; "cancel": "cancel"; "event": "event"; }, ["customHeader", "customFooter", "stepDirectives"], never, true, never>;
194
199
  }
package/dist/shell.js CHANGED
@@ -2,6 +2,7 @@ import { Component, Directive, Input, Output, EventEmitter, ContentChild, Conten
2
2
  import { CommonModule } from "@angular/common";
3
3
  import { Subject } from "rxjs";
4
4
  import { takeUntil } from "rxjs/operators";
5
+ import { formatFieldKey, errorPhaseMessage, } from "@daltonr/pathwrite-core";
5
6
  import { PathFacade } from "./index";
6
7
  import * as i0 from "@angular/core";
7
8
  import * as i1 from "@angular/common";
@@ -160,8 +161,12 @@ export class PathShellComponent {
160
161
  goToStepChecked: (id) => this.facade.goToStepChecked(id),
161
162
  setData: (key, value) => this.facade.setData(key, value),
162
163
  restart: () => this.facade.restart(),
164
+ retry: () => this.facade.retry(),
165
+ suspend: () => this.facade.suspend(),
163
166
  };
164
167
  this.destroy$ = new Subject();
168
+ this.errorPhaseMessage = errorPhaseMessage;
169
+ this.formatFieldKey = formatFieldKey;
165
170
  }
166
171
  ngOnChanges(changes) {
167
172
  if (changes['engine'] && this.engine) {
@@ -216,13 +221,8 @@ export class PathShellComponent {
216
221
  ? (s.stepCount === 1 && s.nestingLevel === 0 ? "form" : "wizard")
217
222
  : this.footerLayout;
218
223
  }
219
- /** Converts a camelCase or lowercase field key to a display label.
220
- * e.g. "firstName" → "First Name", "email" → "Email" */
221
- formatFieldKey(key) {
222
- return key.replace(/([A-Z])/g, " $1").replace(/^./, c => c.toUpperCase()).trim();
223
- }
224
224
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: PathShellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
225
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PathShellComponent, isStandalone: true, selector: "pw-shell", inputs: { path: "path", engine: "engine", initialData: "initialData", autoStart: "autoStart", backLabel: "backLabel", nextLabel: "nextLabel", completeLabel: "completeLabel", cancelLabel: "cancelLabel", hideCancel: "hideCancel", hideProgress: "hideProgress", footerLayout: "footerLayout", validationDisplay: "validationDisplay", progressLayout: "progressLayout" }, outputs: { complete: "complete", cancel: "cancel", event: "event" }, providers: [PathFacade], queries: [{ propertyName: "customHeader", first: true, predicate: PathShellHeaderDirective, descendants: true }, { propertyName: "customFooter", first: true, predicate: PathShellFooterDirective, descendants: true }, { propertyName: "stepDirectives", predicate: PathStepDirective }], usesOnChanges: true, ngImport: i0, template: `
225
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: PathShellComponent, isStandalone: true, selector: "pw-shell", inputs: { path: "path", engine: "engine", initialData: "initialData", autoStart: "autoStart", backLabel: "backLabel", nextLabel: "nextLabel", completeLabel: "completeLabel", loadingLabel: "loadingLabel", cancelLabel: "cancelLabel", hideCancel: "hideCancel", hideProgress: "hideProgress", footerLayout: "footerLayout", validationDisplay: "validationDisplay", progressLayout: "progressLayout" }, outputs: { complete: "complete", cancel: "cancel", event: "event" }, providers: [PathFacade], queries: [{ propertyName: "customHeader", first: true, predicate: PathShellHeaderDirective, descendants: true }, { propertyName: "customFooter", first: true, predicate: PathShellFooterDirective, descendants: true }, { propertyName: "stepDirectives", predicate: PathStepDirective }], usesOnChanges: true, ngImport: i0, template: `
226
226
  <!-- Empty state -->
227
227
  <div class="pw-shell" *ngIf="!(facade.state$ | async)">
228
228
  <div class="pw-shell__empty" *ngIf="!started">
@@ -296,10 +296,43 @@ export class PathShellComponent {
296
296
  </li>
297
297
  </ul>
298
298
 
299
+ <!-- Blocking error — guard returned { allowed: false, reason } -->
300
+ <p class="pw-shell__blocking-error"
301
+ *ngIf="validationDisplay !== 'inline' && s.hasAttemptedNext && s.blockingError">
302
+ {{ s.blockingError }}
303
+ </p>
304
+
305
+ <!-- Error panel — replaces footer when an async operation has failed -->
306
+ <div class="pw-shell__error" *ngIf="s.status === 'error' && s.error; else footerOrCustom">
307
+ <div class="pw-shell__error-title">{{ s.error!.retryCount >= 2 ? 'Still having trouble.' : 'Something went wrong.' }}</div>
308
+ <div class="pw-shell__error-message">{{ errorPhaseMessage(s.error!.phase) }}{{ s.error!.message ? ' ' + s.error!.message : '' }}</div>
309
+ <div class="pw-shell__error-actions">
310
+ <button
311
+ *ngIf="s.error!.retryCount < 2"
312
+ type="button"
313
+ class="pw-shell__btn pw-shell__btn--retry"
314
+ (click)="facade.retry()"
315
+ >Try again</button>
316
+ <button
317
+ *ngIf="s.hasPersistence"
318
+ type="button"
319
+ [class]="'pw-shell__btn ' + (s.error!.retryCount >= 2 ? 'pw-shell__btn--retry' : 'pw-shell__btn--suspend')"
320
+ (click)="facade.suspend()"
321
+ >Save and come back later</button>
322
+ <button
323
+ *ngIf="s.error!.retryCount >= 2 && !s.hasPersistence"
324
+ type="button"
325
+ class="pw-shell__btn pw-shell__btn--retry"
326
+ (click)="facade.retry()"
327
+ >Try again</button>
328
+ </div>
329
+ </div>
299
330
  <!-- Footer — custom or default navigation buttons -->
300
- <ng-container *ngIf="customFooter; else defaultFooter">
301
- <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
302
- </ng-container>
331
+ <ng-template #footerOrCustom>
332
+ <ng-container *ngIf="customFooter; else defaultFooter">
333
+ <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
334
+ </ng-container>
335
+ </ng-template>
303
336
  <ng-template #defaultFooter>
304
337
  <div class="pw-shell__footer">
305
338
  <div class="pw-shell__footer-left">
@@ -308,7 +341,7 @@ export class PathShellComponent {
308
341
  *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
309
342
  type="button"
310
343
  class="pw-shell__btn pw-shell__btn--cancel"
311
- [disabled]="s.isNavigating"
344
+ [disabled]="s.status !== 'idle'"
312
345
  (click)="facade.cancel()"
313
346
  >{{ cancelLabel }}</button>
314
347
  <!-- Wizard mode: Back on the left -->
@@ -316,7 +349,7 @@ export class PathShellComponent {
316
349
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
317
350
  type="button"
318
351
  class="pw-shell__btn pw-shell__btn--back"
319
- [disabled]="s.isNavigating || !s.canMovePrevious"
352
+ [disabled]="s.status !== 'idle' || !s.canMovePrevious"
320
353
  (click)="facade.previous()"
321
354
  >{{ backLabel }}</button>
322
355
  </div>
@@ -326,16 +359,17 @@ export class PathShellComponent {
326
359
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
327
360
  type="button"
328
361
  class="pw-shell__btn pw-shell__btn--cancel"
329
- [disabled]="s.isNavigating"
362
+ [disabled]="s.status !== 'idle'"
330
363
  (click)="facade.cancel()"
331
364
  >{{ cancelLabel }}</button>
332
365
  <!-- Both modes: Submit on the right -->
333
366
  <button
334
367
  type="button"
335
368
  class="pw-shell__btn pw-shell__btn--next"
336
- [disabled]="s.isNavigating"
369
+ [class.pw-shell__btn--loading]="s.status !== 'idle'"
370
+ [disabled]="s.status !== 'idle'"
337
371
  (click)="facade.next()"
338
- >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
372
+ >{{ s.status !== 'idle' && loadingLabel ? loadingLabel : s.isLastStep ? completeLabel : nextLabel }}</button>
339
373
  </div>
340
374
  </div>
341
375
  </ng-template>
@@ -424,10 +458,43 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
424
458
  </li>
425
459
  </ul>
426
460
 
461
+ <!-- Blocking error — guard returned { allowed: false, reason } -->
462
+ <p class="pw-shell__blocking-error"
463
+ *ngIf="validationDisplay !== 'inline' && s.hasAttemptedNext && s.blockingError">
464
+ {{ s.blockingError }}
465
+ </p>
466
+
467
+ <!-- Error panel — replaces footer when an async operation has failed -->
468
+ <div class="pw-shell__error" *ngIf="s.status === 'error' && s.error; else footerOrCustom">
469
+ <div class="pw-shell__error-title">{{ s.error!.retryCount >= 2 ? 'Still having trouble.' : 'Something went wrong.' }}</div>
470
+ <div class="pw-shell__error-message">{{ errorPhaseMessage(s.error!.phase) }}{{ s.error!.message ? ' ' + s.error!.message : '' }}</div>
471
+ <div class="pw-shell__error-actions">
472
+ <button
473
+ *ngIf="s.error!.retryCount < 2"
474
+ type="button"
475
+ class="pw-shell__btn pw-shell__btn--retry"
476
+ (click)="facade.retry()"
477
+ >Try again</button>
478
+ <button
479
+ *ngIf="s.hasPersistence"
480
+ type="button"
481
+ [class]="'pw-shell__btn ' + (s.error!.retryCount >= 2 ? 'pw-shell__btn--retry' : 'pw-shell__btn--suspend')"
482
+ (click)="facade.suspend()"
483
+ >Save and come back later</button>
484
+ <button
485
+ *ngIf="s.error!.retryCount >= 2 && !s.hasPersistence"
486
+ type="button"
487
+ class="pw-shell__btn pw-shell__btn--retry"
488
+ (click)="facade.retry()"
489
+ >Try again</button>
490
+ </div>
491
+ </div>
427
492
  <!-- Footer — custom or default navigation buttons -->
428
- <ng-container *ngIf="customFooter; else defaultFooter">
429
- <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
430
- </ng-container>
493
+ <ng-template #footerOrCustom>
494
+ <ng-container *ngIf="customFooter; else defaultFooter">
495
+ <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
496
+ </ng-container>
497
+ </ng-template>
431
498
  <ng-template #defaultFooter>
432
499
  <div class="pw-shell__footer">
433
500
  <div class="pw-shell__footer-left">
@@ -436,7 +503,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
436
503
  *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
437
504
  type="button"
438
505
  class="pw-shell__btn pw-shell__btn--cancel"
439
- [disabled]="s.isNavigating"
506
+ [disabled]="s.status !== 'idle'"
440
507
  (click)="facade.cancel()"
441
508
  >{{ cancelLabel }}</button>
442
509
  <!-- Wizard mode: Back on the left -->
@@ -444,7 +511,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
444
511
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
445
512
  type="button"
446
513
  class="pw-shell__btn pw-shell__btn--back"
447
- [disabled]="s.isNavigating || !s.canMovePrevious"
514
+ [disabled]="s.status !== 'idle' || !s.canMovePrevious"
448
515
  (click)="facade.previous()"
449
516
  >{{ backLabel }}</button>
450
517
  </div>
@@ -454,16 +521,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
454
521
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
455
522
  type="button"
456
523
  class="pw-shell__btn pw-shell__btn--cancel"
457
- [disabled]="s.isNavigating"
524
+ [disabled]="s.status !== 'idle'"
458
525
  (click)="facade.cancel()"
459
526
  >{{ cancelLabel }}</button>
460
527
  <!-- Both modes: Submit on the right -->
461
528
  <button
462
529
  type="button"
463
530
  class="pw-shell__btn pw-shell__btn--next"
464
- [disabled]="s.isNavigating"
531
+ [class.pw-shell__btn--loading]="s.status !== 'idle'"
532
+ [disabled]="s.status !== 'idle'"
465
533
  (click)="facade.next()"
466
- >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
534
+ >{{ s.status !== 'idle' && loadingLabel ? loadingLabel : s.isLastStep ? completeLabel : nextLabel }}</button>
467
535
  </div>
468
536
  </div>
469
537
  </ng-template>
@@ -484,6 +552,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
484
552
  type: Input
485
553
  }], completeLabel: [{
486
554
  type: Input
555
+ }], loadingLabel: [{
556
+ type: Input
487
557
  }], cancelLabel: [{
488
558
  type: Input
489
559
  }], hideCancel: [{
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":"AAAA,OAAO,EACL,SAAS,EACT,SAAS,EAET,KAAK,EACL,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,eAAe,EAMf,MAAM,EACN,QAAQ,EACR,uBAAuB,EACxB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAQL,cAAc,EACd,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;;;AA0BrC,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAE9E;;;;;;;;;;GAUG;AAEH,MAAM,OAAO,iBAAiB;IAE5B,YAAmC,WAAiC;QAAjC,gBAAW,GAAX,WAAW,CAAsB;IAAG,CAAC;+GAF7D,iBAAiB;mGAAjB,iBAAiB;;4FAAjB,iBAAiB;kBAD7B,SAAS;mBAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,IAAI,EAAE;gFAEP,MAAM;sBAAjD,KAAK;uBAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE;;AAI5C,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;;;;;;;GAYG;AAEH,MAAM,OAAO,wBAAwB;IACnC,YACkB,WAAqD;QAArD,gBAAW,GAAX,WAAW,CAA0C;IACpE,CAAC;+GAHO,wBAAwB;mGAAxB,wBAAwB;;4FAAxB,wBAAwB;kBADpC,SAAS;mBAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE;;AAO5D,8EAA8E;AAC9E,2BAA2B;AAC3B,8EAA8E;AAE9E;;;;;;;;;;;;;;GAcG;AAEH,MAAM,OAAO,wBAAwB;IACnC,YACkB,WAAgF;QAAhF,gBAAW,GAAX,WAAW,CAAqE;IAC/F,CAAC;+GAHO,wBAAwB;mGAAxB,wBAAwB;;4FAAxB,wBAAwB;kBADpC,SAAS;mBAAC,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,IAAI,EAAE;;AAO5D,8EAA8E;AAC9E,qBAAqB;AACrB,8EAA8E;AAE9E;;;;;;;;;;GAUG;AAiKH,MAAM,OAAO,kBAAkB;IAhK/B;QAkLE,yDAAyD;QAChD,gBAAW,GAAa,EAAE,CAAC;QACpC,yFAAyF;QAChF,cAAS,GAAG,IAAI,CAAC;QAC1B,4CAA4C;QACnC,cAAS,GAAG,UAAU,CAAC;QAChC,4CAA4C;QACnC,cAAS,GAAG,MAAM,CAAC;QAC5B,uDAAuD;QAC9C,kBAAa,GAAG,UAAU,CAAC;QAGpC,mCAAmC;QAC1B,gBAAW,GAAG,QAAQ,CAAC;QAChC,uCAAuC;QAC9B,eAAU,GAAG,KAAK,CAAC;QAC5B,iHAAiH;QACxG,iBAAY,GAAG,KAAK,CAAC;QAC9B;;;;;WAKG;QACM,iBAAY,GAA+B,MAAM,CAAC;QAC3D;;;;;WAKG;QACM,sBAAiB,GAAkC,SAAS,CAAC;QACtE;;;;;;WAMG;QACM,mBAAc,GAAmB,QAAQ,CAAC;QAEzC,aAAQ,GAAG,IAAI,YAAY,EAAY,CAAC;QACxC,WAAM,GAAG,IAAI,YAAY,EAAY,CAAC;QACtC,UAAK,GAAG,IAAI,YAAY,EAAa,CAAC;QAMhC,WAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;QAC5C;4FACoF;QACjE,kBAAa,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7C,YAAO,GAAG,KAAK,CAAC;QAEvB,qEAAqE;QAClD,iBAAY,GAAqB;YAClD,IAAI,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;YAC9B,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE;YACtC,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE;YAClC,QAAQ,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1C,eAAe,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,EAAE,CAAC;YACxD,OAAO,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,KAAc,CAAC;YACjE,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;YACpC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;YAChC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE;SACrC,CAAC;QAEe,aAAQ,GAAG,IAAI,OAAO,EAAQ,CAAC;QA6DtC,sBAAiB,GAAG,iBAAiB,CAAC;QACtC,mBAAc,GAAG,cAAc,CAAC;KAC3C;IA7DQ,WAAW,CAAC,OAAsB;QACvC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YACrC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACvC,CAAC;IACH,CAAC;IAEM,QAAQ;QACb,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE;YACrE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;gBAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC/D,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;gBAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/D,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC;IACH,CAAC;IAEM,WAAW;QAChB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAEM,OAAO;QACZ,IAAI,CAAC,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,4DAA4D,CAAC,CAAC;QAC9F,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACjD,CAAC;IAED;;;;;;;;OAQG;IACI,OAAO;QACZ,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IAC/B,CAAC;IAED,+DAA+D;IACrD,YAAY,CAAC,CAAe;QACpC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAuB,CAAC;IAC7D,CAAC;IAED,iEAAiE;IACvD,cAAc,CAAC,CAAe;QACtC,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,CAAuB,CAAC;IAC/D,CAAC;IAED,4GAA4G;IAClG,uBAAuB,CAAC,CAAe;QAC/C,OAAO,IAAI,CAAC,YAAY,KAAK,MAAM;YACjC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,IAAI,CAAC,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;YACjE,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;IACxB,CAAC;+GAjJU,kBAAkB;mGAAlB,kBAAkB,sgBA5JlB,CAAC,UAAU,CAAC,oEA4NT,wBAAwB,+EACxB,wBAAwB,oEAFrB,iBAAiB,kDAzNxB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwJT,2DA3JS,YAAY;;4FA6JX,kBAAkB;kBAhK9B,SAAS;mBAAC;oBACT,QAAQ,EAAE,UAAU;oBACpB,UAAU,EAAE,IAAI;oBAChB,OAAO,EAAE,CAAC,YAAY,CAAC;oBACvB,SAAS,EAAE,CAAC,UAAU,CAAC;oBACvB,eAAe,EAAE,uBAAuB,CAAC,OAAO;oBAChD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwJT;iBACF;8BAGU,IAAI;sBAAZ,KAAK;gBAeG,MAAM;sBAAd,KAAK;gBAEG,WAAW;sBAAnB,KAAK;gBAEG,SAAS;sBAAjB,KAAK;gBAEG,SAAS;sBAAjB,KAAK;gBAEG,SAAS;sBAAjB,KAAK;gBAEG,aAAa;sBAArB,KAAK;gBAEG,YAAY;sBAApB,KAAK;gBAEG,WAAW;sBAAnB,KAAK;gBAEG,UAAU;sBAAlB,KAAK;gBAEG,YAAY;sBAApB,KAAK;gBAOG,YAAY;sBAApB,KAAK;gBAOG,iBAAiB;sBAAzB,KAAK;gBAQG,cAAc;sBAAtB,KAAK;gBAEI,QAAQ;sBAAjB,MAAM;gBACG,MAAM;sBAAf,MAAM;gBACG,KAAK;sBAAd,MAAM;gBAE6B,cAAc;sBAAjD,eAAe;uBAAC,iBAAiB;gBACM,YAAY;sBAAnD,YAAY;uBAAC,wBAAwB;gBACE,YAAY;sBAAnD,YAAY;uBAAC,wBAAwB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@daltonr/pathwrite-angular",
3
- "version": "0.9.0",
3
+ "version": "0.10.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.",
@@ -61,7 +61,7 @@
61
61
  "@angular/compiler-cli": "^17.3.12"
62
62
  },
63
63
  "dependencies": {
64
- "@daltonr/pathwrite-core": "^0.9.0"
64
+ "@daltonr/pathwrite-core": "^0.10.0"
65
65
  },
66
66
  "publishConfig": {
67
67
  "access": "public"
package/src/index.ts CHANGED
@@ -98,6 +98,16 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
98
98
  return this._engine.restart();
99
99
  }
100
100
 
101
+ /** Re-runs the operation that set `snapshot().error`. Increments `retryCount` on repeated failure. No-op when there is no pending error. */
102
+ public retry(): Promise<void> {
103
+ return this._engine.retry();
104
+ }
105
+
106
+ /** Pauses the path with intent to return. Emits `suspended`. All state is preserved. */
107
+ public suspend(): Promise<void> {
108
+ return this._engine.suspend();
109
+ }
110
+
101
111
  public startSubPath(path: PathDefinition<any>, initialData: PathData = {}, meta?: Record<string, unknown>): Promise<void> {
102
112
  return this._engine.startSubPath(path, initialData, meta);
103
113
  }
@@ -141,15 +151,15 @@ export class PathFacade<TData extends PathData = PathData> implements OnDestroy
141
151
  }
142
152
 
143
153
  // ---------------------------------------------------------------------------
144
- // injectPath() - Signal-based path access
154
+ // usePathContext() - Signal-based path access
145
155
  // ---------------------------------------------------------------------------
146
156
 
147
157
  /**
148
- * Return type of `injectPath()`. Provides signal-based reactive access to the
158
+ * Return type of `usePathContext()`. Provides signal-based reactive access to the
149
159
  * path state and strongly-typed navigation actions. Mirrors React's `usePathContext()`
150
160
  * return type for consistency across adapters.
151
161
  */
152
- export interface InjectPathReturn<TData extends PathData = PathData> {
162
+ export interface UsePathContextReturn<TData extends PathData = PathData> {
153
163
  /** Current path snapshot as a signal. Returns `null` when no path is active. */
154
164
  snapshot: Signal<PathSnapshot<TData> | null>;
155
165
  /** Start (or restart) a path. */
@@ -175,16 +185,20 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
175
185
  * Use for "Start over" / retry flows.
176
186
  */
177
187
  restart: () => Promise<void>;
188
+ /** Re-run the operation that set `snapshot().error`. */
189
+ retry: () => Promise<void>;
190
+ /** Pause with intent to return, preserving all state. Emits `suspended`. */
191
+ suspend: () => Promise<void>;
178
192
  }
179
193
 
180
194
  /**
181
- * Inject a PathFacade and return a signal-based API for use in Angular components.
195
+ * Access the nearest `PathFacade`'s path instance for use in Angular step components.
182
196
  * Requires `PathFacade` to be provided in the component's injector tree (either via
183
197
  * `providers: [PathFacade]` in the component or a parent component).
184
198
  *
185
199
  * **This is the recommended way to consume Pathwrite in Angular components** — it
186
- * provides the same ergonomic, framework-native API that React's `usePathContext()`
187
- * and Vue's `usePath()` offer. No template references or manual facade injection needed.
200
+ * provides the same ergonomic API as React's `usePathContext()` and Vue's `usePathContext()`.
201
+ * No template references or manual facade injection needed.
188
202
  *
189
203
  * The optional generic `TData` narrows `snapshot().data` and `setData()` to your
190
204
  * data shape. It is a **type-level assertion**, not a runtime guarantee.
@@ -203,7 +217,7 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
203
217
  * `
204
218
  * })
205
219
  * export class ContactStepComponent {
206
- * protected readonly path = injectPath<ContactData>();
220
+ * protected readonly path = usePathContext<ContactData>();
207
221
  *
208
222
  * updateName(name: string) {
209
223
  * this.path.setData('name', name);
@@ -213,12 +227,12 @@ export interface InjectPathReturn<TData extends PathData = PathData> {
213
227
  *
214
228
  * @throws Error if PathFacade is not provided in the injector tree
215
229
  */
216
- export function injectPath<TData extends PathData = PathData>(): InjectPathReturn<TData> {
230
+ export function usePathContext<TData extends PathData = PathData>(): UsePathContextReturn<TData> {
217
231
  const facade = inject(PathFacade, { optional: true }) as PathFacade<TData> | null;
218
-
232
+
219
233
  if (!facade) {
220
234
  throw new Error(
221
- "injectPath() requires PathFacade to be provided. " +
235
+ "usePathContext() requires PathFacade to be provided. " +
222
236
  "Add 'providers: [PathFacade]' to your component or a parent component."
223
237
  );
224
238
  }
@@ -235,6 +249,8 @@ export function injectPath<TData extends PathData = PathData>(): InjectPathRetur
235
249
  goToStep: (stepId) => facade.goToStep(stepId),
236
250
  goToStepChecked: (stepId) => facade.goToStepChecked(stepId),
237
251
  restart: () => facade.restart(),
252
+ retry: () => facade.retry(),
253
+ suspend: () => facade.suspend(),
238
254
  };
239
255
  }
240
256
 
package/src/shell.ts CHANGED
@@ -26,7 +26,9 @@ import {
26
26
  PathEvent,
27
27
  PathSnapshot,
28
28
  ProgressLayout,
29
- RootProgress
29
+ RootProgress,
30
+ formatFieldKey,
31
+ errorPhaseMessage,
30
32
  } from "@daltonr/pathwrite-core";
31
33
  import { PathFacade } from "./index";
32
34
 
@@ -48,6 +50,10 @@ export interface PathShellActions {
48
50
  setData: (key: string, value: unknown) => Promise<void>;
49
51
  /** Restart the shell's current path with its current `initialData`. */
50
52
  restart: () => Promise<void>;
53
+ /** Re-run the operation that set `snapshot.error`. */
54
+ retry: () => Promise<void>;
55
+ /** Pause with intent to return, preserving all state. Emits `suspended`. */
56
+ suspend: () => Promise<void>;
51
57
  }
52
58
 
53
59
  // ---------------------------------------------------------------------------
@@ -216,10 +222,43 @@ export class PathShellFooterDirective {
216
222
  </li>
217
223
  </ul>
218
224
 
225
+ <!-- Blocking error — guard returned { allowed: false, reason } -->
226
+ <p class="pw-shell__blocking-error"
227
+ *ngIf="validationDisplay !== 'inline' && s.hasAttemptedNext && s.blockingError">
228
+ {{ s.blockingError }}
229
+ </p>
230
+
231
+ <!-- Error panel — replaces footer when an async operation has failed -->
232
+ <div class="pw-shell__error" *ngIf="s.status === 'error' && s.error; else footerOrCustom">
233
+ <div class="pw-shell__error-title">{{ s.error!.retryCount >= 2 ? 'Still having trouble.' : 'Something went wrong.' }}</div>
234
+ <div class="pw-shell__error-message">{{ errorPhaseMessage(s.error!.phase) }}{{ s.error!.message ? ' ' + s.error!.message : '' }}</div>
235
+ <div class="pw-shell__error-actions">
236
+ <button
237
+ *ngIf="s.error!.retryCount < 2"
238
+ type="button"
239
+ class="pw-shell__btn pw-shell__btn--retry"
240
+ (click)="facade.retry()"
241
+ >Try again</button>
242
+ <button
243
+ *ngIf="s.hasPersistence"
244
+ type="button"
245
+ [class]="'pw-shell__btn ' + (s.error!.retryCount >= 2 ? 'pw-shell__btn--retry' : 'pw-shell__btn--suspend')"
246
+ (click)="facade.suspend()"
247
+ >Save and come back later</button>
248
+ <button
249
+ *ngIf="s.error!.retryCount >= 2 && !s.hasPersistence"
250
+ type="button"
251
+ class="pw-shell__btn pw-shell__btn--retry"
252
+ (click)="facade.retry()"
253
+ >Try again</button>
254
+ </div>
255
+ </div>
219
256
  <!-- Footer — custom or default navigation buttons -->
220
- <ng-container *ngIf="customFooter; else defaultFooter">
221
- <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
222
- </ng-container>
257
+ <ng-template #footerOrCustom>
258
+ <ng-container *ngIf="customFooter; else defaultFooter">
259
+ <ng-container *ngTemplateOutlet="customFooter.templateRef; context: { $implicit: s, actions: shellActions }"></ng-container>
260
+ </ng-container>
261
+ </ng-template>
223
262
  <ng-template #defaultFooter>
224
263
  <div class="pw-shell__footer">
225
264
  <div class="pw-shell__footer-left">
@@ -228,7 +267,7 @@ export class PathShellFooterDirective {
228
267
  *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
229
268
  type="button"
230
269
  class="pw-shell__btn pw-shell__btn--cancel"
231
- [disabled]="s.isNavigating"
270
+ [disabled]="s.status !== 'idle'"
232
271
  (click)="facade.cancel()"
233
272
  >{{ cancelLabel }}</button>
234
273
  <!-- Wizard mode: Back on the left -->
@@ -236,7 +275,7 @@ export class PathShellFooterDirective {
236
275
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
237
276
  type="button"
238
277
  class="pw-shell__btn pw-shell__btn--back"
239
- [disabled]="s.isNavigating || !s.canMovePrevious"
278
+ [disabled]="s.status !== 'idle' || !s.canMovePrevious"
240
279
  (click)="facade.previous()"
241
280
  >{{ backLabel }}</button>
242
281
  </div>
@@ -246,16 +285,17 @@ export class PathShellFooterDirective {
246
285
  *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
247
286
  type="button"
248
287
  class="pw-shell__btn pw-shell__btn--cancel"
249
- [disabled]="s.isNavigating"
288
+ [disabled]="s.status !== 'idle'"
250
289
  (click)="facade.cancel()"
251
290
  >{{ cancelLabel }}</button>
252
291
  <!-- Both modes: Submit on the right -->
253
292
  <button
254
293
  type="button"
255
294
  class="pw-shell__btn pw-shell__btn--next"
256
- [disabled]="s.isNavigating"
295
+ [class.pw-shell__btn--loading]="s.status !== 'idle'"
296
+ [disabled]="s.status !== 'idle'"
257
297
  (click)="facade.next()"
258
- >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
298
+ >{{ s.status !== 'idle' && loadingLabel ? loadingLabel : s.isLastStep ? completeLabel : nextLabel }}</button>
259
299
  </div>
260
300
  </div>
261
301
  </ng-template>
@@ -290,6 +330,8 @@ export class PathShellComponent implements OnInit, OnChanges, OnDestroy {
290
330
  @Input() nextLabel = "Next";
291
331
  /** Label for the Next button when on the last step. */
292
332
  @Input() completeLabel = "Complete";
333
+ /** Label shown on the Next/Complete button while an async operation is in progress. When undefined, the button keeps its label and shows a CSS spinner only. */
334
+ @Input() loadingLabel?: string;
293
335
  /** Label for the Cancel button. */
294
336
  @Input() cancelLabel = "Cancel";
295
337
  /** Hide the Cancel button entirely. */
@@ -342,6 +384,8 @@ export class PathShellComponent implements OnInit, OnChanges, OnDestroy {
342
384
  goToStepChecked: (id) => this.facade.goToStepChecked(id),
343
385
  setData: (key, value) => this.facade.setData(key, value as never),
344
386
  restart: () => this.facade.restart(),
387
+ retry: () => this.facade.retry(),
388
+ suspend: () => this.facade.suspend(),
345
389
  };
346
390
 
347
391
  private readonly destroy$ = new Subject<void>();
@@ -405,9 +449,6 @@ export class PathShellComponent implements OnInit, OnChanges, OnDestroy {
405
449
  : this.footerLayout;
406
450
  }
407
451
 
408
- /** Converts a camelCase or lowercase field key to a display label.
409
- * e.g. "firstName" "First Name", "email" → "Email" */
410
- protected formatFieldKey(key: string): string {
411
- return key.replace(/([A-Z])/g, " $1").replace(/^./, c => c.toUpperCase()).trim();
412
- }
452
+ protected errorPhaseMessage = errorPhaseMessage;
453
+ protected formatFieldKey = formatFieldKey;
413
454
  }