ng-hub-ui-stepper 22.8.1 → 22.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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
- > This version (21.2.1) is built for **Angular 21** and uses the new **Signals** architecture.
11
+ > Version `22.9.0` targets **Angular 21** and uses the **Signals** architecture shared across `ng-hub-ui`.
12
12
 
13
13
  ## Documentation and Live Examples
14
14
 
@@ -52,11 +52,16 @@ This library is part of the **ng-hub-ui** ecosystem:
52
52
  - [Linear Stepper](#linear-stepper)
53
53
  - [Custom Navigation](#custom-navigation)
54
54
  - [Custom Buttons](#custom-buttons)
55
+ - [Step transitions](#step-transitions)
55
56
  - [API Reference](#api-reference)
56
- - [StepperComponent](#steppercomponent)
57
- - [StepComponent](#stepcomponent)
57
+ - [StepperComponent](#steppercomponent-hub-stepper)
58
+ - [StepComponent](#stepcomponent-hub-step)
58
59
  - [Directives](#directives)
60
+ - [Host classes](#host-classes)
61
+ - [Services](#services)
62
+ - [Providers](#providers)
59
63
  - [Interfaces](#interfaces)
64
+ - [Internationalization](#internationalization)
60
65
  - [Styling](#styling)
61
66
  - [Contributing](#contributing)
62
67
  - [License](#license)
@@ -64,13 +69,18 @@ This library is part of the **ng-hub-ui** ecosystem:
64
69
  ## Features
65
70
 
66
71
  - 🚀 **Angular 21+ Built-in**: Uses Signals and new control flow syntax.
67
- - 🎨 **Highly Customizable**: Easy to theme via CSS variables and custom templates.
72
+ - 🎨 **Highly Customizable**: Easy to theme via CSS variables, the `hub-stepper-theme()` Sass mixin and custom templates.
68
73
  - â™ŋ **Accessible**: WAI-ARIA tablist rail with full keyboard navigation.
69
74
  - đŸ”ĸ **Multi-layout**: Supports Vertical, Sidebar, and RTL modes.
70
- - 🔄 **Smooth Transitions**: Built-in CSS animations.
75
+ - 🔄 **Smooth Transitions**: Opt-in CSS animations, enabled with the `stepper--animated` host class (see [Step transitions](#step-transitions)). No `@angular/animations` dependency.
71
76
  - 🧩 **Flexible Controls**: Use default buttons or project your own.
72
77
  - âœ‚ī¸ **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';`.
73
78
 
79
+ > â„šī¸ **What the stepper does not do**: it never inspects your forms. `canNavigateTo()` answers on the
80
+ > step's `disabled` input and nothing else, so an "advance only when this step is valid" rule lives in
81
+ > your component — bind `[disabled]` on the following step to whatever your form says (see
82
+ > [Linear Stepper](#linear-stepper)).
83
+
74
84
  > â™ŋ **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'`).
75
85
 
76
86
  ## Installation
@@ -81,19 +91,38 @@ npm install ng-hub-ui-stepper
81
91
 
82
92
  ## Usage (Quick Start)
83
93
 
84
- Import the `StepperModule` in your module/component:
94
+ Import the standalone building blocks your template uses:
85
95
 
86
96
  ```typescript
87
- import { StepperModule } from 'ng-hub-ui-stepper';
97
+ import { StepComponent, StepperComponent } from 'ng-hub-ui-stepper';
88
98
 
89
99
  @Component({
90
100
  standalone: true,
91
- imports: [StepperModule],
101
+ imports: [StepperComponent, StepComponent],
92
102
  // ...
93
103
  })
94
104
  export class YourComponent { }
95
105
  ```
96
106
 
107
+ The rest of the surface — `StepTriggerDirective`, `StepperNavDirective`, `PreviousButtonDirective`,
108
+ `NextButtonDirective` and `SubmitButtonDirective` — is imported the same way, one by one, as each
109
+ template needs it.
110
+
111
+ Then register the library once, so the built-in Back / Continue / Submit controls have text:
112
+
113
+ ```typescript
114
+ import { provideHubStepper } from 'ng-hub-ui-stepper';
115
+
116
+ bootstrapApplication(AppComponent, {
117
+ providers: [provideHubStepper({ language: 'en' })]
118
+ });
119
+ ```
120
+
121
+ > **`StepperModule` is deprecated and will be removed in 23.0.0.** It only re-exports the seven
122
+ > building blocks above, so importing them directly is the whole migration, and
123
+ > `StepperModule.forRoot()` becomes `provideHubStepper()` — same providers, no module. See
124
+ > [Providers](#providers) and [Internationalization](#internationalization).
125
+
97
126
  In your template:
98
127
 
99
128
  ```html
@@ -135,28 +164,40 @@ Control navigation by enabling/disabling steps programmatically.
135
164
 
136
165
  ### Custom Navigation
137
166
 
138
- Provide your own navigation template using the `stepperNavTpt` property.
167
+ Mark an `ng-template` with the `hubStepperNav` (or `stepperNav`) directive and it replaces the whole
168
+ built-in rail. The context gives you `steps` — the projected `StepComponent` instances, so `title` and
169
+ `disabled` are signals — and `currentIndex`. A template reference on the stepper gets you `goTo()`.
139
170
 
140
171
  ```html
141
- <hub-stepper>
142
- <nav *stepperNav="let steps = steps; let currentIndex = currentIndex" class="my-custom-nav">
143
- @for (step of steps; track step; let i = $index) {
144
- <button
145
- [class.active]="i === currentIndex"
146
- (click)="goTo(i)">
147
- {{ step.title() }}
148
- </button>
149
- }
150
- </nav>
172
+ <hub-stepper #stepper>
173
+ <ng-template hubStepperNav let-steps="steps" let-currentIndex="currentIndex">
174
+ <ol class="my-custom-nav">
175
+ @for (step of steps; track step; let i = $index) {
176
+ <li>
177
+ <button
178
+ type="button"
179
+ [class.active]="i === currentIndex"
180
+ [disabled]="!stepper.canNavigateTo(i)"
181
+ (click)="stepper.goTo(i)">
182
+ {{ step.title() || 'Step ' + (i + 1) }}
183
+ </button>
184
+ </li>
185
+ }
186
+ </ol>
187
+ </ng-template>
151
188
 
152
189
  <hub-step title="A">...</hub-step>
153
190
  <hub-step title="B">...</hub-step>
154
191
  </hub-stepper>
155
192
  ```
156
193
 
194
+ > Replacing the rail also replaces its accessibility: the WAI-ARIA tablist, the roving tabindex and the
195
+ > arrow-key handling described above belong to the built-in rail. A custom template owns its own semantics.
196
+
157
197
  ### Custom Buttons
158
198
 
159
- Project your own buttons to override the default footer.
199
+ Project your own buttons to override the default footer. Each directive wires the click and keeps the
200
+ button disabled while the move is unavailable.
160
201
 
161
202
  ```html
162
203
  <hub-stepper>
@@ -168,38 +209,141 @@ Project your own buttons to override the default footer.
168
209
  </hub-stepper>
169
210
  ```
170
211
 
212
+ ### Step transitions
213
+
214
+ Transitions are plain CSS and opt-in: add `stepper--animated` to the host, then pick the flavour with
215
+ `stepper--anim-slide` (the default when neither is set) or `stepper--anim-fade`. Duration comes from
216
+ `--hub-stepper-animation-duration`.
217
+
218
+ ```html
219
+ <hub-stepper
220
+ class="stepper--animated stepper--anim-fade"
221
+ [style.--hub-stepper-animation-duration.ms]="240"
222
+ >
223
+ <hub-step title="Profile">...</hub-step>
224
+ <hub-step title="Summary">...</hub-step>
225
+ </hub-stepper>
226
+ ```
227
+
171
228
  ## API Reference
172
229
 
173
230
  ### StepperComponent (`hub-stepper`)
174
231
 
175
232
  | Input | Type | Default | Description |
176
233
  |---|---|---|---|
177
- | `backLabel` | `string` | `'Back'` | Label for the back button. |
178
- | `continueLabel` | `string` | `'Continue'` | Label for the continue button. |
179
- | `submitLabel` | `string` | `'Submit'` | Label for the submit button. |
234
+ | `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>`. |
235
+ | `backLabel` | `string \| null` | `null` | Overrides the back button label. While `null`, the translated `BACK` label is used. |
236
+ | `continueLabel` | `string \| null` | `null` | Overrides the continue button label. While `null`, the translated `CONTINUE` label is used. |
237
+ | `submitLabel` | `string \| null` | `null` | Overrides the submit button label. While `null`, the translated `SUBMIT` label is used. |
238
+ | `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. |
180
239
  | `railLabel` | `string` | `'Steps'` | Accessible name of the step rail tablist. |
181
- | `variant` | `string` | `'primary'` | Semantic accent for the active step pill and the next / submit controls. Built-in values: `primary`, `success`, `danger`, `warning`, `info`. Any other string is also accepted and resolves through `--hub-sys-color-<variant>`. |
182
240
  | `options` | `StepperOptions` | `{}` | Visual and layout configuration. |
183
241
 
184
242
  | Output | Type | Description |
185
243
  |---|---|---|
186
- | `completed` | `EventEmitter<void>` | Emitted when the last step is completed. |
187
- | `previousStep` | `EventEmitter<number>` | Emitted when moving back. Passes the new index. |
188
- | `nextStep` | `EventEmitter<number>` | Emitted when moving forward. Passes the new index. |
244
+ | `completed` | `OutputEmitterRef<void>` | Emitted when the last step is completed. |
245
+ | `previousStep` | `OutputEmitterRef<number>` | Emitted when moving back. Passes the new index. |
246
+ | `nextStep` | `OutputEmitterRef<number>` | Emitted when moving forward. Passes the new index. |
247
+
248
+ Public members you can reach through a template reference (`<hub-stepper #stepper>`):
249
+
250
+ | Member | Signature | Description |
251
+ |---|---|---|
252
+ | `currentIndex` | `WritableSignal<number>` | Index of the active step. |
253
+ | `steps` | `Signal<readonly StepComponent[]>` | The projected steps, in order. |
254
+ | `currentStep` | `StepComponent \| null` | The active step instance. |
255
+ | `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. |
256
+ | `goToPrevious` / `goToNext` | `() => void` | Moves one step back / forward. |
257
+ | `canNavigateTo` | `(index: number) => boolean` | `true` when the index exists and its step is not `disabled`. |
258
+ | `complete` | `() => void` | Emits `completed`. |
189
259
 
190
260
  ### StepComponent (`hub-step`)
191
261
 
192
262
  | Input | Type | Default | Description |
193
263
  |---|---|---|---|
194
- | `title` | `string` | `optional` | Text displayed in navigation. |
195
- | `disabled` | `boolean` | `false` | Prevents navigation to this step. |
264
+ | `title` | `string \| undefined` | `undefined` | Text displayed in the rail. Falls back to `Step N` when omitted. |
265
+ | `disabled` | `boolean` | `false` | Prevents navigation to this step through the rail and the built-in controls. |
266
+
267
+ `index` is **not** an input: the parent stepper assigns it. Reading it (`step.index()`) is fine; binding it is not.
196
268
 
197
269
  ### Directives
198
270
 
199
- - `nextButton`: Apply to any button to use it as the "next" control.
200
- - `previousButton`: Apply to any button to use it as the "back" control.
201
- - `submitButton`: Apply to any button to use it as the "submit" control.
202
- - `stepperNav`: Mark a template to be used as custom navigation.
271
+ | Directive | Selectors | Applies to | Purpose |
272
+ |---|---|---|---|
273
+ | `NextButtonDirective` | `button[nextButton]`, `button[continueButton]` | `<button>` | Calls `goToNext()` and disables the button when there is no enabled next step. |
274
+ | `PreviousButtonDirective` | `button[previousButton]`, `button[backButton]` | `<button>` | Calls `goToPrevious()` and disables the button when there is no enabled previous step. |
275
+ | `SubmitButtonDirective` | `button[submitButton]` | `<button>` | Calls `complete()` and disables the button while the current step is `disabled`. |
276
+ | `StepperNavDirective` | `[hubStepperNav]`, `[stepperNav]` | `<ng-template>` | Replaces the built-in rail. Context: `steps`, `currentIndex`. |
277
+ | `StepTriggerDirective` | `[hubStepTrigger]`, `[stepTrigger]` | `<ng-template>` | Captures a per-step trigger template. **Exported but not yet rendered** — the stepper draws its own triggers; this is groundwork, and applying it changes nothing today. |
278
+
279
+ ### Host classes
280
+
281
+ Set these on `<hub-stepper>` itself; they are read by the stylesheet, not by inputs.
282
+
283
+ | Class | Effect |
284
+ |---|---|
285
+ | `stepper--animated` | Enables the CSS transition between step panels. Without it, panels swap instantly. |
286
+ | `stepper--anim-slide` | Slide transition (also the default when only `stepper--animated` is set). |
287
+ | `stepper--anim-fade` | Fade transition instead of the slide. |
288
+
289
+ ### Services
290
+
291
+ #### `StepperThemeService`
292
+
293
+ Provided in root. Writes `--hub-stepper-*` custom properties on `documentElement`, for themes decided at
294
+ runtime (a tenant colour arriving from an API, say). Keys are passed **without** the `--hub-stepper-`
295
+ prefix, which the service adds:
296
+
297
+ ```typescript
298
+ inject(StepperThemeService).setTheme({
299
+ accent: '#7c3aed',
300
+ 'nav-link-active-color': '#ffffff',
301
+ gap: '1.5rem'
302
+ });
303
+ ```
304
+
305
+ For a theme known at build time, prefer the [`hub-stepper-theme()` mixin](#sass-mixin): it scopes to a
306
+ selector instead of the document root.
307
+
308
+ ### Providers
309
+
310
+ #### `provideHubStepper(config?: StepperConfig)`
311
+
312
+ The standalone entry point. Registers the ten bundled dictionaries and the `HubTranslationService`
313
+ that `TranslatePipe` injects to resolve the built-in control labels — the service is not
314
+ `providedIn: 'root'`, so without this (or another provider of it) the first render throws
315
+ `NullInjectorError`.
316
+
317
+ ```typescript
318
+ bootstrapApplication(AppComponent, {
319
+ providers: [provideHubStepper({ language: 'en', fallbackLanguage: 'en' })]
320
+ });
321
+ ```
322
+
323
+ It can also be scoped to the route that owns the wizard, which is what an application already
324
+ configuring `provideHubTranslation()` at the root should do — see
325
+ [Internationalization](#internationalization) for why.
326
+
327
+ #### `STEPPER_DICTIONARIES`
328
+
329
+ The bundled dictionaries as a plain record, keyed by language code, so you can register only the
330
+ languages you ship or merge the labels into a dictionary of your own:
331
+
332
+ ```typescript
333
+ import { STEPPER_DICTIONARIES } from 'ng-hub-ui-stepper';
334
+
335
+ provideHubTranslation({
336
+ language: 'ca',
337
+ fallbackLanguage: 'en',
338
+ dictionaries: {
339
+ ca: { ...STEPPER_DICTIONARIES['ca'], ...myCatalanStrings },
340
+ en: { ...STEPPER_DICTIONARIES['en'], ...myEnglishStrings }
341
+ }
342
+ });
343
+ ```
344
+
345
+ The keys are flat — `BACK`, `CONTINUE`, `SUBMIT` — which is what the component resolves once its
346
+ `HUBUI.STEPPER` namespace misses.
203
347
 
204
348
  ### Interfaces
205
349
 
@@ -211,15 +355,53 @@ interface StepperOptions {
211
355
  }
212
356
  ```
213
357
 
358
+ #### `StepperConfig`
359
+
360
+ Accepted by `provideHubStepper()` and by the deprecated `StepperModule.forRoot()`:
361
+
362
+ ```typescript
363
+ interface StepperConfig {
364
+ language?: string; // default 'es'
365
+ fallbackLanguage?: string; // default 'en'
366
+ }
367
+ ```
368
+
214
369
  ## Internationalization
215
370
 
216
- The built-in back, continue and submit labels use `TranslatePipe` from `ng-hub-ui-utils`. Configure `provideHubTranslationAdapter()` once in `app.config.ts`; its reactive dictionary updates the rendered navigation automatically.
371
+ The built-in back, continue and submit labels go through `TranslatePipe` from `ng-hub-ui-utils`. There are
372
+ two ways to feed them, and they can be combined.
373
+
374
+ **The bundled dictionaries.** `provideHubStepper()` registers translations for `en`, `es`, `ca`, `eu`,
375
+ `gl`, `ast`, `an`, `de`, `zh` and `ar`, and the `HubTranslationService` that `TranslatePipe` injects:
217
376
 
218
377
  ```typescript
219
- // The adapter source supplies the active dictionary to HubTranslationService.
220
- // Expected keys: BACK, CONTINUE and SUBMIT.
378
+ providers: [provideHubStepper({ language: 'en', fallbackLanguage: 'en' })];
221
379
  ```
222
380
 
381
+ `StepperModule.forRoot({ language: 'en', fallbackLanguage: 'en' })` does the same and is **removed in
382
+ 23.0.0** along with the module; it now delegates to `provideHubStepper()`, so the swap changes nothing
383
+ at runtime.
384
+
385
+ One caveat: `provideHubStepper()` writes `HUB_TRANSLATION_CONFIG`, and that is a single
386
+ application-wide token. If your application already calls `provideHubTranslation()` at the root, do
387
+ not register both — whichever comes last wins and the other loses its dictionaries. Merge the stepper
388
+ labels into your own call with [`STEPPER_DICTIONARIES`](#stepper_dictionaries), or scope
389
+ `provideHubStepper()` to the route that owns the wizard.
390
+
391
+ **Your application dictionary.** Configure `provideHubTranslationAdapter()` once in `app.config.ts`; its
392
+ reactive dictionary updates the rendered navigation automatically. The component provides
393
+ `HUB_TRANSLATION_PREFIX` as `HUBUI.STEPPER`, so the namespaced keys are looked up first and the bare keys
394
+ remain as the fallback:
395
+
396
+ ```typescript
397
+ // Preferred — namespaced, so the stepper reserves no generic top-level keys:
398
+ // HUBUI.STEPPER.BACK, HUBUI.STEPPER.CONTINUE, HUBUI.STEPPER.SUBMIT
399
+ // Still honoured for existing flat dictionaries:
400
+ // BACK, CONTINUE, SUBMIT
401
+ ```
402
+
403
+ Per instance, `backLabel` / `continueLabel` / `submitLabel` win over both.
404
+
223
405
  ## Styling
224
406
 
225
407
  Customize the component using CSS variables. For a complete list of available tokens, see the [CSS Variables Reference](docs/css-variables-reference.md).
@@ -234,7 +416,7 @@ Customize the component using CSS variables. For a complete list of available to
234
416
 
235
417
  ### Semantic accent
236
418
 
237
- The `--hub-stepper-accent` token drives the active step pill and the next / submit controls. It defaults to `var(--hub-sys-color-primary)`. The easiest way to set it is the `variant` input (see [API Reference](#steppercomponent)), but you can also override the token directly:
419
+ The `--hub-stepper-accent` token drives the active step pill and the next / submit controls. It defaults to `var(--hub-sys-color-primary)`. The easiest way to set it is the `variant` input (see [API Reference](#steppercomponent-hub-stepper)), but you can also override the token directly:
238
420
 
239
421
  ```css
240
422
  .my-stepper {
@@ -247,7 +429,7 @@ The `--hub-stepper-accent` token drives the active step pill and the next / subm
247
429
  For full theming in a single call, the package ships a `hub-stepper-theme()` Sass mixin. Every parameter is optional and defaults to `null`, so only the ones you pass are emitted as `--hub-stepper-*` overrides:
248
430
 
249
431
  ```scss
250
- @use 'ng-hub-ui-stepper/styles/mixins/stepper-theme' as *;
432
+ @use 'ng-hub-ui-stepper/styles' as *;
251
433
 
252
434
  .checkout-stepper {
253
435
  @include hub-stepper-theme(