@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/src/shell.ts CHANGED
@@ -153,7 +153,7 @@ export class PathShellFooterDirective {
153
153
  <ng-container *ngTemplateOutlet="customHeader.templateRef; context: { $implicit: s }"></ng-container>
154
154
  </ng-container>
155
155
  <ng-template #defaultHeader>
156
- <div class="pw-shell__header" *ngIf="!hideProgress">
156
+ <div class="pw-shell__header" *ngIf="!hideProgress && (s.stepCount > 1 || s.nestingLevel > 0)">
157
157
  <div class="pw-shell__steps">
158
158
  <div
159
159
  *ngFor="let step of s.steps; let i = index"
@@ -179,9 +179,11 @@ export class PathShellFooterDirective {
179
179
  </ng-container>
180
180
  </div>
181
181
 
182
- <!-- Validation messages -->
183
- <ul class="pw-shell__validation" *ngIf="s.validationMessages.length > 0">
184
- <li *ngFor="let msg of s.validationMessages" class="pw-shell__validation-item">{{ msg }}</li>
182
+ <!-- Validation messages — labeled by field name -->
183
+ <ul class="pw-shell__validation" *ngIf="s.hasAttemptedNext && fieldEntries(s).length > 0">
184
+ <li *ngFor="let entry of fieldEntries(s)" class="pw-shell__validation-item">
185
+ <span *ngIf="entry[0] !== '_'" class="pw-shell__validation-label">{{ formatFieldKey(entry[0]) }}</span>{{ entry[1] }}
186
+ </li>
185
187
  </ul>
186
188
 
187
189
  <!-- Footer — custom or default navigation buttons -->
@@ -191,8 +193,17 @@ export class PathShellFooterDirective {
191
193
  <ng-template #defaultFooter>
192
194
  <div class="pw-shell__footer">
193
195
  <div class="pw-shell__footer-left">
196
+ <!-- Form mode: Cancel on the left -->
194
197
  <button
195
- *ngIf="!s.isFirstStep"
198
+ *ngIf="getResolvedFooterLayout(s) === 'form' && !hideCancel"
199
+ type="button"
200
+ class="pw-shell__btn pw-shell__btn--cancel"
201
+ [disabled]="s.isNavigating"
202
+ (click)="facade.cancel()"
203
+ >{{ cancelLabel }}</button>
204
+ <!-- Wizard mode: Back on the left -->
205
+ <button
206
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !s.isFirstStep"
196
207
  type="button"
197
208
  class="pw-shell__btn pw-shell__btn--back"
198
209
  [disabled]="s.isNavigating || !s.canMovePrevious"
@@ -200,19 +211,21 @@ export class PathShellFooterDirective {
200
211
  >{{ backLabel }}</button>
201
212
  </div>
202
213
  <div class="pw-shell__footer-right">
214
+ <!-- Wizard mode: Cancel on the right -->
203
215
  <button
204
- *ngIf="!hideCancel"
216
+ *ngIf="getResolvedFooterLayout(s) === 'wizard' && !hideCancel"
205
217
  type="button"
206
218
  class="pw-shell__btn pw-shell__btn--cancel"
207
219
  [disabled]="s.isNavigating"
208
220
  (click)="facade.cancel()"
209
221
  >{{ cancelLabel }}</button>
222
+ <!-- Both modes: Submit on the right -->
210
223
  <button
211
224
  type="button"
212
225
  class="pw-shell__btn pw-shell__btn--next"
213
- [disabled]="s.isNavigating || !s.canMoveNext"
226
+ [disabled]="s.isNavigating"
214
227
  (click)="facade.next()"
215
- >{{ s.isLastStep ? finishLabel : nextLabel }}</button>
228
+ >{{ s.isLastStep ? completeLabel : nextLabel }}</button>
216
229
  </div>
217
230
  </div>
218
231
  </ng-template>
@@ -227,17 +240,24 @@ export class PathShellComponent implements OnInit, OnDestroy {
227
240
  /** Start the path automatically on ngOnInit. Set to false to call doStart() manually. */
228
241
  @Input() autoStart = true;
229
242
  /** Label for the Back navigation button. */
230
- @Input() backLabel = "Back";
243
+ @Input() backLabel = "Previous";
231
244
  /** Label for the Next navigation button. */
232
245
  @Input() nextLabel = "Next";
233
246
  /** Label for the Next button when on the last step. */
234
- @Input() finishLabel = "Finish";
247
+ @Input() completeLabel = "Complete";
235
248
  /** Label for the Cancel button. */
236
249
  @Input() cancelLabel = "Cancel";
237
250
  /** Hide the Cancel button entirely. */
238
251
  @Input() hideCancel = false;
239
- /** Hide the step progress indicator in the header. */
252
+ /** Hide the step progress indicator in the header. Also hidden automatically when the path has only one step. */
240
253
  @Input() hideProgress = false;
254
+ /**
255
+ * Footer layout mode:
256
+ * - "auto" (default): Uses "form" for single-step top-level paths, "wizard" otherwise.
257
+ * - "wizard": Back button on left, Cancel and Submit together on right.
258
+ * - "form": Cancel on left, Submit alone on right. Back button never shown.
259
+ */
260
+ @Input() footerLayout: "wizard" | "form" | "auto" = "auto";
241
261
 
242
262
  @Output() completed = new EventEmitter<PathData>();
243
263
  @Output() cancelled = new EventEmitter<PathData>();
@@ -287,4 +307,35 @@ export class PathShellComponent implements OnInit, OnDestroy {
287
307
  this.started = true;
288
308
  this.facade.start(this.path, this.initialData);
289
309
  }
310
+
311
+ /**
312
+ * Restart the active path from step 1 with the original `initialData`,
313
+ * without unmounting the shell. Call this via a `#shell` template reference:
314
+ *
315
+ * ```html
316
+ * <pw-shell #shell [path]="myPath" ...></pw-shell>
317
+ * <button (click)="shell.restart()">Try Again</button>
318
+ * ```
319
+ */
320
+ public restart(): Promise<void> {
321
+ return this.facade.restart(this.path, this.initialData);
322
+ }
323
+
324
+ /** Returns Object.entries(s.fieldMessages) for use in *ngFor. */
325
+ protected fieldEntries(s: PathSnapshot): [string, string][] {
326
+ return Object.entries(s.fieldMessages) as [string, string][];
327
+ }
328
+
329
+ /** Resolves "auto" footerLayout based on snapshot. Single-step top-level → "form", otherwise → "wizard". */
330
+ protected getResolvedFooterLayout(s: PathSnapshot): "wizard" | "form" {
331
+ return this.footerLayout === "auto"
332
+ ? (s.stepCount === 1 && s.nestingLevel === 0 ? "form" : "wizard")
333
+ : this.footerLayout;
334
+ }
335
+
336
+ /** Converts a camelCase or lowercase field key to a display label.
337
+ * e.g. "firstName" → "First Name", "email" → "Email" */
338
+ protected formatFieldKey(key: string): string {
339
+ return key.replace(/([A-Z])/g, " $1").replace(/^./, c => c.toUpperCase()).trim();
340
+ }
290
341
  }