ng-hub-ui-stepper 22.11.0 β†’ 22.12.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 CHANGED
@@ -8,7 +8,7 @@
8
8
  A flexible, customizable, and accessible stepper component for Angular 21+. Perfect for multi-step forms, wizards, and guided user experiences with a focus on developer experience and modern standards.
9
9
 
10
10
  > [!IMPORTANT]
11
- > Version `22.11.0` targets **Angular 22** and uses the **Signals** architecture shared across `ng-hub-ui`.
11
+ > Version `22.12.0` targets **Angular 22** and uses the **Signals** architecture shared across `ng-hub-ui`.
12
12
 
13
13
  ## Documentation and Live Examples
14
14
 
@@ -50,18 +50,20 @@ This library is part of the **ng-hub-ui** ecosystem:
50
50
  - [Installation](#installation)
51
51
  - [Usage (Quick Start)](#usage-quick-start)
52
52
  - [Examples](#examples)
53
- - [Linear Stepper](#linear-stepper)
54
- - [Custom Navigation](#custom-navigation)
55
- - [Custom Buttons](#custom-buttons)
56
- - [Step transitions](#step-transitions)
53
+ - [Linear Stepper](#linear-stepper)
54
+ - [Custom Navigation](#custom-navigation)
55
+ - [Custom Buttons](#custom-buttons)
56
+ - [Step transitions](#step-transitions)
57
+ - [Inline track rail](#inline-track-rail)
58
+ - [Saving before a jump](#saving-before-a-jump)
57
59
  - [API Reference](#api-reference)
58
- - [StepperComponent](#steppercomponent-hub-stepper)
59
- - [StepComponent](#stepcomponent-hub-step)
60
- - [Directives](#directives)
61
- - [Host classes](#host-classes)
62
- - [Services](#services)
63
- - [Providers](#providers)
64
- - [Interfaces](#interfaces)
60
+ - [StepperComponent](#steppercomponent-hub-stepper)
61
+ - [StepComponent](#stepcomponent-hub-step)
62
+ - [Directives](#directives)
63
+ - [Host classes](#host-classes)
64
+ - [Services](#services)
65
+ - [Providers](#providers)
66
+ - [Interfaces](#interfaces)
65
67
  - [Internationalization](#internationalization)
66
68
  - [Styling](#styling)
67
69
  - [Contributing](#contributing)
@@ -75,12 +77,21 @@ This library is part of the **ng-hub-ui** ecosystem:
75
77
  - πŸ”’ **Multi-layout**: Supports Vertical, Sidebar, and RTL modes.
76
78
  - πŸ”„ **Smooth Transitions**: Opt-in CSS animations, enabled with the `stepper--animated` host class (see [Step transitions](#step-transitions)). No `@angular/animations` dependency.
77
79
  - 🧩 **Flexible Controls**: Use default buttons or project your own.
80
+ - βœ… **Step validity**: tell each step whether it passes with `[valid]`; the stepper turns that into `inOrder` and decides what the user can reach.
81
+ - πŸ”’ **Two rails**: the pill rail, or the inline `nav="track"` with numbered markers, a connector and a tick on every step in order.
82
+ - ⏸️ **Interceptable jumps**: `beforeStepChange` runs before the step moves, so you can save what the user typed β€” and refuse the jump if the save fails.
78
83
  - βœ‚οΈ **Opt-in title truncation + tooltip**: set `truncateTitles` to clip long nav titles (bounded by `--hub-stepper-nav-title-max-width`) and reveal the full text on hover β€” hub-ui tooltip by default, swappable with `provideHubTooltip`. Requires `ng-hub-ui-utils >= 22.6.0` + `@use 'ng-hub-ui-utils/styles/tooltip';`.
79
84
 
80
- > ℹ️ **What the stepper does not do**: it never inspects your forms. `canNavigateTo()` answers on the
81
- > step's `disabled` input and nothing else, so an "advance only when this step is valid" rule lives in
82
- > your component β€” bind `[disabled]` on the following step to whatever your form says (see
83
- > [Linear Stepper](#linear-stepper)).
85
+ > ℹ️ **What the stepper does not do**: it never inspects your forms. It does listen, though β€” state
86
+ > the verdict with `[valid]` on the step and the stepper derives what the user can reach from it (see
87
+ > [Inline track rail](#inline-track-rail)). `canNavigateTo()` is unchanged and still answers on the
88
+ > step's `disabled` input alone.
89
+
90
+ > πŸ”€ **Three words this library keeps apart**: a step is **visited** when the user has actually stood
91
+ > on it, **valid** when your component says its data passes, **in order** when it is either stated
92
+ > valid or β€” with nothing stated β€” visited, and **reachable** when the track will let the user jump
93
+ > to it from where they are. `isCompleted` in the trigger template context is none of these: it is
94
+ > `index < currentIndex`, position and nothing else, and it is left that way.
84
95
 
85
96
  > β™Ώ **Accessibility model**: the step rail is a WAI-ARIA `tablist` (each trigger a `tab`, each step content a `tabpanel`) with a roving tabindex, so it is a single Tab stop. Arrow keys move focus between enabled steps (skipping disabled ones, wrapping), `Home`/`End` jump to the first/last enabled step, and `Enter`/`Space` activates the focused step under the same rules as clicking it. The rail's accessible name comes from the `railLabel` input (default `'Steps'`).
86
97
 
@@ -98,11 +109,11 @@ Import the standalone building blocks your template uses:
98
109
  import { StepComponent, StepperComponent } from 'ng-hub-ui-stepper';
99
110
 
100
111
  @Component({
101
- standalone: true,
102
- imports: [StepperComponent, StepComponent],
103
- // ...
112
+ standalone: true,
113
+ imports: [StepperComponent, StepComponent]
114
+ // ...
104
115
  })
105
- export class YourComponent { }
116
+ export class YourComponent {}
106
117
  ```
107
118
 
108
119
  The rest of the surface β€” `StepTriggerDirective`, `StepperNavDirective`, `PreviousButtonDirective`,
@@ -115,7 +126,7 @@ Then register the library once, so the built-in Back / Continue / Submit control
115
126
  import { provideHubStepper } from 'ng-hub-ui-stepper';
116
127
 
117
128
  bootstrapApplication(AppComponent, {
118
- providers: [provideHubStepper({ language: 'en' })]
129
+ providers: [provideHubStepper({ language: 'en' })]
119
130
  });
120
131
  ```
121
132
 
@@ -128,20 +139,20 @@ In your template:
128
139
 
129
140
  ```html
130
141
  <hub-stepper>
131
- <hub-step title="Account Setup">
132
- <h3>Welcome!</h3>
133
- <p>Setup your account details here.</p>
134
- </hub-step>
135
-
136
- <hub-step title="Personal Info">
137
- <h3>Profile Data</h3>
138
- <p>Tell us more about yourself.</p>
139
- </hub-step>
140
-
141
- <hub-step title="Review">
142
- <h3>Save & Finalize</h3>
143
- <p>Ready to go?</p>
144
- </hub-step>
142
+ <hub-step title="Account Setup">
143
+ <h3>Welcome!</h3>
144
+ <p>Setup your account details here.</p>
145
+ </hub-step>
146
+
147
+ <hub-step title="Personal Info">
148
+ <h3>Profile Data</h3>
149
+ <p>Tell us more about yourself.</p>
150
+ </hub-step>
151
+
152
+ <hub-step title="Review">
153
+ <h3>Save & Finalize</h3>
154
+ <p>Ready to go?</p>
155
+ </hub-step>
145
156
  </hub-stepper>
146
157
  ```
147
158
 
@@ -153,13 +164,13 @@ Control navigation by enabling/disabling steps programmatically.
153
164
 
154
165
  ```html
155
166
  <hub-stepper (completed)="onFinish()">
156
- <hub-step title="Step 1">
157
- <!-- Step 1 Content -->
158
- </hub-step>
167
+ <hub-step title="Step 1">
168
+ <!-- Step 1 Content -->
169
+ </hub-step>
159
170
 
160
- <hub-step title="Step 2" [disabled]="!isStep1Valid()">
161
- <!-- Step 2 Content -->
162
- </hub-step>
171
+ <hub-step title="Step 2" [disabled]="!isStep1Valid()">
172
+ <!-- Step 2 Content -->
173
+ </hub-step>
163
174
  </hub-stepper>
164
175
  ```
165
176
 
@@ -171,24 +182,25 @@ built-in rail. The context gives you `steps` β€” the projected `StepComponent` i
171
182
 
172
183
  ```html
173
184
  <hub-stepper #stepper>
174
- <ng-template hubStepperNav let-steps="steps" let-currentIndex="currentIndex">
175
- <ol class="my-custom-nav">
176
- @for (step of steps; track step; let i = $index) {
177
- <li>
178
- <button
179
- type="button"
180
- [class.active]="i === currentIndex"
181
- [disabled]="!stepper.canNavigateTo(i)"
182
- (click)="stepper.goTo(i)">
183
- {{ step.title() || 'Step ' + (i + 1) }}
184
- </button>
185
- </li>
186
- }
187
- </ol>
188
- </ng-template>
189
-
190
- <hub-step title="A">...</hub-step>
191
- <hub-step title="B">...</hub-step>
185
+ <ng-template hubStepperNav let-steps="steps" let-currentIndex="currentIndex">
186
+ <ol class="my-custom-nav">
187
+ @for (step of steps; track step; let i = $index) {
188
+ <li>
189
+ <button
190
+ type="button"
191
+ [class.active]="i === currentIndex"
192
+ [disabled]="!stepper.canNavigateTo(i)"
193
+ (click)="stepper.goTo(i)"
194
+ >
195
+ {{ step.title() || 'Step ' + (i + 1) }}
196
+ </button>
197
+ </li>
198
+ }
199
+ </ol>
200
+ </ng-template>
201
+
202
+ <hub-step title="A">...</hub-step>
203
+ <hub-step title="B">...</hub-step>
192
204
  </hub-stepper>
193
205
  ```
194
206
 
@@ -202,11 +214,11 @@ button disabled while the move is unavailable.
202
214
 
203
215
  ```html
204
216
  <hub-stepper>
205
- <hub-step>...</hub-step>
217
+ <hub-step>...</hub-step>
206
218
 
207
- <button previousButton class="btn-back">Go back</button>
208
- <button nextButton class="btn-next">Next step</button>
209
- <button submitButton class="btn-done">Complete</button>
219
+ <button previousButton class="btn-back">Go back</button>
220
+ <button nextButton class="btn-next">Next step</button>
221
+ <button submitButton class="btn-done">Complete</button>
210
222
  </hub-stepper>
211
223
  ```
212
224
 
@@ -218,12 +230,9 @@ comes from `--hub-stepper-animation-duration`. The unprefixed `stepper--animated
218
230
  `stepper--anim-slide` and `stepper--anim-fade` are still read, and go in 23.0.0.
219
231
 
220
232
  ```html
221
- <hub-stepper
222
- class="hub-stepper--animated hub-stepper--anim-fade"
223
- [style.--hub-stepper-animation-duration.ms]="240"
224
- >
225
- <hub-step title="Profile">...</hub-step>
226
- <hub-step title="Summary">...</hub-step>
233
+ <hub-stepper class="hub-stepper--animated hub-stepper--anim-fade" [style.--hub-stepper-animation-duration.ms]="240">
234
+ <hub-step title="Profile">...</hub-step>
235
+ <hub-step title="Summary">...</hub-step>
227
236
  </hub-stepper>
228
237
  ```
229
238
 
@@ -236,90 +245,138 @@ button inside each item is yours.
236
245
 
237
246
  ```html
238
247
  <hub-stepper #wizard>
239
- <ng-template hubStepTrigger let-title="title" let-index="index" let-isCurrent="isCurrent" let-disabled="disabled">
240
- <button
241
- type="button"
242
- role="tab"
243
- [attr.aria-selected]="isCurrent"
244
- [disabled]="disabled"
245
- (click)="wizard.goTo(index)"
246
- >
247
- <span class="badge">{{ index + 1 }}</span> {{ title }}
248
- </button>
249
- </ng-template>
250
-
251
- <hub-step title="Account">...</hub-step>
252
- <hub-step title="Payment">...</hub-step>
248
+ <ng-template hubStepTrigger let-title="title" let-index="index" let-isCurrent="isCurrent" let-disabled="disabled">
249
+ <button type="button" role="tab" [attr.aria-selected]="isCurrent" [disabled]="disabled" (click)="wizard.goTo(index)">
250
+ <span class="badge">{{ index + 1 }}</span> {{ title }}
251
+ </button>
252
+ </ng-template>
253
+
254
+ <hub-step title="Account">...</hub-step>
255
+ <hub-step title="Payment">...</hub-step>
253
256
  </hub-stepper>
254
257
  ```
255
258
 
256
259
  The context carries the step as `$implicit` and as `step`, plus `title`, `index`, `isCurrent`,
257
- `isCompleted` and `disabled`. Activating a step stays with you, through a template reference on the
260
+ `isCompleted`, `disabled`, `visited`, `valid`, `inOrder` and `reachable`. `isCompleted` is position
261
+ alone (`index < currentIndex`) and always has been β€” `inOrder` is the one that knows about validity. Activating a step stays with you, through a template reference on the
258
262
  host β€” which is why the example names the stepper `#wizard`. Give the trigger `role="tab"` if you
259
263
  want the rail's arrow-key navigation to keep finding it.
260
264
 
261
265
  A stepper that declares both templates uses `hubStepperNav` and ignores this one.
262
266
 
267
+ ### Inline track rail
268
+
269
+ `nav="track"` swaps the pill rail for a track: one numbered marker per step, joined by a connector,
270
+ with a tick in the corner of every step that is in order β€” the number stays visible under it. The
271
+ default is `nav="pills"`, so nothing changes until you ask.
272
+
273
+ The track also enforces a rule the pills rail does not: a jump back is always allowed, a jump
274
+ forward only once every step in between is in order.
275
+
276
+ ```html
277
+ <hub-stepper nav="track" [beforeStepChange]="saveBeforeLeaving">
278
+ <hub-step title="Account" [valid]="accountForm.valid">...</hub-step>
279
+ <hub-step title="Address" [valid]="addressForm.valid">...</hub-step>
280
+ <hub-step title="Payment" [valid]="paymentForm.valid">...</hub-step>
281
+ <hub-step title="Review">...</hub-step>
282
+ </hub-stepper>
283
+ ```
284
+
285
+ `[valid]` is tri-state on purpose. `true` and `false` are your verdict; leaving it unbound means
286
+ "nobody has looked yet", and the step then counts as in order once it has been visited β€” which is
287
+ how a stepper that never mentions validity keeps behaving exactly as it always did.
288
+
289
+ ### Saving before a jump
290
+
291
+ `beforeStepChange` runs **while the stepper is still on the step being left**, which is the only
292
+ moment at which saving is useful. Return `false`, a promise resolving to `false`, or a promise that
293
+ rejects, and the move is cancelled.
294
+
295
+ ```typescript
296
+ saveBeforeLeaving = async ({ from, to }: StepperStepChange): Promise<boolean> => {
297
+ try {
298
+ await this.drafts.save(from);
299
+ return true;
300
+ } catch {
301
+ this.toast.error(`Could not save step ${from + 1}`);
302
+ return false;
303
+ }
304
+ };
305
+ ```
306
+
307
+ An output cannot do this job: by the time one fires the step has already changed and there is
308
+ nothing left to refuse. It is the same contract `ng-hub-ui-portal` gives `beforeDismiss`.
309
+
263
310
  ## API Reference
264
311
 
265
312
  ### StepperComponent (`hub-stepper`)
266
313
 
267
314
  | Input | Type | Default | Description |
268
- |---|---|---|---|
315
+ | --- | --- | --- | --- |
269
316
  | `variant` | `string` | `undefined` (renders as primary) | Semantic accent for the active step pill and the next / submit controls. Built-in values: `primary`, `secondary`, `success`, `danger`, `warning`, `info`, `neutral`, `light`, `dark`. Any other string is also accepted and resolves through `--hub-sys-color-<variant>`. |
270
317
  | `backLabel` | `string \| null` | `null` | Overrides the back button label. While `null`, the translated `BACK` label is used. |
271
318
  | `continueLabel` | `string \| null` | `null` | Overrides the continue button label. While `null`, the translated `CONTINUE` label is used. |
272
319
  | `submitLabel` | `string \| null` | `null` | Overrides the submit button label. While `null`, the translated `SUBMIT` label is used. |
273
320
  | `truncateTitles` | `boolean` | `false` | Clips each rail title to `--hub-stepper-nav-title-max-width` (default `12rem`) and reveals the full text as a tooltip when it overflows. |
274
321
  | `railLabel` | `string` | `'Steps'` | Accessible name of the step rail tablist. |
322
+ | `nav` | `'pills' \| 'track'` | `'pills'` | Which rail to draw. `'track'` paints the inline variant and gates a forward jump on the steps in between being in order. |
323
+ | `inOrderLabel` | `string \| null` | `null` | Overrides the status a screen reader hears on a ticked track marker. While `null`, the translated `IN_ORDER` label is used. |
324
+ | `beforeStepChange` | `StepperStepChangeGuard \| null` | `null` | Consulted before the active step changes, while the stepper is still on the step being left. Returning `false`, a promise resolving to `false`, or a promise that rejects cancels the move. |
275
325
  | `options` | `StepperOptions` | `{}` | Visual and layout configuration. |
276
326
 
277
327
  | Output | Type | Description |
278
- |---|---|---|
328
+ | --- | --- | --- |
279
329
  | `completed` | `OutputEmitterRef<void>` | Emitted when the last step is completed. |
280
330
  | `previousStep` | `OutputEmitterRef<number>` | Emitted when moving back. Passes the new index. |
281
331
  | `nextStep` | `OutputEmitterRef<number>` | Emitted when moving forward. Passes the new index. |
282
332
 
283
333
  Public members you can reach through a template reference (`<hub-stepper #stepper>`):
284
334
 
285
- | Member | Signature | Description |
286
- |---|---|---|
287
- | `currentIndex` | `WritableSignal<number>` | Index of the active step. |
288
- | `steps` | `Signal<readonly StepComponent[]>` | The projected steps, in order. |
289
- | `currentStep` | `StepComponent \| null` | The active step instance. |
290
- | `goTo` | `(index: number) => void` | Activates a step. Only bounds are checked β€” it does not consult `canNavigateTo`, so a programmatic jump can land on a `disabled` step. |
291
- | `goToPrevious` / `goToNext` | `() => void` | Moves one step back / forward. |
292
- | `canNavigateTo` | `(index: number) => boolean` | `true` when the index exists and its step is not `disabled`. |
293
- | `complete` | `() => void` | Emits `completed`. |
335
+ | Member | Signature | Description |
336
+ | --------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
337
+ | `currentIndex` | `WritableSignal<number>` | Index of the active step. |
338
+ | `steps` | `Signal<readonly StepComponent[]>` | The projected steps, in order. |
339
+ | `currentStep` | `StepComponent \| null` | The active step instance. |
340
+ | `goTo` | `(index: number) => void` | Activates a step, when `canNavigateTo()` allows it and `beforeStepChange` lets it through. Since 22.12.0 it refuses a `disabled` step, as the rail always has. |
341
+ | `goToPrevious` / `goToNext` | `() => void` | Moves one step back / forward. |
342
+ | `canNavigateTo` | `(index: number) => boolean` | `true` when the index exists and its step is not `disabled`. |
343
+ | `isInOrder` | `(index: number) => boolean` | `true` when the step is stated valid, or β€” with nothing stated β€” has been visited. |
344
+ | `isReachable` | `(index: number) => boolean` | What the track allows: backwards always, forwards only once every step in between is in order. A `disabled` step is closed off either way. |
345
+ | `complete` | `() => void` | Emits `completed`. |
294
346
 
295
347
  ### StepComponent (`hub-step`)
296
348
 
297
349
  | Input | Type | Default | Description |
298
- |---|---|---|---|
350
+ | --- | --- | --- | --- |
299
351
  | `title` | `string \| undefined` | `undefined` | Text displayed in the rail. Falls back to `Step N` when omitted. |
300
352
  | `disabled` | `boolean` | `false` | Prevents navigation to this step through the rail and the built-in controls. |
353
+ | `valid` | `boolean \| null` | `null` | Your verdict on this step's data. `null` means nothing has been said, and the step then counts as in order once visited. |
354
+
355
+ Two read-only signals come with it: `visited`, set by the stepper when the step is actually reached
356
+ (a jump over it does not count, and walking back does not unset it), and `inOrder`, the stated
357
+ validity when there is one and `visited` otherwise.
301
358
 
302
359
  `index` is **not** an input: the parent stepper assigns it. Reading it (`step.index()`) is fine; binding it is not.
303
360
 
304
361
  ### Directives
305
362
 
306
- | Directive | Selectors | Applies to | Purpose |
307
- |---|---|---|---|
308
- | `NextButtonDirective` | `button[nextButton]`, `button[continueButton]` | `<button>` | Calls `goToNext()` and disables the button when there is no enabled next step. |
309
- | `PreviousButtonDirective` | `button[previousButton]`, `button[backButton]` | `<button>` | Calls `goToPrevious()` and disables the button when there is no enabled previous step. |
310
- | `SubmitButtonDirective` | `button[submitButton]` | `<button>` | Calls `complete()` and disables the button while the current step is `disabled`. |
311
- | `StepperNavDirective` | `[hubStepperNav]`, `[stepperNav]` | `<ng-template>` | Replaces the built-in rail. Context: `steps`, `currentIndex`. |
312
- | `StepTriggerDirective` | `[hubStepTrigger]`, `[stepTrigger]` | `<ng-template>` | Replaces the rail trigger the default rail draws, once per step. Context: `$implicit` / `step`, `title`, `index`, `isCurrent`, `isCompleted`, `disabled`. Ignored when a `hubStepperNav` template is present, since a custom rail draws its own triggers. |
363
+ | Directive | Selectors | Applies to | Purpose |
364
+ | ------------------------- | ---------------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
365
+ | `NextButtonDirective` | `button[nextButton]`, `button[continueButton]` | `<button>` | Calls `goToNext()` and disables the button while the next step is not reachable. |
366
+ | `PreviousButtonDirective` | `button[previousButton]`, `button[backButton]` | `<button>` | Calls `goToPrevious()` and disables the button when there is no enabled previous step. |
367
+ | `SubmitButtonDirective` | `button[submitButton]` | `<button>` | Calls `complete()` and disables the button while the current step is `disabled` or out of order. |
368
+ | `StepperNavDirective` | `[hubStepperNav]`, `[stepperNav]` | `<ng-template>` | Replaces the built-in rail. Context: `steps`, `currentIndex`. |
369
+ | `StepTriggerDirective` | `[hubStepTrigger]`, `[stepTrigger]` | `<ng-template>` | Replaces the rail trigger the default rail draws, once per step. Context: `$implicit` / `step`, `title`, `index`, `isCurrent`, `isCompleted`, `disabled`, `visited`, `valid`, `inOrder`, `reachable`. Ignored when a `hubStepperNav` template is present, and by `nav="track"`, which draws its own marker, since a custom rail draws its own triggers. |
313
370
 
314
371
  ### Host classes
315
372
 
316
373
  Set these on `<hub-stepper>` itself; they are read by the stylesheet, not by inputs.
317
374
 
318
- | Class | Effect |
319
- |---|---|
320
- | `hub-stepper--animated` | Enables the CSS transition between step panels. Without it, panels swap instantly. |
321
- | `hub-stepper--anim-slide` | Slide transition (also the default when only `hub-stepper--animated` is set). |
322
- | `hub-stepper--anim-fade` | Fade transition instead of the slide. |
375
+ | Class | Effect |
376
+ | ------------------------- | ---------------------------------------------------------------------------------- |
377
+ | `hub-stepper--animated` | Enables the CSS transition between step panels. Without it, panels swap instantly. |
378
+ | `hub-stepper--anim-slide` | Slide transition (also the default when only `hub-stepper--animated` is set). |
379
+ | `hub-stepper--anim-fade` | Fade transition instead of the slide. |
323
380
 
324
381
  > **Renamed in 22.10.0.** These were `stepper--animated`, `stepper--anim-slide` and
325
382
  > `stepper--anim-fade`, and the component itself wore a bare `stepper` class. `stepper` is a word in
@@ -338,9 +395,9 @@ prefix, which the service adds:
338
395
 
339
396
  ```typescript
340
397
  inject(StepperThemeService).setTheme({
341
- accent: '#7c3aed',
342
- 'nav-link-active-color': '#ffffff',
343
- gap: '1.5rem'
398
+ accent: '#7c3aed',
399
+ 'nav-link-active-color': '#ffffff',
400
+ gap: '1.5rem'
344
401
  });
345
402
  ```
346
403
 
@@ -358,7 +415,7 @@ that `TranslatePipe` injects to resolve the built-in control labels β€” the serv
358
415
 
359
416
  ```typescript
360
417
  bootstrapApplication(AppComponent, {
361
- providers: [provideHubStepper({ language: 'en', fallbackLanguage: 'en' })]
418
+ providers: [provideHubStepper({ language: 'en', fallbackLanguage: 'en' })]
362
419
  });
363
420
  ```
364
421
 
@@ -375,12 +432,12 @@ languages you ship or merge the labels into a dictionary of your own:
375
432
  import { STEPPER_DICTIONARIES } from 'ng-hub-ui-stepper';
376
433
 
377
434
  provideHubTranslation({
378
- language: 'ca',
379
- fallbackLanguage: 'en',
380
- dictionaries: {
381
- ca: { ...STEPPER_DICTIONARIES['ca'], ...myCatalanStrings },
382
- en: { ...STEPPER_DICTIONARIES['en'], ...myEnglishStrings }
383
- }
435
+ language: 'ca',
436
+ fallbackLanguage: 'en',
437
+ dictionaries: {
438
+ ca: { ...STEPPER_DICTIONARIES['ca'], ...myCatalanStrings },
439
+ en: { ...STEPPER_DICTIONARIES['en'], ...myEnglishStrings }
440
+ }
384
441
  });
385
442
  ```
386
443
 
@@ -390,11 +447,25 @@ The keys are flat β€” `BACK`, `CONTINUE`, `SUBMIT` β€” which is what the compone
390
447
  ### Interfaces
391
448
 
392
449
  #### `StepperOptions`
450
+
393
451
  ```typescript
394
452
  interface StepperOptions {
395
- layout?: 'vertical' | 'sidebar';
396
- rtl?: boolean;
453
+ layout?: 'vertical' | 'sidebar';
454
+ rtl?: boolean;
455
+ }
456
+ ```
457
+
458
+ #### `StepperNavVariant`, `StepperStepChange` and `StepperStepChangeGuard`
459
+
460
+ ```typescript
461
+ type StepperNavVariant = 'pills' | 'track';
462
+
463
+ interface StepperStepChange {
464
+ from: number; // the index being left β€” still the active one while the guard runs
465
+ to: number; // the index the stepper would land on
397
466
  }
467
+
468
+ type StepperStepChangeGuard = (change: StepperStepChange) => boolean | Promise<boolean>;
398
469
  ```
399
470
 
400
471
  #### `StepperConfig`
@@ -403,8 +474,8 @@ Accepted by `provideHubStepper()` and by the deprecated `StepperModule.forRoot()
403
474
 
404
475
  ```typescript
405
476
  interface StepperConfig {
406
- language?: string; // default 'es'
407
- fallbackLanguage?: string; // default 'en'
477
+ language?: string; // default 'es'
478
+ fallbackLanguage?: string; // default 'en'
408
479
  }
409
480
  ```
410
481
 
@@ -450,9 +521,9 @@ Customize the component using CSS variables. For a complete list of available to
450
521
 
451
522
  ```css
452
523
  .my-stepper {
453
- --hub-stepper-primary-color: #0d6efd;
454
- --hub-stepper-surface-color: #ffffff;
455
- --hub-stepper-gap: 1.5rem;
524
+ --hub-stepper-primary-color: #0d6efd;
525
+ --hub-stepper-surface-color: #ffffff;
526
+ --hub-stepper-gap: 1.5rem;
456
527
  }
457
528
  ```
458
529
 
@@ -462,7 +533,7 @@ The `--hub-stepper-accent` token drives the active step pill and the next / subm
462
533
 
463
534
  ```css
464
535
  .my-stepper {
465
- --hub-stepper-accent: var(--hub-sys-color-success);
536
+ --hub-stepper-accent: var(--hub-sys-color-success);
466
537
  }
467
538
  ```
468
539
 
@@ -474,12 +545,12 @@ For full theming in a single call, the package ships a `hub-stepper-theme()` Sas
474
545
  @use 'ng-hub-ui-stepper/styles' as *;
475
546
 
476
547
  .checkout-stepper {
477
- @include hub-stepper-theme(
478
- $accent: var(--hub-sys-color-success),
479
- $gap: 1.5rem,
480
- $nav-link-active-color: #fff,
481
- $sidebar-width: 220px
482
- );
548
+ @include hub-stepper-theme(
549
+ $accent: var(--hub-sys-color-success),
550
+ $gap: 1.5rem,
551
+ $nav-link-active-color: #fff,
552
+ $sidebar-width: 220px
553
+ );
483
554
  }
484
555
  ```
485
556