forty-cdk 0.2.0 → 0.3.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/accordion/README.md +122 -0
- package/aspect-ratio/README.md +76 -0
- package/avatar/README.md +100 -0
- package/breadcrumbs/README.md +49 -0
- package/breakpoints/README.md +81 -0
- package/button/README.md +49 -0
- package/calendar/README.md +458 -0
- package/carousel/README.md +358 -0
- package/checkbox/README.md +146 -0
- package/combobox/README.md +535 -0
- package/context-menu/README.md +139 -0
- package/date-field/README.md +184 -0
- package/date-picker/README.md +338 -0
- package/dialog/README.md +388 -0
- package/disclosure/README.md +114 -0
- package/drag-drop/README.md +359 -0
- package/drawer/README.md +560 -0
- package/dropdown-menu/README.md +176 -0
- package/fesm2022/forty-cdk-accordion.mjs +348 -0
- package/fesm2022/forty-cdk-accordion.mjs.map +1 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs +74 -0
- package/fesm2022/forty-cdk-aspect-ratio.mjs.map +1 -0
- package/fesm2022/forty-cdk-avatar.mjs +308 -0
- package/fesm2022/forty-cdk-avatar.mjs.map +1 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs +125 -0
- package/fesm2022/forty-cdk-breadcrumbs.mjs.map +1 -0
- package/fesm2022/forty-cdk-breakpoints.mjs +117 -0
- package/fesm2022/forty-cdk-breakpoints.mjs.map +1 -0
- package/fesm2022/forty-cdk-button.mjs +134 -0
- package/fesm2022/forty-cdk-button.mjs.map +1 -0
- package/fesm2022/forty-cdk-calendar.mjs +2034 -0
- package/fesm2022/forty-cdk-calendar.mjs.map +1 -0
- package/fesm2022/forty-cdk-carousel.mjs +968 -0
- package/fesm2022/forty-cdk-carousel.mjs.map +1 -0
- package/fesm2022/forty-cdk-checkbox.mjs +226 -0
- package/fesm2022/forty-cdk-checkbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-combobox.mjs +2596 -0
- package/fesm2022/forty-cdk-combobox.mjs.map +1 -0
- package/fesm2022/forty-cdk-context-menu.mjs +413 -0
- package/fesm2022/forty-cdk-context-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-core.mjs +9022 -0
- package/fesm2022/forty-cdk-core.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-field.mjs +744 -0
- package/fesm2022/forty-cdk-date-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-date-picker.mjs +1011 -0
- package/fesm2022/forty-cdk-date-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-dialog.mjs +707 -0
- package/fesm2022/forty-cdk-dialog.mjs.map +1 -0
- package/fesm2022/forty-cdk-disclosure.mjs +190 -0
- package/fesm2022/forty-cdk-disclosure.mjs.map +1 -0
- package/fesm2022/forty-cdk-drag-drop.mjs +1180 -0
- package/fesm2022/forty-cdk-drag-drop.mjs.map +1 -0
- package/fesm2022/forty-cdk-drawer.mjs +1641 -0
- package/fesm2022/forty-cdk-drawer.mjs.map +1 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs +350 -0
- package/fesm2022/forty-cdk-dropdown-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-field.mjs +425 -0
- package/fesm2022/forty-cdk-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-fieldset.mjs +164 -0
- package/fesm2022/forty-cdk-fieldset.mjs.map +1 -0
- package/fesm2022/forty-cdk-file-upload.mjs +221 -0
- package/fesm2022/forty-cdk-file-upload.mjs.map +1 -0
- package/fesm2022/forty-cdk-hover-card.mjs +496 -0
- package/fesm2022/forty-cdk-hover-card.mjs.map +1 -0
- package/fesm2022/forty-cdk-input.mjs +274 -0
- package/fesm2022/forty-cdk-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-internationalized-date.mjs +1 -1
- package/fesm2022/forty-cdk-internationalized-date.mjs.map +1 -1
- package/fesm2022/forty-cdk-listbox.mjs +1279 -0
- package/fesm2022/forty-cdk-listbox.mjs.map +1 -0
- package/fesm2022/forty-cdk-menu.mjs +1439 -0
- package/fesm2022/forty-cdk-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-menubar.mjs +787 -0
- package/fesm2022/forty-cdk-menubar.mjs.map +1 -0
- package/fesm2022/forty-cdk-meter.mjs +211 -0
- package/fesm2022/forty-cdk-meter.mjs.map +1 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs +1145 -0
- package/fesm2022/forty-cdk-navigation-menu.mjs.map +1 -0
- package/fesm2022/forty-cdk-number-input.mjs +559 -0
- package/fesm2022/forty-cdk-number-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-otp-input.mjs +527 -0
- package/fesm2022/forty-cdk-otp-input.mjs.map +1 -0
- package/fesm2022/forty-cdk-pagination.mjs +323 -0
- package/fesm2022/forty-cdk-pagination.mjs.map +1 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs +297 -0
- package/fesm2022/forty-cdk-pane-resizer.mjs.map +1 -0
- package/fesm2022/forty-cdk-popover.mjs +698 -0
- package/fesm2022/forty-cdk-popover.mjs.map +1 -0
- package/fesm2022/forty-cdk-progress.mjs +226 -0
- package/fesm2022/forty-cdk-progress.mjs.map +1 -0
- package/fesm2022/forty-cdk-radio-group.mjs +378 -0
- package/fesm2022/forty-cdk-radio-group.mjs.map +1 -0
- package/fesm2022/forty-cdk-scroll-area.mjs +640 -0
- package/fesm2022/forty-cdk-scroll-area.mjs.map +1 -0
- package/fesm2022/forty-cdk-search.mjs +205 -0
- package/fesm2022/forty-cdk-search.mjs.map +1 -0
- package/fesm2022/forty-cdk-select.mjs +1661 -0
- package/fesm2022/forty-cdk-select.mjs.map +1 -0
- package/fesm2022/forty-cdk-separator.mjs +82 -0
- package/fesm2022/forty-cdk-separator.mjs.map +1 -0
- package/fesm2022/forty-cdk-signal-forms.mjs +97 -0
- package/fesm2022/forty-cdk-signal-forms.mjs.map +1 -0
- package/fesm2022/forty-cdk-slider.mjs +803 -0
- package/fesm2022/forty-cdk-slider.mjs.map +1 -0
- package/fesm2022/forty-cdk-stepper.mjs +886 -0
- package/fesm2022/forty-cdk-stepper.mjs.map +1 -0
- package/fesm2022/forty-cdk-switch.mjs +137 -0
- package/fesm2022/forty-cdk-switch.mjs.map +1 -0
- package/fesm2022/forty-cdk-table.mjs +1518 -0
- package/fesm2022/forty-cdk-table.mjs.map +1 -0
- package/fesm2022/forty-cdk-tabs.mjs +400 -0
- package/fesm2022/forty-cdk-tabs.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-field.mjs +593 -0
- package/fesm2022/forty-cdk-time-field.mjs.map +1 -0
- package/fesm2022/forty-cdk-time-picker.mjs +1013 -0
- package/fesm2022/forty-cdk-time-picker.mjs.map +1 -0
- package/fesm2022/forty-cdk-toast.mjs +1153 -0
- package/fesm2022/forty-cdk-toast.mjs.map +1 -0
- package/fesm2022/forty-cdk-toggle.mjs +516 -0
- package/fesm2022/forty-cdk-toggle.mjs.map +1 -0
- package/fesm2022/forty-cdk-toolbar.mjs +374 -0
- package/fesm2022/forty-cdk-toolbar.mjs.map +1 -0
- package/fesm2022/forty-cdk-tooltip.mjs +672 -0
- package/fesm2022/forty-cdk-tooltip.mjs.map +1 -0
- package/fesm2022/forty-cdk-tree.mjs +2007 -0
- package/fesm2022/forty-cdk-tree.mjs.map +1 -0
- package/fesm2022/forty-cdk-virtualization.mjs +1 -1
- package/fesm2022/forty-cdk-virtualization.mjs.map +1 -1
- package/fesm2022/forty-cdk.mjs +0 -43310
- package/fesm2022/forty-cdk.mjs.map +1 -1
- package/field/README.md +97 -0
- package/fieldset/README.md +86 -0
- package/file-upload/README.md +73 -0
- package/hover-card/README.md +171 -0
- package/input/README.md +156 -0
- package/listbox/README.md +424 -0
- package/menu/README.md +181 -0
- package/menubar/README.md +140 -0
- package/meter/README.md +128 -0
- package/navigation-menu/README.md +253 -0
- package/number-input/README.md +171 -0
- package/otp-input/README.md +198 -0
- package/package.json +213 -1
- package/pagination/README.md +61 -0
- package/pane-resizer/README.md +136 -0
- package/popover/README.md +262 -0
- package/progress/README.md +115 -0
- package/radio-group/README.md +129 -0
- package/scroll-area/README.md +184 -0
- package/search/README.md +42 -0
- package/select/README.md +488 -0
- package/separator/README.md +84 -0
- package/signal-forms/README.md +72 -0
- package/slider/README.md +152 -0
- package/stepper/README.md +292 -0
- package/switch/README.md +116 -0
- package/table/README.md +769 -0
- package/tabs/README.md +130 -0
- package/time-field/README.md +157 -0
- package/time-picker/README.md +172 -0
- package/toast/README.md +398 -0
- package/toggle/README.md +224 -0
- package/toolbar/README.md +109 -0
- package/tooltip/README.md +274 -0
- package/tree/README.md +708 -0
- package/types/forty-cdk-accordion.d.ts +242 -0
- package/types/forty-cdk-aspect-ratio.d.ts +59 -0
- package/types/forty-cdk-avatar.d.ts +133 -0
- package/types/forty-cdk-breadcrumbs.d.ts +92 -0
- package/types/forty-cdk-breakpoints.d.ts +141 -0
- package/types/forty-cdk-button.d.ts +80 -0
- package/types/forty-cdk-calendar.d.ts +914 -0
- package/types/forty-cdk-carousel.d.ts +530 -0
- package/types/forty-cdk-checkbox.d.ts +141 -0
- package/types/forty-cdk-combobox.d.ts +1259 -0
- package/types/forty-cdk-context-menu.d.ts +313 -0
- package/types/forty-cdk-core.d.ts +5774 -0
- package/types/forty-cdk-date-field.d.ts +307 -0
- package/types/forty-cdk-date-picker.d.ts +622 -0
- package/types/forty-cdk-dialog.d.ts +546 -0
- package/types/forty-cdk-disclosure.d.ts +127 -0
- package/types/forty-cdk-drag-drop.d.ts +456 -0
- package/types/forty-cdk-drawer.d.ts +871 -0
- package/types/forty-cdk-dropdown-menu.d.ts +242 -0
- package/types/forty-cdk-field.d.ts +236 -0
- package/types/forty-cdk-fieldset.d.ts +119 -0
- package/types/forty-cdk-file-upload.d.ts +124 -0
- package/types/forty-cdk-hover-card.d.ts +320 -0
- package/types/forty-cdk-input.d.ts +169 -0
- package/types/forty-cdk-internationalized-date.d.ts +1 -1
- package/types/forty-cdk-listbox.d.ts +513 -0
- package/types/forty-cdk-menu.d.ts +629 -0
- package/types/forty-cdk-menubar.d.ts +451 -0
- package/types/forty-cdk-meter.d.ts +122 -0
- package/types/forty-cdk-navigation-menu.d.ts +514 -0
- package/types/forty-cdk-number-input.d.ts +319 -0
- package/types/forty-cdk-otp-input.d.ts +248 -0
- package/types/forty-cdk-pagination.d.ts +214 -0
- package/types/forty-cdk-pane-resizer.d.ts +145 -0
- package/types/forty-cdk-popover.d.ts +509 -0
- package/types/forty-cdk-progress.d.ts +143 -0
- package/types/forty-cdk-radio-group.d.ts +222 -0
- package/types/forty-cdk-scroll-area.d.ts +258 -0
- package/types/forty-cdk-search.d.ts +142 -0
- package/types/forty-cdk-select.d.ts +899 -0
- package/types/forty-cdk-separator.d.ts +59 -0
- package/types/forty-cdk-signal-forms.d.ts +58 -0
- package/types/forty-cdk-slider.d.ts +379 -0
- package/types/forty-cdk-stepper.d.ts +650 -0
- package/types/forty-cdk-switch.d.ts +87 -0
- package/types/forty-cdk-table.d.ts +723 -0
- package/types/forty-cdk-tabs.d.ts +235 -0
- package/types/forty-cdk-time-field.d.ts +307 -0
- package/types/forty-cdk-time-picker.d.ts +578 -0
- package/types/forty-cdk-toast.d.ts +598 -0
- package/types/forty-cdk-toggle.d.ts +310 -0
- package/types/forty-cdk-toolbar.d.ts +217 -0
- package/types/forty-cdk-tooltip.d.ts +436 -0
- package/types/forty-cdk-tree.d.ts +688 -0
- package/types/forty-cdk.d.ts +1 -19743
|
@@ -0,0 +1,886 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, inject, model, output, input, booleanAttribute, computed, effect, Directive, ElementRef } from '@angular/core';
|
|
3
|
+
import { createDefaults, injectTextDirection, RovingTabindex, Collection, firstEnabledHost, reconcileRovingActive, moveIndex, registerHandle, hostId, resolveListNavigation, injectHasFocusableContent } from 'forty-cdk/core';
|
|
4
|
+
|
|
5
|
+
/** Injection token for the root stepper context (`ForStepper`). */
|
|
6
|
+
const FOR_STEPPER_CONTEXT = new InjectionToken('FOR_STEPPER_CONTEXT');
|
|
7
|
+
/** Injection token for the per-step item context (`ForStepperItem`). */
|
|
8
|
+
const FOR_STEPPER_ITEM_CONTEXT = new InjectionToken('FOR_STEPPER_ITEM_CONTEXT');
|
|
9
|
+
function injectStepperContext(piece) {
|
|
10
|
+
const ctx = inject(FOR_STEPPER_CONTEXT, { optional: true });
|
|
11
|
+
if (!ctx) {
|
|
12
|
+
throw new Error(`[forty-cdk/stepper] ${piece} must be used inside a [forStepper] element.`);
|
|
13
|
+
}
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
function injectStepperItemContext(piece) {
|
|
17
|
+
const ctx = inject(FOR_STEPPER_ITEM_CONTEXT, { optional: true });
|
|
18
|
+
if (!ctx) {
|
|
19
|
+
throw new Error(`[forty-cdk/stepper] ${piece} must be used inside a [forStepperItem] element.`);
|
|
20
|
+
}
|
|
21
|
+
return ctx;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Library fallback for stepper defaults, read at the root injector when no
|
|
26
|
+
* consumer has called `provideForStepperDefaults`. Exported for the shared
|
|
27
|
+
* defaults contract spec; not re-exported from the primitive's public entry.
|
|
28
|
+
*/
|
|
29
|
+
const FOR_STEPPER_FALLBACK_DEFAULTS = {
|
|
30
|
+
activationMode: 'manual',
|
|
31
|
+
loop: true,
|
|
32
|
+
};
|
|
33
|
+
const { token, provideDefaults } = createDefaults('FOR_STEPPER_DEFAULTS', FOR_STEPPER_FALLBACK_DEFAULTS);
|
|
34
|
+
/** Token holding the resolved stepper defaults for the current scope. */
|
|
35
|
+
const FOR_STEPPER_DEFAULTS = token;
|
|
36
|
+
/**
|
|
37
|
+
* Configures forty-cdk stepper defaults for this injector scope. Partial
|
|
38
|
+
* overrides inherit unspecified keys from the parent scope (or library defaults
|
|
39
|
+
* at the root).
|
|
40
|
+
*/
|
|
41
|
+
function provideForStepperDefaults(defaults = {}) {
|
|
42
|
+
return provideDefaults(defaults);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Root of the Stepper primitive. Owns the selected step index, linear
|
|
47
|
+
* progression, accessibility mode, orientation, and disabled state. Provides
|
|
48
|
+
* the shared context to descendant directives.
|
|
49
|
+
*
|
|
50
|
+
* Implements the [WAI-ARIA Tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/)
|
|
51
|
+
* in `mode="interactive"` (roving tabindex, `role="tablist"`, `aria-selected`)
|
|
52
|
+
* and an ordered-list progress pattern with `aria-current="step"` in
|
|
53
|
+
* `mode="progress"`.
|
|
54
|
+
*
|
|
55
|
+
* `activationMode='manual'` (default): arrow navigation only moves focus;
|
|
56
|
+
* Space / Enter activates. Recommended for wizards where step activation
|
|
57
|
+
* triggers validation. `activationMode='automatic'`: arrow navigation moves
|
|
58
|
+
* focus AND selects the step.
|
|
59
|
+
*/
|
|
60
|
+
class ForStepper {
|
|
61
|
+
#defaults = inject(FOR_STEPPER_DEFAULTS);
|
|
62
|
+
/**
|
|
63
|
+
* Two-way bindable selected step index in the inclusive range `0 … count`.
|
|
64
|
+
* Defaults to `0`. The terminal value `=== count()` (one past the last step) is
|
|
65
|
+
* the **completed** state (see `isCompleted` / `complete`). The `model()` change
|
|
66
|
+
* emitter (`(selectedIndexChange)`) fires only on internal selection changes
|
|
67
|
+
* (trigger click, Next/Previous, automatic-mode arrow navigation), never on
|
|
68
|
+
* consumer writes via `[(selectedIndex)]`.
|
|
69
|
+
*/
|
|
70
|
+
selectedIndex = model(0, /* @ts-ignore */
|
|
71
|
+
...(ngDevMode ? [{ debugName: "selectedIndex" }] : /* istanbul ignore next */ []));
|
|
72
|
+
/**
|
|
73
|
+
* Emits once each time the stepper transitions **into** the completed terminal
|
|
74
|
+
* state — i.e. when `selectedIndex()` reaches `count()`. Does not re-emit while
|
|
75
|
+
* it stays completed; retreating via `previous()` and re-entering emits again.
|
|
76
|
+
*/
|
|
77
|
+
complete = output();
|
|
78
|
+
/**
|
|
79
|
+
* When true, steps are only reachable after all preceding steps are completed
|
|
80
|
+
* or optional. Navigating back (`previous()`) always works regardless of this
|
|
81
|
+
* flag.
|
|
82
|
+
*/
|
|
83
|
+
linear = input(false, { ...(ngDevMode ? { debugName: "linear" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
84
|
+
/**
|
|
85
|
+
* Accessibility model. `'interactive'` (default): full WAI-ARIA Tabs pattern
|
|
86
|
+
* — `role="tablist"`, `role="tab"`, `role="tabpanel"`, roving tabindex, arrow
|
|
87
|
+
* navigation. `'progress'`: ordered list with `aria-current="step"` on the
|
|
88
|
+
* active trigger — no tab stop manipulation.
|
|
89
|
+
*/
|
|
90
|
+
mode = input('interactive', /* @ts-ignore */
|
|
91
|
+
...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
92
|
+
/**
|
|
93
|
+
* Layout axis for the step list. Affects `aria-orientation` on the list and
|
|
94
|
+
* which arrow keys navigate in interactive mode.
|
|
95
|
+
*/
|
|
96
|
+
orientation = input('horizontal', /* @ts-ignore */
|
|
97
|
+
...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
|
|
98
|
+
/**
|
|
99
|
+
* Arrow-key activation timing in interactive mode. Defaults to the value from
|
|
100
|
+
* `provideForStepperDefaults` for the surrounding scope (library default:
|
|
101
|
+
* `'manual'`).
|
|
102
|
+
*/
|
|
103
|
+
activationMode = input(this.#defaults.activationMode, /* @ts-ignore */
|
|
104
|
+
...(ngDevMode ? [{ debugName: "activationMode" }] : /* istanbul ignore next */ []));
|
|
105
|
+
/**
|
|
106
|
+
* Whether arrow navigation wraps around past the first / last selectable
|
|
107
|
+
* trigger. Defaults to the value from `provideForStepperDefaults` for the
|
|
108
|
+
* surrounding scope (library default: `true`).
|
|
109
|
+
*/
|
|
110
|
+
loop = input(this.#defaults.loop, { ...(ngDevMode ? { debugName: "loop" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
111
|
+
/**
|
|
112
|
+
* When true, all step triggers are non-interactive and the root carries
|
|
113
|
+
* `data-disabled`.
|
|
114
|
+
*/
|
|
115
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
116
|
+
/**
|
|
117
|
+
* Writing direction. When unset (default `null`), the inherited ambient
|
|
118
|
+
* direction is resolved from the nearest ancestor carrying a `dir` attribute
|
|
119
|
+
* (or `<html dir>`), defaulting to `'ltr'`. An explicit `[dir]` always wins.
|
|
120
|
+
* The resolved value is reflected to the host `dir` attribute and swaps
|
|
121
|
+
* ArrowLeft / ArrowRight semantics in RTL.
|
|
122
|
+
*/
|
|
123
|
+
_dirInput = input(null, { ...(ngDevMode ? { debugName: "_dirInput" } : /* istanbul ignore next */ {}), alias: 'dir' });
|
|
124
|
+
dir = injectTextDirection(this._dirInput);
|
|
125
|
+
roving = new RovingTabindex();
|
|
126
|
+
#items = new Collection();
|
|
127
|
+
#triggers = new Collection();
|
|
128
|
+
#contents = new Collection();
|
|
129
|
+
/** Total number of registered step items. */
|
|
130
|
+
count = computed(() => this.#items.items().length, /* @ts-ignore */
|
|
131
|
+
...(ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []));
|
|
132
|
+
/**
|
|
133
|
+
* True when the stepper is in the terminal **completed** state: `selectedIndex()`
|
|
134
|
+
* has reached `count()` (one past the last step). No step is current and every
|
|
135
|
+
* `[forStepperContent]` panel is inactive while this holds.
|
|
136
|
+
*/
|
|
137
|
+
isCompleted = computed(() => this.selectedIndex() >= this.count(), /* @ts-ignore */
|
|
138
|
+
...(ngDevMode ? [{ debugName: "isCompleted" }] : /* istanbul ignore next */ []));
|
|
139
|
+
#firstSelectableTriggerHost = computed(() => firstEnabledHost(this.#triggers.items()), /* @ts-ignore */
|
|
140
|
+
...(ngDevMode ? [{ debugName: "#firstSelectableTriggerHost" }] : /* istanbul ignore next */ []));
|
|
141
|
+
constructor() {
|
|
142
|
+
reconcileRovingActive(this.roving, this.#triggers.items);
|
|
143
|
+
let wasCompleted = this.isCompleted();
|
|
144
|
+
effect(() => {
|
|
145
|
+
const completed = this.isCompleted();
|
|
146
|
+
if (completed && !wasCompleted) {
|
|
147
|
+
this.complete.emit();
|
|
148
|
+
}
|
|
149
|
+
wasCompleted = completed;
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
indexOf(item) {
|
|
153
|
+
return this.#items.items().indexOf(item);
|
|
154
|
+
}
|
|
155
|
+
isCurrent(index) {
|
|
156
|
+
return this.selectedIndex() === index;
|
|
157
|
+
}
|
|
158
|
+
isReachable(index) {
|
|
159
|
+
if (!this.linear()) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (index === 0) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
const items = this.#items.items();
|
|
166
|
+
for (let i = 0; i < index; i++) {
|
|
167
|
+
const item = items[i];
|
|
168
|
+
if (!item || (!item.completed() && !item.optional())) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
resolvedStateFor(index) {
|
|
175
|
+
return this.#items.items()[index]?.resolvedState() ?? 'pending';
|
|
176
|
+
}
|
|
177
|
+
select(index) {
|
|
178
|
+
if (this.disabled()) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
const count = this.count();
|
|
182
|
+
if (count === 0) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const clamped = Math.max(0, Math.min(index, count));
|
|
186
|
+
if (!this.isReachable(clamped)) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
this.selectedIndex.set(clamped);
|
|
190
|
+
}
|
|
191
|
+
next() {
|
|
192
|
+
if (!this.canAdvance()) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
this.selectedIndex.set(this.selectedIndex() + 1);
|
|
196
|
+
}
|
|
197
|
+
previous() {
|
|
198
|
+
if (this.disabled() || this.selectedIndex() <= 0) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
this.selectedIndex.set(this.selectedIndex() - 1);
|
|
202
|
+
}
|
|
203
|
+
canAdvance() {
|
|
204
|
+
if (this.disabled() || this.selectedIndex() >= this.count()) {
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
if (!this.linear()) {
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
const current = this.#items.items()[this.selectedIndex()];
|
|
211
|
+
return (current?.completed() ?? false) || (current?.optional() ?? false);
|
|
212
|
+
}
|
|
213
|
+
canRetreat() {
|
|
214
|
+
return this.selectedIndex() > 0 && !this.disabled();
|
|
215
|
+
}
|
|
216
|
+
navigate(currentTrigger, action) {
|
|
217
|
+
if (this.disabled()) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const triggers = this.#triggers.items();
|
|
221
|
+
if (triggers.length === 0) {
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const currentIndex = triggers.findIndex((t) => t.host === currentTrigger);
|
|
225
|
+
const next = moveIndex(currentIndex < 0 ? 0 : currentIndex, triggers.length, action, {
|
|
226
|
+
loop: this.loop(),
|
|
227
|
+
isDisabled: (i) => !triggers[i].selectable(),
|
|
228
|
+
});
|
|
229
|
+
if (next === null) {
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const target = triggers[next];
|
|
233
|
+
if (!target) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
target.host.focus();
|
|
237
|
+
if (this.activationMode() === 'automatic') {
|
|
238
|
+
this.selectedIndex.set(next);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
registerItem(h) {
|
|
242
|
+
this.#items.register(h);
|
|
243
|
+
}
|
|
244
|
+
unregisterItem(h) {
|
|
245
|
+
this.#items.unregister(h);
|
|
246
|
+
}
|
|
247
|
+
registerTrigger(h) {
|
|
248
|
+
this.#triggers.register(h);
|
|
249
|
+
}
|
|
250
|
+
unregisterTrigger(h) {
|
|
251
|
+
this.#triggers.unregister(h);
|
|
252
|
+
this.roving.unregister(h.host);
|
|
253
|
+
}
|
|
254
|
+
registerContent(h) {
|
|
255
|
+
this.#contents.register(h);
|
|
256
|
+
}
|
|
257
|
+
unregisterContent(h) {
|
|
258
|
+
this.#contents.unregister(h);
|
|
259
|
+
}
|
|
260
|
+
triggerIdFor(index) {
|
|
261
|
+
return this.#triggers.items()[index]?.id() ?? null;
|
|
262
|
+
}
|
|
263
|
+
contentIdFor(index) {
|
|
264
|
+
return this.#contents.items()[index]?.id() ?? null;
|
|
265
|
+
}
|
|
266
|
+
indexOfContent(host) {
|
|
267
|
+
return this.#contents.indexOfHost(host);
|
|
268
|
+
}
|
|
269
|
+
isFirstSelectableTrigger(el) {
|
|
270
|
+
return this.#firstSelectableTriggerHost() === el;
|
|
271
|
+
}
|
|
272
|
+
hasCurrentTrigger() {
|
|
273
|
+
const idx = this.selectedIndex();
|
|
274
|
+
const triggers = this.#triggers.items();
|
|
275
|
+
if (idx < 0 || idx >= triggers.length) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
const trigger = triggers[idx];
|
|
279
|
+
return trigger !== undefined && trigger.selectable();
|
|
280
|
+
}
|
|
281
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepper, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
282
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForStepper, isStandalone: true, selector: "[forStepper]", inputs: { selectedIndex: { classPropertyName: "selectedIndex", publicName: "selectedIndex", isSignal: true, isRequired: false, transformFunction: null }, linear: { classPropertyName: "linear", publicName: "linear", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, activationMode: { classPropertyName: "activationMode", publicName: "activationMode", isSignal: true, isRequired: false, transformFunction: null }, loop: { classPropertyName: "loop", publicName: "loop", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _dirInput: { classPropertyName: "_dirInput", publicName: "dir", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { selectedIndex: "selectedIndexChange", complete: "complete" }, host: { properties: { "attr.data-orientation": "orientation()", "attr.data-mode": "mode()", "attr.data-disabled": "disabled() ? \"\" : null", "attr.dir": "dir()" } }, providers: [{ provide: FOR_STEPPER_CONTEXT, useExisting: ForStepper }], exportAs: ["forStepper"], ngImport: i0 });
|
|
283
|
+
}
|
|
284
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepper, decorators: [{
|
|
285
|
+
type: Directive,
|
|
286
|
+
args: [{
|
|
287
|
+
selector: '[forStepper]',
|
|
288
|
+
exportAs: 'forStepper',
|
|
289
|
+
host: {
|
|
290
|
+
'[attr.data-orientation]': 'orientation()',
|
|
291
|
+
'[attr.data-mode]': 'mode()',
|
|
292
|
+
'[attr.data-disabled]': 'disabled() ? "" : null',
|
|
293
|
+
'[attr.dir]': 'dir()',
|
|
294
|
+
},
|
|
295
|
+
providers: [{ provide: FOR_STEPPER_CONTEXT, useExisting: ForStepper }],
|
|
296
|
+
}]
|
|
297
|
+
}], ctorParameters: () => [], propDecorators: { selectedIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIndex", required: false }] }, { type: i0.Output, args: ["selectedIndexChange"] }], complete: [{ type: i0.Output, args: ["complete"] }], linear: [{ type: i0.Input, args: [{ isSignal: true, alias: "linear", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], activationMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "activationMode", required: false }] }], loop: [{ type: i0.Input, args: [{ isSignal: true, alias: "loop", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], _dirInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "dir", required: false }] }] } });
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The step-list container. In `mode="interactive"` renders as `role="tablist"`
|
|
301
|
+
* with `aria-orientation`; in `mode="progress"` renders as `role="list"`.
|
|
302
|
+
*
|
|
303
|
+
* Apply on an `<ol>` (recommended for semantic list markup) wrapping the
|
|
304
|
+
* `[forStepperItem]` elements. The `[forStepperContent]` panels live as
|
|
305
|
+
* siblings of the list.
|
|
306
|
+
*/
|
|
307
|
+
class ForStepperList {
|
|
308
|
+
ctx = injectStepperContext('ForStepperList');
|
|
309
|
+
/** Accessible name for the step list. Defers to a consumer `aria-labelledby`. */
|
|
310
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
311
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
312
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperList, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
313
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForStepperList, isStandalone: true, selector: "[forStepperList]", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.role": "ctx.mode() === 'interactive' ? 'tablist' : 'list'", "attr.aria-orientation": "ctx.mode() === 'interactive' ? ctx.orientation() : null", "attr.aria-label": "ariaLabel() || null", "attr.data-orientation": "ctx.orientation()" } }, exportAs: ["forStepperList"], ngImport: i0 });
|
|
314
|
+
}
|
|
315
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperList, decorators: [{
|
|
316
|
+
type: Directive,
|
|
317
|
+
args: [{
|
|
318
|
+
selector: '[forStepperList]',
|
|
319
|
+
exportAs: 'forStepperList',
|
|
320
|
+
host: {
|
|
321
|
+
'[attr.role]': "ctx.mode() === 'interactive' ? 'tablist' : 'list'",
|
|
322
|
+
'[attr.aria-orientation]': "ctx.mode() === 'interactive' ? ctx.orientation() : null",
|
|
323
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
324
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
325
|
+
},
|
|
326
|
+
}]
|
|
327
|
+
}], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* One step within a `ForStepper`. Owns per-step state (`completed`, `optional`,
|
|
331
|
+
* `disabled`, `hasError`, `state`) and exposes the `ForStepperItemContext` that
|
|
332
|
+
* child `[forStepperTrigger]`, `[forStepperIndicator]`, and
|
|
333
|
+
* `[forStepperSeparator]` directives consume.
|
|
334
|
+
*
|
|
335
|
+
* Apply on a list item element (e.g. `<li forStepperItem>`). The step's index
|
|
336
|
+
* is derived reactively from its DOM position within the stepper via the root's
|
|
337
|
+
* item collection.
|
|
338
|
+
*/
|
|
339
|
+
class ForStepperItem {
|
|
340
|
+
ctx = injectStepperContext('ForStepperItem');
|
|
341
|
+
/** Host element — satisfies `ForStepperItemHandle.host` for collection registration. */
|
|
342
|
+
host = inject(ElementRef).nativeElement;
|
|
343
|
+
/**
|
|
344
|
+
* Manual completion flag. When `true`, the step is completed regardless of any
|
|
345
|
+
* bound `field()`. Bind via `[completed]`.
|
|
346
|
+
*/
|
|
347
|
+
_completedInput = input(false, { ...(ngDevMode ? { debugName: "_completedInput" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'completed' });
|
|
348
|
+
/** Marks this step as optional (can be skipped in linear mode). */
|
|
349
|
+
optional = input(false, { ...(ngDevMode ? { debugName: "optional" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
350
|
+
/** When true, this step's trigger ignores clicks and the step is unreachable by keyboard. */
|
|
351
|
+
disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
352
|
+
/**
|
|
353
|
+
* Manual error flag. When `true`, the step reflects the `'error'` resolved state
|
|
354
|
+
* (unless current) regardless of any bound `field()`. Bind via `[hasError]`.
|
|
355
|
+
*/
|
|
356
|
+
_hasErrorInput = input(false, { ...(ngDevMode ? { debugName: "_hasErrorInput" } : /* istanbul ignore next */ {}), transform: booleanAttribute, alias: 'hasError' });
|
|
357
|
+
/**
|
|
358
|
+
* Optional Signal Forms field. When bound, the step's `completed` and
|
|
359
|
+
* `hasError` derive from the field's reactive validity: `completed` is `true`
|
|
360
|
+
* when the field is valid and touched; `hasError` is `true` when the field is
|
|
361
|
+
* touched and invalid. A manual `[completed]` / `[hasError]` input always wins
|
|
362
|
+
* when set. Leave unset to drive completion manually.
|
|
363
|
+
*
|
|
364
|
+
* The `@angular/forms` peer is optional: the field *type* is imported with
|
|
365
|
+
* `import type`, so binding `[field]` requires the peer but the primitive
|
|
366
|
+
* compiles and tree-shakes without it for consumers who never use it.
|
|
367
|
+
*/
|
|
368
|
+
field = input(null, /* @ts-ignore */
|
|
369
|
+
...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
|
|
370
|
+
/**
|
|
371
|
+
* Whether this step is completed. A manual `[completed]` input wins; otherwise,
|
|
372
|
+
* when a `field()` is bound, derives `true` from `field` valid + touched.
|
|
373
|
+
*/
|
|
374
|
+
completed = computed(() => {
|
|
375
|
+
if (this._completedInput()) {
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
const f = this.field();
|
|
379
|
+
if (!f) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
const state = f();
|
|
383
|
+
return state.valid() && state.touched();
|
|
384
|
+
}, /* @ts-ignore */
|
|
385
|
+
...(ngDevMode ? [{ debugName: "completed" }] : /* istanbul ignore next */ []));
|
|
386
|
+
/**
|
|
387
|
+
* Whether this step has an error. A manual `[hasError]` input wins; otherwise,
|
|
388
|
+
* when a `field()` is bound, derives `true` from `field` touched + invalid.
|
|
389
|
+
*/
|
|
390
|
+
hasError = computed(() => {
|
|
391
|
+
if (this._hasErrorInput()) {
|
|
392
|
+
return true;
|
|
393
|
+
}
|
|
394
|
+
const f = this.field();
|
|
395
|
+
if (!f) {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
const state = f();
|
|
399
|
+
return state.touched() && state.invalid();
|
|
400
|
+
}, /* @ts-ignore */
|
|
401
|
+
...(ngDevMode ? [{ debugName: "hasError" }] : /* istanbul ignore next */ []));
|
|
402
|
+
/**
|
|
403
|
+
* Custom state string override. When non-empty, wins over the derived resolved
|
|
404
|
+
* state (`error`, `active`, `completed`, `pending`) and drives every
|
|
405
|
+
* `data-state` attribute on this step.
|
|
406
|
+
*/
|
|
407
|
+
state = input(null, /* @ts-ignore */
|
|
408
|
+
...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
|
|
409
|
+
/** Whether this step is disabled — own `disabled` OR the root stepper's `disabled`. */
|
|
410
|
+
effectiveDisabled = computed(() => this.disabled() || this.ctx.disabled(), /* @ts-ignore */
|
|
411
|
+
...(ngDevMode ? [{ debugName: "effectiveDisabled" }] : /* istanbul ignore next */ []));
|
|
412
|
+
/** DOM-order index of this step within the stepper. */
|
|
413
|
+
index = computed(() => this.ctx.indexOf(this), /* @ts-ignore */
|
|
414
|
+
...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
|
|
415
|
+
/** True when this step is the currently selected step. */
|
|
416
|
+
current = computed(() => this.ctx.isCurrent(this.index()), /* @ts-ignore */
|
|
417
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
418
|
+
/**
|
|
419
|
+
* True when this step can be focused and activated. False when the step is
|
|
420
|
+
* effectively disabled or not yet reachable in linear mode.
|
|
421
|
+
*/
|
|
422
|
+
selectable = computed(() => this.ctx.isReachable(this.index()) && !this.effectiveDisabled(), /* @ts-ignore */
|
|
423
|
+
...(ngDevMode ? [{ debugName: "selectable" }] : /* istanbul ignore next */ []));
|
|
424
|
+
/**
|
|
425
|
+
* Resolved `data-state` string. Precedence:
|
|
426
|
+
* 1. Custom `state()` when non-empty.
|
|
427
|
+
* 2. `'error'` when `hasError()` and not current.
|
|
428
|
+
* 3. `'active'` when current.
|
|
429
|
+
* 4. `'completed'` when `completed()`.
|
|
430
|
+
* 5. `'pending'` otherwise.
|
|
431
|
+
*/
|
|
432
|
+
resolvedState = computed(() => {
|
|
433
|
+
const custom = this.state();
|
|
434
|
+
if (custom) {
|
|
435
|
+
return custom;
|
|
436
|
+
}
|
|
437
|
+
if (this.hasError() && !this.current()) {
|
|
438
|
+
return 'error';
|
|
439
|
+
}
|
|
440
|
+
if (this.current()) {
|
|
441
|
+
return 'active';
|
|
442
|
+
}
|
|
443
|
+
if (this.completed()) {
|
|
444
|
+
return 'completed';
|
|
445
|
+
}
|
|
446
|
+
return 'pending';
|
|
447
|
+
}, /* @ts-ignore */
|
|
448
|
+
...(ngDevMode ? [{ debugName: "resolvedState" }] : /* istanbul ignore next */ []));
|
|
449
|
+
constructor() {
|
|
450
|
+
registerHandle(this, (h) => this.ctx.registerItem(h), (h) => this.ctx.unregisterItem(h), 'afterNextRender');
|
|
451
|
+
}
|
|
452
|
+
/** Selects this step. Delegates to `ForStepperContext.select(index)`. */
|
|
453
|
+
select() {
|
|
454
|
+
this.ctx.select(this.index());
|
|
455
|
+
}
|
|
456
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperItem, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
457
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForStepperItem, isStandalone: true, selector: "[forStepperItem]", inputs: { _completedInput: { classPropertyName: "_completedInput", publicName: "completed", isSignal: true, isRequired: false, transformFunction: null }, optional: { classPropertyName: "optional", publicName: "optional", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, _hasErrorInput: { classPropertyName: "_hasErrorInput", publicName: "hasError", isSignal: true, isRequired: false, transformFunction: null }, field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-state": "resolvedState()", "attr.data-disabled": "effectiveDisabled() ? \"\" : null", "attr.data-orientation": "ctx.orientation()" } }, providers: [{ provide: FOR_STEPPER_ITEM_CONTEXT, useExisting: ForStepperItem }], exportAs: ["forStepperItem"], ngImport: i0 });
|
|
458
|
+
}
|
|
459
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperItem, decorators: [{
|
|
460
|
+
type: Directive,
|
|
461
|
+
args: [{
|
|
462
|
+
selector: '[forStepperItem]',
|
|
463
|
+
exportAs: 'forStepperItem',
|
|
464
|
+
host: {
|
|
465
|
+
'[attr.data-state]': 'resolvedState()',
|
|
466
|
+
'[attr.data-disabled]': 'effectiveDisabled() ? "" : null',
|
|
467
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
468
|
+
},
|
|
469
|
+
providers: [{ provide: FOR_STEPPER_ITEM_CONTEXT, useExisting: ForStepperItem }],
|
|
470
|
+
}]
|
|
471
|
+
}], ctorParameters: () => [], propDecorators: { _completedInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "completed", required: false }] }], optional: [{ type: i0.Input, args: [{ isSignal: true, alias: "optional", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], _hasErrorInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "hasError", required: false }] }], field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: false }] }], state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: false }] }] } });
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Header for one step. In `mode="interactive"` carries `role="tab"`, participates
|
|
475
|
+
* in the roving tabindex, and wires `aria-selected` / `aria-controls` /
|
|
476
|
+
* `aria-disabled`. In `mode="progress"` carries `aria-current="step"` on the
|
|
477
|
+
* active step only and is a static element (no tab-stop manipulation).
|
|
478
|
+
*
|
|
479
|
+
* Apply on a `<button type="button">` for interactive mode so Enter / Space
|
|
480
|
+
* activation is native. A static element (`<span>`) is acceptable for progress
|
|
481
|
+
* mode.
|
|
482
|
+
*
|
|
483
|
+
* Disabled triggers in interactive mode keep their tab stop (`aria-disabled`
|
|
484
|
+
* instead of native `disabled`) so assistive tech can announce them.
|
|
485
|
+
*/
|
|
486
|
+
class ForStepperTrigger {
|
|
487
|
+
ctx = injectStepperContext('ForStepperTrigger');
|
|
488
|
+
item = injectStepperItemContext('ForStepperTrigger');
|
|
489
|
+
#host = inject(ElementRef).nativeElement;
|
|
490
|
+
/** Generated (or consumer-set static) id for this trigger. Used by content panels for `aria-labelledby`. */
|
|
491
|
+
id = hostId('for-stepper-trigger');
|
|
492
|
+
/** Id of the content panel this trigger controls (the matching panel by step index). */
|
|
493
|
+
controlsId = computed(() => this.ctx.contentIdFor(this.item.index()), /* @ts-ignore */
|
|
494
|
+
...(ngDevMode ? [{ debugName: "controlsId" }] : /* istanbul ignore next */ []));
|
|
495
|
+
/**
|
|
496
|
+
* APG tabindex cascade (interactive mode only). In progress mode returns
|
|
497
|
+
* `null` so no tab-stop attribute is emitted. In interactive mode:
|
|
498
|
+
* - `null` when not selectable (disabled or unreachable in linear mode).
|
|
499
|
+
* - Roving-tracker value once any trigger has been focused.
|
|
500
|
+
* - `0` when this step is current.
|
|
501
|
+
* - `-1` when another trigger owns the current step.
|
|
502
|
+
* - `0` if this is the first selectable trigger (fallback entry point).
|
|
503
|
+
* - `-1` otherwise.
|
|
504
|
+
*/
|
|
505
|
+
tabindex = computed(() => {
|
|
506
|
+
if (this.ctx.mode() !== 'interactive') {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
if (!this.item.selectable()) {
|
|
510
|
+
return -1;
|
|
511
|
+
}
|
|
512
|
+
if (this.ctx.roving.hasActive()) {
|
|
513
|
+
return this.ctx.roving.tabindexFor(this.#host);
|
|
514
|
+
}
|
|
515
|
+
if (this.item.current()) {
|
|
516
|
+
return 0;
|
|
517
|
+
}
|
|
518
|
+
if (this.ctx.hasCurrentTrigger()) {
|
|
519
|
+
return -1;
|
|
520
|
+
}
|
|
521
|
+
return this.ctx.isFirstSelectableTrigger(this.#host) ? 0 : -1;
|
|
522
|
+
}, /* @ts-ignore */
|
|
523
|
+
...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
|
|
524
|
+
constructor() {
|
|
525
|
+
const handle = {
|
|
526
|
+
host: this.#host,
|
|
527
|
+
id: this.id,
|
|
528
|
+
selectable: this.item.selectable,
|
|
529
|
+
disabled: computed(() => !this.item.selectable()),
|
|
530
|
+
};
|
|
531
|
+
registerHandle(handle, (h) => this.ctx.registerTrigger(h), (h) => this.ctx.unregisterTrigger(h), 'afterNextRender');
|
|
532
|
+
}
|
|
533
|
+
onClick() {
|
|
534
|
+
if (this.ctx.mode() !== 'interactive' || !this.item.selectable()) {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
this.item.select();
|
|
538
|
+
}
|
|
539
|
+
onFocus() {
|
|
540
|
+
if (this.ctx.mode() !== 'interactive' || !this.item.selectable()) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
this.ctx.roving.setActive(this.#host);
|
|
544
|
+
}
|
|
545
|
+
onKeyDown(event) {
|
|
546
|
+
if (this.ctx.mode() !== 'interactive' || !this.item.selectable()) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
const action = resolveListNavigation(event, {
|
|
550
|
+
orientation: this.ctx.orientation(),
|
|
551
|
+
dir: this.ctx.dir(),
|
|
552
|
+
});
|
|
553
|
+
if (!action) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
event.preventDefault();
|
|
557
|
+
this.ctx.navigate(this.#host, action);
|
|
558
|
+
}
|
|
559
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperTrigger, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
560
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperTrigger, isStandalone: true, selector: "[forStepperTrigger]", host: { listeners: { "click": "onClick()", "focus": "onFocus()", "keydown": "onKeyDown($event)" }, properties: { "attr.role": "ctx.mode() === 'interactive' ? 'tab' : null", "id": "id()", "attr.aria-selected": "ctx.mode() === 'interactive' ? (item.current() ? 'true' : 'false') : null", "attr.aria-controls": "ctx.mode() === 'interactive' && item.current() ? controlsId() : null", "attr.aria-current": "ctx.mode() === 'progress' && item.current() ? 'step' : null", "attr.aria-disabled": "ctx.mode() === 'interactive' && !item.selectable() ? 'true' : null", "attr.tabindex": "tabindex()", "attr.data-state": "item.resolvedState()", "attr.data-disabled": "item.effectiveDisabled() ? \"\" : null", "attr.data-orientation": "ctx.orientation()" } }, exportAs: ["forStepperTrigger"], ngImport: i0 });
|
|
561
|
+
}
|
|
562
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperTrigger, decorators: [{
|
|
563
|
+
type: Directive,
|
|
564
|
+
args: [{
|
|
565
|
+
selector: '[forStepperTrigger]',
|
|
566
|
+
exportAs: 'forStepperTrigger',
|
|
567
|
+
host: {
|
|
568
|
+
'[attr.role]': "ctx.mode() === 'interactive' ? 'tab' : null",
|
|
569
|
+
'[id]': 'id()',
|
|
570
|
+
'[attr.aria-selected]': "ctx.mode() === 'interactive' ? (item.current() ? 'true' : 'false') : null",
|
|
571
|
+
'[attr.aria-controls]': "ctx.mode() === 'interactive' && item.current() ? controlsId() : null",
|
|
572
|
+
'[attr.aria-current]': "ctx.mode() === 'progress' && item.current() ? 'step' : null",
|
|
573
|
+
'[attr.aria-disabled]': "ctx.mode() === 'interactive' && !item.selectable() ? 'true' : null",
|
|
574
|
+
'[attr.tabindex]': 'tabindex()',
|
|
575
|
+
'[attr.data-state]': 'item.resolvedState()',
|
|
576
|
+
'[attr.data-disabled]': 'item.effectiveDisabled() ? "" : null',
|
|
577
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
578
|
+
'(click)': 'onClick()',
|
|
579
|
+
'(focus)': 'onFocus()',
|
|
580
|
+
'(keydown)': 'onKeyDown($event)',
|
|
581
|
+
},
|
|
582
|
+
}]
|
|
583
|
+
}], ctorParameters: () => [] });
|
|
584
|
+
|
|
585
|
+
/**
|
|
586
|
+
* Decorative icon slot for a step. Hidden from the accessibility tree
|
|
587
|
+
* (`aria-hidden="true"`). Reflects the step's resolved `data-state` so
|
|
588
|
+
* consumers can swap icons via CSS `[data-state="completed"]`,
|
|
589
|
+
* `[data-state="error"]`, etc., or with `@if` keyed on the exported
|
|
590
|
+
* `#indicator="forStepperIndicator"` reference.
|
|
591
|
+
*
|
|
592
|
+
* Must be used inside a `[forStepperItem]` element.
|
|
593
|
+
*/
|
|
594
|
+
class ForStepperIndicator {
|
|
595
|
+
item = injectStepperItemContext('ForStepperIndicator');
|
|
596
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperIndicator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
597
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperIndicator, isStandalone: true, selector: "[forStepperIndicator]", host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-state": "item.resolvedState()" } }, exportAs: ["forStepperIndicator"], ngImport: i0 });
|
|
598
|
+
}
|
|
599
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperIndicator, decorators: [{
|
|
600
|
+
type: Directive,
|
|
601
|
+
args: [{
|
|
602
|
+
selector: '[forStepperIndicator]',
|
|
603
|
+
exportAs: 'forStepperIndicator',
|
|
604
|
+
host: {
|
|
605
|
+
'aria-hidden': 'true',
|
|
606
|
+
'[attr.data-state]': 'item.resolvedState()',
|
|
607
|
+
},
|
|
608
|
+
}]
|
|
609
|
+
}] });
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* Decorative connector between steps. Hidden from the accessibility tree
|
|
613
|
+
* (`aria-hidden="true"`). Reflects `data-state="completed"` when the preceding
|
|
614
|
+
* step is marked completed, `data-state="pending"` otherwise. Also reflects
|
|
615
|
+
* `data-orientation` so consumers can style horizontal vs vertical separators
|
|
616
|
+
* differently.
|
|
617
|
+
*
|
|
618
|
+
* Must be used inside a `[forStepperItem]` element.
|
|
619
|
+
*/
|
|
620
|
+
class ForStepperSeparator {
|
|
621
|
+
ctx = injectStepperContext('ForStepperSeparator');
|
|
622
|
+
item = injectStepperItemContext('ForStepperSeparator');
|
|
623
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperSeparator, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
624
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperSeparator, isStandalone: true, selector: "[forStepperSeparator]", host: { attributes: { "aria-hidden": "true" }, properties: { "attr.data-state": "item.completed() ? 'completed' : 'pending'", "attr.data-orientation": "ctx.orientation()" } }, exportAs: ["forStepperSeparator"], ngImport: i0 });
|
|
625
|
+
}
|
|
626
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperSeparator, decorators: [{
|
|
627
|
+
type: Directive,
|
|
628
|
+
args: [{
|
|
629
|
+
selector: '[forStepperSeparator]',
|
|
630
|
+
exportAs: 'forStepperSeparator',
|
|
631
|
+
host: {
|
|
632
|
+
'aria-hidden': 'true',
|
|
633
|
+
'[attr.data-state]': "item.completed() ? 'completed' : 'pending'",
|
|
634
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
635
|
+
},
|
|
636
|
+
}]
|
|
637
|
+
}] });
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Panel for one step. Correlates to a step by **position** — the Nth
|
|
641
|
+
* `[forStepperContent]` panel in registration (DOM document) order corresponds
|
|
642
|
+
* to the Nth step.
|
|
643
|
+
*
|
|
644
|
+
* In `mode="interactive"` the panel carries `role="tabpanel"` and is a tab
|
|
645
|
+
* stop (`tabindex="0"`) only when it has no focusable content of its own
|
|
646
|
+
* (WAI-ARIA Tabs APG rule). In `mode="progress"` the panel carries
|
|
647
|
+
* `role="group"` and is never a tab stop.
|
|
648
|
+
*
|
|
649
|
+
* While inactive the directive reflects `aria-hidden="true"` and `inert` so
|
|
650
|
+
* mounted-but-inactive panels are removed from the accessibility tree and focus
|
|
651
|
+
* order automatically. DOM presence is the consumer's responsibility — wrap
|
|
652
|
+
* with `@if (current())` to unmount or leave mounted and toggle visibility with
|
|
653
|
+
* CSS via `[data-state="inactive"]`.
|
|
654
|
+
*/
|
|
655
|
+
class ForStepperContent {
|
|
656
|
+
ctx = injectStepperContext('ForStepperContent');
|
|
657
|
+
#host = inject(ElementRef).nativeElement;
|
|
658
|
+
/** Generated (or consumer-set static) id for this panel. Used by triggers for `aria-controls`. */
|
|
659
|
+
id = hostId('for-stepper-content');
|
|
660
|
+
#hasFocusableContent = injectHasFocusableContent();
|
|
661
|
+
#index = computed(() => this.ctx.indexOfContent(this.#host), /* @ts-ignore */
|
|
662
|
+
...(ngDevMode ? [{ debugName: "#index" }] : /* istanbul ignore next */ []));
|
|
663
|
+
/** True when this panel corresponds to the currently selected step. */
|
|
664
|
+
current = computed(() => this.ctx.isCurrent(this.#index()), /* @ts-ignore */
|
|
665
|
+
...(ngDevMode ? [{ debugName: "current" }] : /* istanbul ignore next */ []));
|
|
666
|
+
/** Id of the trigger that labels this panel (`aria-labelledby`). */
|
|
667
|
+
labelledBy = computed(() => this.ctx.triggerIdFor(this.#index()), /* @ts-ignore */
|
|
668
|
+
...(ngDevMode ? [{ debugName: "labelledBy" }] : /* istanbul ignore next */ []));
|
|
669
|
+
/**
|
|
670
|
+
* APG tabindex: `'0'` only when in interactive mode and the panel has no
|
|
671
|
+
* focusable content of its own, so screen-reader users can reach the panel
|
|
672
|
+
* itself. A panel containing buttons / links / form controls gets no
|
|
673
|
+
* `tabindex`, avoiding a redundant tab stop.
|
|
674
|
+
*/
|
|
675
|
+
tabindex = computed(() => {
|
|
676
|
+
if (this.ctx.mode() !== 'interactive') {
|
|
677
|
+
return null;
|
|
678
|
+
}
|
|
679
|
+
return this.#hasFocusableContent() ? null : '0';
|
|
680
|
+
}, /* @ts-ignore */
|
|
681
|
+
...(ngDevMode ? [{ debugName: "tabindex" }] : /* istanbul ignore next */ []));
|
|
682
|
+
constructor() {
|
|
683
|
+
const handle = { host: this.#host, id: this.id };
|
|
684
|
+
registerHandle(handle, (h) => this.ctx.registerContent(h), (h) => this.ctx.unregisterContent(h), 'afterNextRender');
|
|
685
|
+
}
|
|
686
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
687
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperContent, isStandalone: true, selector: "[forStepperContent]", host: { properties: { "attr.role": "ctx.mode() === 'interactive' ? 'tabpanel' : 'group'", "id": "id()", "attr.aria-labelledby": "labelledBy()", "attr.tabindex": "tabindex()", "attr.aria-hidden": "current() ? null : \"true\"", "attr.inert": "current() ? null : \"\"", "attr.data-state": "current() ? \"active\" : \"inactive\"", "attr.data-orientation": "ctx.orientation()" } }, exportAs: ["forStepperContent"], ngImport: i0 });
|
|
688
|
+
}
|
|
689
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperContent, decorators: [{
|
|
690
|
+
type: Directive,
|
|
691
|
+
args: [{
|
|
692
|
+
selector: '[forStepperContent]',
|
|
693
|
+
exportAs: 'forStepperContent',
|
|
694
|
+
host: {
|
|
695
|
+
'[attr.role]': "ctx.mode() === 'interactive' ? 'tabpanel' : 'group'",
|
|
696
|
+
'[id]': 'id()',
|
|
697
|
+
'[attr.aria-labelledby]': 'labelledBy()',
|
|
698
|
+
'[attr.tabindex]': 'tabindex()',
|
|
699
|
+
'[attr.aria-hidden]': 'current() ? null : "true"',
|
|
700
|
+
'[attr.inert]': 'current() ? null : ""',
|
|
701
|
+
'[attr.data-state]': 'current() ? "active" : "inactive"',
|
|
702
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
703
|
+
},
|
|
704
|
+
}]
|
|
705
|
+
}], ctorParameters: () => [] });
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* Terminal "all steps complete" panel. Shown (active) only while the stepper is in
|
|
709
|
+
* the completed state (`selectedIndex` has reached `count`); gated the same way as
|
|
710
|
+
* `[forStepperContent]` — `aria-hidden` / `inert` / `data-state` flip with
|
|
711
|
+
* `isCompleted()`. The natural place for a wizard's final success panel.
|
|
712
|
+
*
|
|
713
|
+
* DOM presence is the consumer's responsibility — wrap with `@if` driven by the same
|
|
714
|
+
* completed condition, or leave it mounted and toggle visibility with CSS via
|
|
715
|
+
* `[data-state="active"]`.
|
|
716
|
+
*/
|
|
717
|
+
class ForStepperCompletedContent {
|
|
718
|
+
ctx = injectStepperContext('ForStepperCompletedContent');
|
|
719
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperCompletedContent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
720
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperCompletedContent, isStandalone: true, selector: "[forStepperCompletedContent]", host: { attributes: { "role": "group" }, properties: { "attr.aria-hidden": "ctx.isCompleted() ? null : \"true\"", "attr.inert": "ctx.isCompleted() ? null : \"\"", "attr.data-state": "ctx.isCompleted() ? \"active\" : \"inactive\"" } }, exportAs: ["forStepperCompletedContent"], ngImport: i0 });
|
|
721
|
+
}
|
|
722
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperCompletedContent, decorators: [{
|
|
723
|
+
type: Directive,
|
|
724
|
+
args: [{
|
|
725
|
+
selector: '[forStepperCompletedContent]',
|
|
726
|
+
exportAs: 'forStepperCompletedContent',
|
|
727
|
+
host: {
|
|
728
|
+
role: 'group',
|
|
729
|
+
'[attr.aria-hidden]': 'ctx.isCompleted() ? null : "true"',
|
|
730
|
+
'[attr.inert]': 'ctx.isCompleted() ? null : ""',
|
|
731
|
+
'[attr.data-state]': 'ctx.isCompleted() ? "active" : "inactive"',
|
|
732
|
+
},
|
|
733
|
+
}]
|
|
734
|
+
}] });
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* "Next" button for the stepper. Apply on a `<button>` element. Reflects
|
|
738
|
+
* `aria-disabled="true"` when `canAdvance()` is false (last step reached, root
|
|
739
|
+
* disabled, or in linear mode the current step is not completed / optional).
|
|
740
|
+
*
|
|
741
|
+
* The button retains its native `type="button"` attribute via the `type` host
|
|
742
|
+
* binding to prevent accidental form submission.
|
|
743
|
+
*
|
|
744
|
+
* Clicking while `aria-disabled` is a no-op because `next()` guards internally.
|
|
745
|
+
*/
|
|
746
|
+
class ForStepperNext {
|
|
747
|
+
ctx = injectStepperContext('ForStepperNext');
|
|
748
|
+
onClick() {
|
|
749
|
+
this.ctx.next();
|
|
750
|
+
}
|
|
751
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperNext, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
752
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperNext, isStandalone: true, selector: "button[forStepperNext]", host: { attributes: { "type": "button" }, listeners: { "click": "onClick()" }, properties: { "attr.aria-disabled": "!ctx.canAdvance() ? \"true\" : null" } }, exportAs: ["forStepperNext"], ngImport: i0 });
|
|
753
|
+
}
|
|
754
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperNext, decorators: [{
|
|
755
|
+
type: Directive,
|
|
756
|
+
args: [{
|
|
757
|
+
selector: 'button[forStepperNext]',
|
|
758
|
+
exportAs: 'forStepperNext',
|
|
759
|
+
host: {
|
|
760
|
+
type: 'button',
|
|
761
|
+
'[attr.aria-disabled]': '!ctx.canAdvance() ? "true" : null',
|
|
762
|
+
'(click)': 'onClick()',
|
|
763
|
+
},
|
|
764
|
+
}]
|
|
765
|
+
}] });
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* "Previous" button for the stepper. Apply on a `<button>` element. Reflects
|
|
769
|
+
* `aria-disabled="true"` when `canRetreat()` is false (first step reached or
|
|
770
|
+
* root disabled).
|
|
771
|
+
*
|
|
772
|
+
* The button retains its native `type="button"` attribute via the `type` host
|
|
773
|
+
* binding to prevent accidental form submission.
|
|
774
|
+
*
|
|
775
|
+
* Clicking while `aria-disabled` is a no-op because `previous()` guards
|
|
776
|
+
* internally.
|
|
777
|
+
*/
|
|
778
|
+
class ForStepperPrevious {
|
|
779
|
+
ctx = injectStepperContext('ForStepperPrevious');
|
|
780
|
+
onClick() {
|
|
781
|
+
this.ctx.previous();
|
|
782
|
+
}
|
|
783
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperPrevious, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
784
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.2", type: ForStepperPrevious, isStandalone: true, selector: "button[forStepperPrevious]", host: { attributes: { "type": "button" }, listeners: { "click": "onClick()" }, properties: { "attr.aria-disabled": "!ctx.canRetreat() ? \"true\" : null" } }, exportAs: ["forStepperPrevious"], ngImport: i0 });
|
|
785
|
+
}
|
|
786
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperPrevious, decorators: [{
|
|
787
|
+
type: Directive,
|
|
788
|
+
args: [{
|
|
789
|
+
selector: 'button[forStepperPrevious]',
|
|
790
|
+
exportAs: 'forStepperPrevious',
|
|
791
|
+
host: {
|
|
792
|
+
type: 'button',
|
|
793
|
+
'[attr.aria-disabled]': '!ctx.canRetreat() ? "true" : null',
|
|
794
|
+
'(click)': 'onClick()',
|
|
795
|
+
},
|
|
796
|
+
}]
|
|
797
|
+
}] });
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Optional progress indicator for the Stepper. Reads the stepper context and
|
|
801
|
+
* reflects how far through the steps the user is as a WAI-ARIA
|
|
802
|
+
* [`progressbar`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/progressbar_role):
|
|
803
|
+
* `aria-valuemin="0"`, `aria-valuemax="100"`, `aria-valuenow` = the computed
|
|
804
|
+
* percent, and an `aria-valuetext` describing the position. Publishes a
|
|
805
|
+
* `--for-stepper-progress` (0–1) custom property for styling; ships no visuals.
|
|
806
|
+
*
|
|
807
|
+
* Must be used inside a `[forStepper]` element.
|
|
808
|
+
*/
|
|
809
|
+
class ForStepperProgress {
|
|
810
|
+
ctx = injectStepperContext('ForStepperProgress');
|
|
811
|
+
/**
|
|
812
|
+
* Basis for the reported percent. `'index'` (default) derives progress from
|
|
813
|
+
* the current step index; `'completed'`
|
|
814
|
+
* derives it from the count of steps whose resolved state is `completed`.
|
|
815
|
+
*/
|
|
816
|
+
valueBy = input('index', /* @ts-ignore */
|
|
817
|
+
...(ngDevMode ? [{ debugName: "valueBy" }] : /* istanbul ignore next */ []));
|
|
818
|
+
/**
|
|
819
|
+
* Accessible name for the progressbar. Defaults to `null`, emitting no
|
|
820
|
+
* `aria-label`; prefer a visible label referenced via `aria-labelledby` when
|
|
821
|
+
* one exists.
|
|
822
|
+
*/
|
|
823
|
+
ariaLabel = input(null, /* @ts-ignore */
|
|
824
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
825
|
+
#completedCount = computed(() => {
|
|
826
|
+
const total = this.ctx.count();
|
|
827
|
+
let n = 0;
|
|
828
|
+
for (let i = 0; i < total; i++) {
|
|
829
|
+
if (this.ctx.resolvedStateFor(i) === 'completed') {
|
|
830
|
+
n++;
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return n;
|
|
834
|
+
}, /* @ts-ignore */
|
|
835
|
+
...(ngDevMode ? [{ debugName: "#completedCount" }] : /* istanbul ignore next */ []));
|
|
836
|
+
#value = computed(() => this.valueBy() === 'completed' ? this.#completedCount() : this.ctx.selectedIndex(), /* @ts-ignore */
|
|
837
|
+
...(ngDevMode ? [{ debugName: "#value" }] : /* istanbul ignore next */ []));
|
|
838
|
+
/**
|
|
839
|
+
* Progress as a whole-number percent in `[0, 100]`, reflected to
|
|
840
|
+
* `aria-valuenow` and (divided by 100) to the `--for-stepper-progress`
|
|
841
|
+
* custom property.
|
|
842
|
+
*/
|
|
843
|
+
percent = computed(() => {
|
|
844
|
+
const total = this.ctx.count();
|
|
845
|
+
if (total <= 1) {
|
|
846
|
+
return total === 1 && this.#value() >= 1 ? 100 : 0;
|
|
847
|
+
}
|
|
848
|
+
const raw = Math.round((this.#value() / (total - 1)) * 100);
|
|
849
|
+
return Math.min(100, Math.max(0, raw));
|
|
850
|
+
}, /* @ts-ignore */
|
|
851
|
+
...(ngDevMode ? [{ debugName: "percent" }] : /* istanbul ignore next */ []));
|
|
852
|
+
/**
|
|
853
|
+
* Human-readable position string reflected to `aria-valuetext`: `"Step X of N"`
|
|
854
|
+
* on the `'index'` basis, `"P% complete"` on the `'completed'` basis.
|
|
855
|
+
*/
|
|
856
|
+
valueText = computed(() => this.valueBy() === 'completed'
|
|
857
|
+
? `${this.percent()}% complete`
|
|
858
|
+
: `Step ${Math.min(this.ctx.selectedIndex() + 1, this.ctx.count())} of ${this.ctx.count()}`, /* @ts-ignore */
|
|
859
|
+
...(ngDevMode ? [{ debugName: "valueText" }] : /* istanbul ignore next */ []));
|
|
860
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperProgress, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
861
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.2", type: ForStepperProgress, isStandalone: true, selector: "[forStepperProgress]", inputs: { valueBy: { classPropertyName: "valueBy", publicName: "valueBy", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "progressbar" }, properties: { "attr.aria-valuemin": "0", "attr.aria-valuemax": "100", "attr.aria-valuenow": "percent()", "attr.aria-valuetext": "valueText()", "attr.aria-label": "ariaLabel() || null", "attr.data-orientation": "ctx.orientation()", "style.--for-stepper-progress": "percent() / 100" } }, exportAs: ["forStepperProgress"], ngImport: i0 });
|
|
862
|
+
}
|
|
863
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.2", ngImport: i0, type: ForStepperProgress, decorators: [{
|
|
864
|
+
type: Directive,
|
|
865
|
+
args: [{
|
|
866
|
+
selector: '[forStepperProgress]',
|
|
867
|
+
exportAs: 'forStepperProgress',
|
|
868
|
+
host: {
|
|
869
|
+
role: 'progressbar',
|
|
870
|
+
'[attr.aria-valuemin]': '0',
|
|
871
|
+
'[attr.aria-valuemax]': '100',
|
|
872
|
+
'[attr.aria-valuenow]': 'percent()',
|
|
873
|
+
'[attr.aria-valuetext]': 'valueText()',
|
|
874
|
+
'[attr.aria-label]': 'ariaLabel() || null',
|
|
875
|
+
'[attr.data-orientation]': 'ctx.orientation()',
|
|
876
|
+
'[style.--for-stepper-progress]': 'percent() / 100',
|
|
877
|
+
},
|
|
878
|
+
}]
|
|
879
|
+
}], propDecorators: { valueBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "valueBy", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
|
|
880
|
+
|
|
881
|
+
/**
|
|
882
|
+
* Generated bundle index. Do not edit.
|
|
883
|
+
*/
|
|
884
|
+
|
|
885
|
+
export { FOR_STEPPER_CONTEXT, FOR_STEPPER_DEFAULTS, FOR_STEPPER_ITEM_CONTEXT, ForStepper, ForStepperCompletedContent, ForStepperContent, ForStepperIndicator, ForStepperItem, ForStepperList, ForStepperNext, ForStepperPrevious, ForStepperProgress, ForStepperSeparator, ForStepperTrigger, provideForStepperDefaults };
|
|
886
|
+
//# sourceMappingURL=forty-cdk-stepper.mjs.map
|