@styloviz/stepper 0.1.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 StyloViz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # @styloviz/stepper
2
+
3
+ > 6 visual variants — default, pill, minimal, icon, dot, line — all free, plus horizontal/vertical orientation. Linear wizard mode, guard rules, and telemetry are available in the Pro package.
4
+
5
+ Part of the **StyloViz UI Kit** — a premium Angular 21 + Tailwind CSS 4 dashboard component library. Standalone, `OnPush`, strongly typed, dark-mode ready.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @styloviz/core @styloviz/stepper
11
+ ```
12
+
13
+ Requires `@angular/core` and `@angular/common` >= 21. `@styloviz/core` is a peer dependency shared by every component. Prefer everything at once? `@styloviz/all` installs the whole free tier in one command.
14
+
15
+ ## Usage
16
+
17
+ ```ts
18
+ import { SvStepperComponent } from '@styloviz/stepper';
19
+
20
+ @Component({
21
+ standalone: true,
22
+ imports: [SvStepperComponent],
23
+ template: `
24
+ <sv-stepper [items]="steps" [activeIndex]="step" (stepChange)="step = $event" />
25
+ `,
26
+ })
27
+ export class DemoComponent {}
28
+ ```
29
+
30
+ ## Inputs
31
+
32
+ | Input | Type | Default | Description |
33
+ | --- | --- | --- | --- |
34
+ | `items` | `StepperItem[]` | `[]` | The steps to render (label, optional description, state). |
35
+ | `activeIndex` | `number (two-way)` | `0` | 0-based active step index. Supports two-way binding via `[(activeIndex)]`. |
36
+ | `orientation` | `StepperOrientation` | `'horizontal'` | Layout: horizontal or vertical. |
37
+ | `variant` | `StepperVariant` | `'default'` | Visual variant (default, icon, dot, line, pill, minimal…). |
38
+ | `size` | `StepperSize` | `'md'` | Step size: sm, md or lg. |
39
+ | `mode` | `StepperMode` | `'standard'` | Navigation mode: standard sequence, or branch (conditional paths via `activeBranch`). |
40
+ | `interactive` | `boolean` | `true` | Whether users can click steps to navigate. |
41
+ | `showDescriptions` | `boolean` | `true` | Show step descriptions under labels. |
42
+ | `showConnectors` | `boolean` | `true` | Render connectors between steps. |
43
+ | `enableHotkeys` | `boolean` | `false` | Enables advanced keyboard shortcuts for rapid step navigation. |
44
+ | `activeBranch` | `string \| null (two-way)` | `null` | Active branch key used in `mode='branch'`. Supports two-way binding. |
45
+ | `customClass` | `string` | `''` | Extra CSS classes for the root element. |
46
+
47
+ ## Outputs
48
+
49
+ | Output | Type | Description |
50
+ | --- | --- | --- |
51
+ | `stepChange` | `StepperChangeEvent` | Emitted when the active step changes. |
52
+ | `stepClick` | `{ index: number; step: StepperItem }` | Emitted when a step is clicked. |
53
+ | `stepBlocked` | `StepperBlockedEvent` | Emitted when navigation to a step is blocked (e.g. linear validation). |
54
+ | `branchChange` | `StepperBranchChangeEvent` | Emitted when the active branch changes (branch mode). |
55
+
56
+ ## Documentation
57
+
58
+ Full API reference and live demos: https://styloviz.dev/docs/stepper
59
+
60
+ ## License
61
+
62
+ MIT
@@ -0,0 +1,336 @@
1
+ import * as i0 from '@angular/core';
2
+ import { input, model, output, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { NgClass } from '@angular/common';
4
+
5
+ // ─── Component ───────────────────────────────────────────────────────────────
6
+ /**
7
+ * Stepper — multi-step progress and workflow navigation component.
8
+ *
9
+ * Features:
10
+ * - 6 visual variants: default, pill, minimal, icon, dot, line
11
+ * - Horizontal and vertical orientation
12
+ * - Branch mode for conditional workflows (`mode="branch"`, `activeBranch`)
13
+ * - Validation states: complete, current, warning, error, disabled
14
+ * - Two-way active index via `[(activeIndex)]`
15
+ * - Full keyboard and screen-reader friendly semantics
16
+ *
17
+ * For linear (wizard-gated) sequencing, per-step guard rules, and telemetry
18
+ * events, see the Pro Stepper (`@styloviz-pro/stepper`).
19
+ */
20
+ class SvStepperComponent {
21
+ // ── Inputs ────────────────────────────────────────────────────────────────
22
+ /** The steps to render (label, optional description, state). */
23
+ items = input([], ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
24
+ /** 0-based active step index. Supports two-way binding via `[(activeIndex)]`. */
25
+ activeIndex = model(0, ...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
26
+ /** Layout: horizontal or vertical. @default 'horizontal' */
27
+ orientation = input('horizontal', ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
28
+ /** Visual variant (default, icon, dot, line, pill, minimal…). @default 'default' */
29
+ variant = input('default', ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
30
+ /** Step size: sm, md or lg. @default 'md' */
31
+ size = input('md', ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
32
+ /** Navigation mode: standard sequence, or branch (conditional paths via `activeBranch`). @default 'standard' */
33
+ mode = input('standard', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
34
+ /** Whether users can click steps to navigate. */
35
+ interactive = input(true, ...(ngDevMode ? [{ debugName: "interactive" }] : /* istanbul ignore next */ []));
36
+ /** Show step descriptions under labels. */
37
+ showDescriptions = input(true, ...(ngDevMode ? [{ debugName: "showDescriptions" }] : /* istanbul ignore next */ []));
38
+ /** Render connectors between steps. */
39
+ showConnectors = input(true, ...(ngDevMode ? [{ debugName: "showConnectors" }] : /* istanbul ignore next */ []));
40
+ /** Enables advanced keyboard shortcuts for rapid step navigation. */
41
+ enableHotkeys = input(false, ...(ngDevMode ? [{ debugName: "enableHotkeys" }] : /* istanbul ignore next */ []));
42
+ /** Active branch key used in `mode='branch'`. Supports two-way binding. */
43
+ activeBranch = model(null, ...(ngDevMode ? [{ debugName: "activeBranch" }] : /* istanbul ignore next */ []));
44
+ /** Extra CSS classes for the root element. */
45
+ customClass = input('', ...(ngDevMode ? [{ debugName: "customClass" }] : /* istanbul ignore next */ []));
46
+ // ── Outputs ───────────────────────────────────────────────────────────────
47
+ /** Emitted when the active step changes. */
48
+ stepChange = output();
49
+ /** Emitted when a step is clicked. */
50
+ stepClick = output();
51
+ /** Emitted when navigation to a step is blocked (e.g. linear validation). */
52
+ stepBlocked = output();
53
+ /** Emitted when the active branch changes (branch mode). */
54
+ branchChange = output();
55
+ branchShift = signal(false, ...(ngDevMode ? [{ debugName: "branchShift" }] : /* istanbul ignore next */ []));
56
+ // ── Computed ──────────────────────────────────────────────────────────────
57
+ visibleItems = computed(() => {
58
+ const branch = this.activeBranch();
59
+ if (this.mode() !== 'branch')
60
+ return this.items();
61
+ return this.items().filter(item => !item.branch || item.branch === branch);
62
+ }, ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
63
+ normalizedSteps = computed(() => {
64
+ const current = this.clampedActiveIndex();
65
+ return this.visibleItems().map((item, index) => {
66
+ const state = this.resolveState(item, index, current);
67
+ return { index, item, state };
68
+ });
69
+ }, ...(ngDevMode ? [{ debugName: "normalizedSteps" }] : /* istanbul ignore next */ []));
70
+ clampedActiveIndex = computed(() => {
71
+ const max = this.visibleItems().length - 1;
72
+ if (max < 0)
73
+ return 0;
74
+ return Math.max(0, Math.min(this.activeIndex(), max));
75
+ }, ...(ngDevMode ? [{ debugName: "clampedActiveIndex" }] : /* istanbul ignore next */ []));
76
+ rootClasses = computed(() => {
77
+ const isVertical = this.orientation() === 'vertical';
78
+ const base = isVertical ? 'flex flex-col gap-0' : 'flex items-start';
79
+ return [base, this.customClass()].filter(Boolean).join(' ');
80
+ }, ...(ngDevMode ? [{ debugName: "rootClasses" }] : /* istanbul ignore next */ []));
81
+ // ── State helpers ─────────────────────────────────────────────────────────
82
+ resolveState(item, index, current) {
83
+ if (item.status)
84
+ return item.status;
85
+ if (index < current)
86
+ return 'complete';
87
+ if (index === current)
88
+ return 'current';
89
+ return 'pending';
90
+ }
91
+ isClickable(step) {
92
+ if (!this.interactive())
93
+ return false;
94
+ return this.blockReason(step) === null;
95
+ }
96
+ blockReason(step) {
97
+ if (!this.interactive())
98
+ return null;
99
+ if (step.state === 'disabled')
100
+ return 'disabled';
101
+ return null;
102
+ }
103
+ onSelect(step) {
104
+ this.stepClick.emit({ index: step.index, step: step.item });
105
+ const reason = this.blockReason(step);
106
+ if (reason) {
107
+ this.stepBlocked.emit({ index: step.index, step: step.item, reason });
108
+ return;
109
+ }
110
+ if (step.item.selectsBranch && this.activeBranch() !== step.item.selectsBranch) {
111
+ this.activeBranch.set(step.item.selectsBranch);
112
+ this.branchChange.emit({ branch: step.item.selectsBranch, step: step.item });
113
+ this.triggerBranchShift();
114
+ }
115
+ const previous = this.clampedActiveIndex();
116
+ if (previous === step.index)
117
+ return;
118
+ this.activeIndex.set(step.index);
119
+ this.stepChange.emit({ previousIndex: previous, currentIndex: step.index, step: step.item });
120
+ }
121
+ onStepKeydown(event, step) {
122
+ if (!this.enableHotkeys())
123
+ return;
124
+ const list = this.normalizedSteps();
125
+ if (!list.length)
126
+ return;
127
+ const key = event.key;
128
+ if (/^[1-9]$/.test(key)) {
129
+ const target = Number(key) - 1;
130
+ if (target < list.length) {
131
+ event.preventDefault();
132
+ this.onSelect(list[target]);
133
+ this.focusStep(list[target].item.id);
134
+ }
135
+ return;
136
+ }
137
+ const isVertical = this.orientation() === 'vertical';
138
+ let target;
139
+ if ((isVertical && key === 'ArrowDown') || (!isVertical && key === 'ArrowRight')) {
140
+ target = list[Math.min(list.length - 1, step.index + 1)];
141
+ }
142
+ if ((isVertical && key === 'ArrowUp') || (!isVertical && key === 'ArrowLeft')) {
143
+ target = list[Math.max(0, step.index - 1)];
144
+ }
145
+ if (key === 'Home')
146
+ target = list[0];
147
+ if (key === 'End')
148
+ target = list[list.length - 1];
149
+ if (target) {
150
+ event.preventDefault();
151
+ this.onSelect(target);
152
+ this.focusStep(target.item.id);
153
+ }
154
+ }
155
+ // ── Class helpers ─────────────────────────────────────────────────────────
156
+ indicatorClass(step) {
157
+ const size = this.indicatorSizeClass();
158
+ const variant = this.variant();
159
+ const base = [
160
+ 'inline-flex shrink-0 items-center justify-center font-semibold transition-colors duration-200',
161
+ size,
162
+ this.shapeClass(variant),
163
+ ];
164
+ switch (step.state) {
165
+ case 'complete':
166
+ base.push('bg-accent-500 text-white dark:bg-accent-500');
167
+ break;
168
+ case 'current':
169
+ base.push('bg-primary-600 text-white ring-4 ring-primary-100 dark:bg-primary-500 dark:ring-primary-900/50');
170
+ break;
171
+ case 'error':
172
+ base.push('bg-danger-500 text-white dark:bg-danger-500');
173
+ break;
174
+ case 'warning':
175
+ base.push('bg-warning-500 text-white dark:bg-warning-500');
176
+ break;
177
+ case 'disabled':
178
+ base.push('bg-gray-200 text-gray-400 dark:bg-gray-800 dark:text-gray-500');
179
+ break;
180
+ default:
181
+ base.push('bg-white text-gray-500 ring-1 ring-gray-300 dark:bg-gray-900 dark:text-gray-400 dark:ring-gray-700');
182
+ break;
183
+ }
184
+ if (variant === 'minimal') {
185
+ base.push('ring-0');
186
+ if (step.state === 'current') {
187
+ base.push('bg-primary-500');
188
+ }
189
+ }
190
+ return base.join(' ');
191
+ }
192
+ connectorClass(step) {
193
+ if (step.state === 'complete')
194
+ return 'bg-accent-400 dark:bg-accent-500';
195
+ if (step.state === 'error')
196
+ return 'bg-danger-300 dark:bg-danger-500/70';
197
+ if (step.state === 'warning')
198
+ return 'bg-warning-300 dark:bg-warning-500/70';
199
+ return 'bg-gray-200 dark:bg-gray-700';
200
+ }
201
+ /** Connector thickness — `line` renders a bold, continuous progress bar; every other variant uses a thin hairline. */
202
+ connectorSizeClass() {
203
+ return this.variant() === 'line' ? 'h-1' : 'h-px';
204
+ }
205
+ verticalRowPaddingClass() {
206
+ if (this.size() === 'sm')
207
+ return 'pl-11';
208
+ if (this.size() === 'lg')
209
+ return 'pl-16';
210
+ return 'pl-14';
211
+ }
212
+ verticalConnectorLayoutClass() {
213
+ const width = this.variant() === 'line' ? 'w-1' : 'w-0.5';
214
+ if (this.size() === 'sm')
215
+ return `left-[13px] top-7 h-[calc(100%-1rem)] ${width}`;
216
+ if (this.size() === 'lg')
217
+ return `left-[19px] top-10 h-[calc(100%-1.35rem)] ${width}`;
218
+ return `left-[15px] top-8 h-[calc(100%-1.15rem)] ${width}`;
219
+ }
220
+ labelClass(step) {
221
+ const base = [this.labelSizeClass(), 'font-medium tracking-tight'];
222
+ switch (step.state) {
223
+ case 'current':
224
+ base.push('text-gray-900 dark:text-white');
225
+ break;
226
+ case 'complete':
227
+ base.push('text-accent-700 dark:text-accent-400');
228
+ break;
229
+ case 'error':
230
+ base.push('text-danger-700 dark:text-danger-400');
231
+ break;
232
+ case 'warning':
233
+ base.push('text-warning-700 dark:text-warning-400');
234
+ break;
235
+ case 'disabled':
236
+ base.push('text-gray-500 dark:text-gray-400');
237
+ break;
238
+ default:
239
+ base.push('text-gray-600 dark:text-gray-300');
240
+ break;
241
+ }
242
+ return base.join(' ');
243
+ }
244
+ descriptionClass(step) {
245
+ const base = [this.descriptionSizeClass(), 'mt-1 leading-relaxed'];
246
+ if (step.state === 'disabled') {
247
+ base.push('text-gray-500 dark:text-gray-400');
248
+ }
249
+ else {
250
+ base.push('text-gray-500 dark:text-gray-400');
251
+ }
252
+ return base.join(' ');
253
+ }
254
+ shapeClass(variant) {
255
+ switch (variant) {
256
+ case 'pill':
257
+ return 'rounded-xl px-2.5';
258
+ case 'dot':
259
+ case 'minimal':
260
+ return 'rounded-full';
261
+ default:
262
+ return 'rounded-full';
263
+ }
264
+ }
265
+ indicatorSizeClass() {
266
+ const size = this.size();
267
+ const variant = this.variant();
268
+ if (variant === 'dot' || variant === 'minimal') {
269
+ return size === 'sm' ? 'h-2.5 w-2.5' : size === 'lg' ? 'h-4 w-4' : 'h-3 w-3';
270
+ }
271
+ if (variant === 'pill') {
272
+ return size === 'sm' ? 'h-7 min-w-7 text-xs' : size === 'lg' ? 'h-10 min-w-10 text-base' : 'h-8 min-w-8 text-sm';
273
+ }
274
+ return size === 'sm' ? 'h-7 w-7 text-xs' : size === 'lg' ? 'h-10 w-10 text-base' : 'h-8 w-8 text-sm';
275
+ }
276
+ labelSizeClass() {
277
+ return this.size() === 'sm' ? 'text-xs' : this.size() === 'lg' ? 'text-base' : 'text-sm';
278
+ }
279
+ descriptionSizeClass() {
280
+ return this.size() === 'sm' ? 'text-[11px]' : 'text-xs';
281
+ }
282
+ // ── Icon/text helpers ─────────────────────────────────────────────────────
283
+ shouldShowText(_step) {
284
+ return !(this.variant() === 'dot' || this.variant() === 'minimal');
285
+ }
286
+ shouldShowIcon(step) {
287
+ if (step.state === 'complete' || step.state === 'error' || step.state === 'warning')
288
+ return true;
289
+ return this.variant() === 'icon' && !!step.item.iconPath;
290
+ }
291
+ iconPath(step) {
292
+ if (step.state === 'complete')
293
+ return 'M5 13l4 4L19 7';
294
+ if (step.state === 'error')
295
+ return 'M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z';
296
+ if (step.state === 'warning')
297
+ return 'M12 8v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z';
298
+ return step.item.iconPath ?? '';
299
+ }
300
+ indicatorText(step) {
301
+ return String(step.index + 1);
302
+ }
303
+ shouldShowBadge(step) {
304
+ return !!step.item.badge || !!step.item.selectsBranch;
305
+ }
306
+ badgeText(step) {
307
+ return step.item.badge ?? step.item.selectsBranch ?? '';
308
+ }
309
+ branchShiftClass() {
310
+ return this.branchShift() ? 'stp-branch-shift' : '';
311
+ }
312
+ triggerBranchShift() {
313
+ this.branchShift.set(false);
314
+ queueMicrotask(() => {
315
+ this.branchShift.set(true);
316
+ setTimeout(() => this.branchShift.set(false), 280);
317
+ });
318
+ }
319
+ focusStep(id) {
320
+ const el = document.getElementById(`stepper-item-${id}`);
321
+ el?.focus();
322
+ }
323
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SvStepperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
324
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: SvStepperComponent, isStandalone: true, selector: "sv-stepper", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, activeIndex: { classPropertyName: "activeIndex", publicName: "activeIndex", isSignal: true, isRequired: false, transformFunction: null }, orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, interactive: { classPropertyName: "interactive", publicName: "interactive", isSignal: true, isRequired: false, transformFunction: null }, showDescriptions: { classPropertyName: "showDescriptions", publicName: "showDescriptions", isSignal: true, isRequired: false, transformFunction: null }, showConnectors: { classPropertyName: "showConnectors", publicName: "showConnectors", isSignal: true, isRequired: false, transformFunction: null }, enableHotkeys: { classPropertyName: "enableHotkeys", publicName: "enableHotkeys", isSignal: true, isRequired: false, transformFunction: null }, activeBranch: { classPropertyName: "activeBranch", publicName: "activeBranch", isSignal: true, isRequired: false, transformFunction: null }, customClass: { classPropertyName: "customClass", publicName: "customClass", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { activeIndex: "activeIndexChange", activeBranch: "activeBranchChange", stepChange: "stepChange", stepClick: "stepClick", stepBlocked: "stepBlocked", branchChange: "branchChange" }, ngImport: i0, template: "<div [ngClass]=\"[rootClasses(), branchShiftClass()]\" role=\"list\" aria-label=\"Progress steps\">\n\n @if (orientation() === 'horizontal') {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"flex min-w-0 flex-1\" role=\"listitem\">\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex min-w-0 flex-1 flex-col items-start gap-2 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <div class=\"flex w-full items-center\">\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (showConnectors() && !last) {\n <span\n class=\"ml-3 flex-1 rounded-full\"\n [ngClass]=\"[connectorClass(step), connectorSizeClass()]\"\n aria-hidden=\"true\"\n ></span>\n }\n </div>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n } @else {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"relative pb-6 last:pb-0\" [ngClass]=\"verticalRowPaddingClass()\" role=\"listitem\">\n @if (showConnectors() && !last) {\n <span\n class=\"absolute rounded-full\"\n [ngClass]=\"[verticalConnectorLayoutClass(), connectorClass(step)]\"\n aria-hidden=\"true\"\n ></span>\n }\n\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex w-full items-start gap-3 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n }\n\n</div>\n", styles: ["@keyframes stepper-branch-shift{0%{opacity:.55;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}.stp-branch-shift{animation:stepper-branch-shift .26s cubic-bezier(.22,1,.36,1)}\n"], dependencies: [{ kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
325
+ }
326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: SvStepperComponent, decorators: [{
327
+ type: Component,
328
+ args: [{ selector: 'sv-stepper', standalone: true, imports: [NgClass], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [ngClass]=\"[rootClasses(), branchShiftClass()]\" role=\"list\" aria-label=\"Progress steps\">\n\n @if (orientation() === 'horizontal') {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"flex min-w-0 flex-1\" role=\"listitem\">\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex min-w-0 flex-1 flex-col items-start gap-2 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <div class=\"flex w-full items-center\">\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (showConnectors() && !last) {\n <span\n class=\"ml-3 flex-1 rounded-full\"\n [ngClass]=\"[connectorClass(step), connectorSizeClass()]\"\n aria-hidden=\"true\"\n ></span>\n }\n </div>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n } @else {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"relative pb-6 last:pb-0\" [ngClass]=\"verticalRowPaddingClass()\" role=\"listitem\">\n @if (showConnectors() && !last) {\n <span\n class=\"absolute rounded-full\"\n [ngClass]=\"[verticalConnectorLayoutClass(), connectorClass(step)]\"\n aria-hidden=\"true\"\n ></span>\n }\n\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex w-full items-start gap-3 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n }\n\n</div>\n", styles: ["@keyframes stepper-branch-shift{0%{opacity:.55;transform:translateY(3px)}to{opacity:1;transform:translateY(0)}}.stp-branch-shift{animation:stepper-branch-shift .26s cubic-bezier(.22,1,.36,1)}\n"] }]
329
+ }], propDecorators: { items: [{ type: i0.Input, args: [{ isSignal: true, alias: "items", required: false }] }], activeIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeIndex", required: false }] }, { type: i0.Output, args: ["activeIndexChange"] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], showDescriptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "showDescriptions", required: false }] }], showConnectors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showConnectors", required: false }] }], enableHotkeys: [{ type: i0.Input, args: [{ isSignal: true, alias: "enableHotkeys", required: false }] }], activeBranch: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeBranch", required: false }] }, { type: i0.Output, args: ["activeBranchChange"] }], customClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "customClass", required: false }] }], stepChange: [{ type: i0.Output, args: ["stepChange"] }], stepClick: [{ type: i0.Output, args: ["stepClick"] }], stepBlocked: [{ type: i0.Output, args: ["stepBlocked"] }], branchChange: [{ type: i0.Output, args: ["branchChange"] }] } });
330
+
331
+ /**
332
+ * Generated bundle index. Do not edit.
333
+ */
334
+
335
+ export { SvStepperComponent };
336
+ //# sourceMappingURL=styloviz-stepper.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styloviz-stepper.mjs","sources":["../../../../projects/stepper-free/src/lib/stepper.component.ts","../../../../projects/stepper-free/src/lib/stepper.component.html","../../../../projects/stepper-free/src/styloviz-stepper.ts"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n model,\n output,\n signal,\n} from '@angular/core';\nimport { NgClass } from '@angular/common';\n\n// ─── Types ────────────────────────────────────────────────────────────────────\n\nexport type StepperOrientation = 'horizontal' | 'vertical';\nexport type StepperVariant = 'default' | 'pill' | 'minimal' | 'icon' | 'dot' | 'line';\nexport type StepperSize = 'sm' | 'md' | 'lg';\nexport type StepperMode = 'standard' | 'branch';\n\nexport type StepperStepStatus =\n | 'pending'\n | 'current'\n | 'complete'\n | 'error'\n | 'warning'\n | 'disabled';\n\nexport interface StepperItem {\n id: string;\n label: string;\n description?: string;\n iconPath?: string;\n optional?: boolean;\n status?: StepperStepStatus;\n /** Branch key to which this step belongs (advanced branch mode). */\n branch?: string;\n /** Selecting this step sets the active branch to the provided value. */\n selectsBranch?: string;\n /** Optional short badge next to the step label. */\n badge?: string;\n}\n\nexport interface StepperChangeEvent {\n previousIndex: number;\n currentIndex: number;\n step: StepperItem;\n}\n\nexport interface StepperBlockedEvent {\n index: number;\n step: StepperItem;\n reason: 'disabled';\n}\n\nexport interface StepperBranchChangeEvent {\n branch: string | null;\n step: StepperItem;\n}\n\n\ninterface NormalizedStep {\n index: number;\n item: StepperItem;\n state: StepperStepStatus;\n}\n\n// ─── Component ───────────────────────────────────────────────────────────────\n\n/**\n * Stepper — multi-step progress and workflow navigation component.\n *\n * Features:\n * - 6 visual variants: default, pill, minimal, icon, dot, line\n * - Horizontal and vertical orientation\n * - Branch mode for conditional workflows (`mode=\"branch\"`, `activeBranch`)\n * - Validation states: complete, current, warning, error, disabled\n * - Two-way active index via `[(activeIndex)]`\n * - Full keyboard and screen-reader friendly semantics\n *\n * For linear (wizard-gated) sequencing, per-step guard rules, and telemetry\n * events, see the Pro Stepper (`@styloviz-pro/stepper`).\n */\n@Component({\n selector: 'sv-stepper',\n standalone: true,\n imports: [NgClass],\n templateUrl: './stepper.component.html',\n styleUrl: './stepper.component.css',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class SvStepperComponent {\n // ── Inputs ────────────────────────────────────────────────────────────────\n\n /** The steps to render (label, optional description, state). */\n items = input<StepperItem[]>([]);\n\n /** 0-based active step index. Supports two-way binding via `[(activeIndex)]`. */\n activeIndex = model<number>(0);\n\n /** Layout: horizontal or vertical. @default 'horizontal' */\n orientation = input<StepperOrientation>('horizontal');\n /** Visual variant (default, icon, dot, line, pill, minimal…). @default 'default' */\n variant = input<StepperVariant>('default');\n /** Step size: sm, md or lg. @default 'md' */\n size = input<StepperSize>('md');\n /** Navigation mode: standard sequence, or branch (conditional paths via `activeBranch`). @default 'standard' */\n mode = input<StepperMode>('standard');\n\n\n /** Whether users can click steps to navigate. */\n interactive = input<boolean>(true);\n\n /** Show step descriptions under labels. */\n showDescriptions = input<boolean>(true);\n\n /** Render connectors between steps. */\n showConnectors = input<boolean>(true);\n\n /** Enables advanced keyboard shortcuts for rapid step navigation. */\n enableHotkeys = input<boolean>(false);\n\n\n /** Active branch key used in `mode='branch'`. Supports two-way binding. */\n activeBranch = model<string | null>(null);\n\n /** Extra CSS classes for the root element. */\n customClass = input<string>('');\n\n // ── Outputs ───────────────────────────────────────────────────────────────\n\n /** Emitted when the active step changes. */\n stepChange = output<StepperChangeEvent>();\n /** Emitted when a step is clicked. */\n stepClick = output<{ index: number; step: StepperItem }>();\n /** Emitted when navigation to a step is blocked (e.g. linear validation). */\n stepBlocked = output<StepperBlockedEvent>();\n /** Emitted when the active branch changes (branch mode). */\n branchChange = output<StepperBranchChangeEvent>();\n\n private readonly branchShift = signal(false);\n\n // ── Computed ──────────────────────────────────────────────────────────────\n\n visibleItems = computed<StepperItem[]>(() => {\n const branch = this.activeBranch();\n if (this.mode() !== 'branch') return this.items();\n return this.items().filter(item => !item.branch || item.branch === branch);\n });\n\n normalizedSteps = computed<NormalizedStep[]>(() => {\n const current = this.clampedActiveIndex();\n return this.visibleItems().map((item, index) => {\n const state = this.resolveState(item, index, current);\n return { index, item, state };\n });\n });\n\n clampedActiveIndex = computed(() => {\n const max = this.visibleItems().length - 1;\n if (max < 0) return 0;\n return Math.max(0, Math.min(this.activeIndex(), max));\n });\n\n rootClasses = computed(() => {\n const isVertical = this.orientation() === 'vertical';\n const base = isVertical ? 'flex flex-col gap-0' : 'flex items-start';\n return [base, this.customClass()].filter(Boolean).join(' ');\n });\n\n // ── State helpers ─────────────────────────────────────────────────────────\n\n private resolveState(item: StepperItem, index: number, current: number): StepperStepStatus {\n if (item.status) return item.status;\n if (index < current) return 'complete';\n if (index === current) return 'current';\n return 'pending';\n }\n\n isClickable(step: NormalizedStep): boolean {\n if (!this.interactive()) return false;\n return this.blockReason(step) === null;\n }\n\n private blockReason(step: NormalizedStep): 'disabled' | null {\n if (!this.interactive()) return null;\n if (step.state === 'disabled') return 'disabled';\n\n return null;\n }\n\n onSelect(step: NormalizedStep): void {\n this.stepClick.emit({ index: step.index, step: step.item });\n\n const reason = this.blockReason(step);\n if (reason) {\n this.stepBlocked.emit({ index: step.index, step: step.item, reason });\n return;\n }\n\n if (step.item.selectsBranch && this.activeBranch() !== step.item.selectsBranch) {\n this.activeBranch.set(step.item.selectsBranch);\n this.branchChange.emit({ branch: step.item.selectsBranch, step: step.item });\n this.triggerBranchShift();\n }\n\n const previous = this.clampedActiveIndex();\n if (previous === step.index) return;\n\n this.activeIndex.set(step.index);\n this.stepChange.emit({ previousIndex: previous, currentIndex: step.index, step: step.item });\n }\n\n onStepKeydown(event: KeyboardEvent, step: NormalizedStep): void {\n if (!this.enableHotkeys()) return;\n\n const list = this.normalizedSteps();\n if (!list.length) return;\n\n const key = event.key;\n\n if (/^[1-9]$/.test(key)) {\n const target = Number(key) - 1;\n if (target < list.length) {\n event.preventDefault();\n this.onSelect(list[target]);\n this.focusStep(list[target].item.id);\n }\n return;\n }\n\n const isVertical = this.orientation() === 'vertical';\n let target: NormalizedStep | undefined;\n\n if ((isVertical && key === 'ArrowDown') || (!isVertical && key === 'ArrowRight')) {\n target = list[Math.min(list.length - 1, step.index + 1)];\n }\n\n if ((isVertical && key === 'ArrowUp') || (!isVertical && key === 'ArrowLeft')) {\n target = list[Math.max(0, step.index - 1)];\n }\n\n if (key === 'Home') target = list[0];\n if (key === 'End') target = list[list.length - 1];\n\n if (target) {\n event.preventDefault();\n this.onSelect(target);\n this.focusStep(target.item.id);\n }\n }\n\n // ── Class helpers ─────────────────────────────────────────────────────────\n\n indicatorClass(step: NormalizedStep): string {\n const size = this.indicatorSizeClass();\n const variant = this.variant();\n\n const base = [\n 'inline-flex shrink-0 items-center justify-center font-semibold transition-colors duration-200',\n size,\n this.shapeClass(variant),\n ];\n\n switch (step.state) {\n case 'complete':\n base.push('bg-accent-500 text-white dark:bg-accent-500');\n break;\n case 'current':\n base.push('bg-primary-600 text-white ring-4 ring-primary-100 dark:bg-primary-500 dark:ring-primary-900/50');\n break;\n case 'error':\n base.push('bg-danger-500 text-white dark:bg-danger-500');\n break;\n case 'warning':\n base.push('bg-warning-500 text-white dark:bg-warning-500');\n break;\n case 'disabled':\n base.push('bg-gray-200 text-gray-400 dark:bg-gray-800 dark:text-gray-500');\n break;\n default:\n base.push('bg-white text-gray-500 ring-1 ring-gray-300 dark:bg-gray-900 dark:text-gray-400 dark:ring-gray-700');\n break;\n }\n\n if (variant === 'minimal') {\n base.push('ring-0');\n if (step.state === 'current') {\n base.push('bg-primary-500');\n }\n }\n\n return base.join(' ');\n }\n\n connectorClass(step: NormalizedStep): string {\n if (step.state === 'complete') return 'bg-accent-400 dark:bg-accent-500';\n if (step.state === 'error') return 'bg-danger-300 dark:bg-danger-500/70';\n if (step.state === 'warning') return 'bg-warning-300 dark:bg-warning-500/70';\n return 'bg-gray-200 dark:bg-gray-700';\n }\n\n /** Connector thickness — `line` renders a bold, continuous progress bar; every other variant uses a thin hairline. */\n connectorSizeClass(): string {\n return this.variant() === 'line' ? 'h-1' : 'h-px';\n }\n\n verticalRowPaddingClass(): string {\n if (this.size() === 'sm') return 'pl-11';\n if (this.size() === 'lg') return 'pl-16';\n return 'pl-14';\n }\n\n verticalConnectorLayoutClass(): string {\n const width = this.variant() === 'line' ? 'w-1' : 'w-0.5';\n if (this.size() === 'sm') return `left-[13px] top-7 h-[calc(100%-1rem)] ${width}`;\n if (this.size() === 'lg') return `left-[19px] top-10 h-[calc(100%-1.35rem)] ${width}`;\n return `left-[15px] top-8 h-[calc(100%-1.15rem)] ${width}`;\n }\n\n labelClass(step: NormalizedStep): string {\n const base = [this.labelSizeClass(), 'font-medium tracking-tight'];\n\n switch (step.state) {\n case 'current':\n base.push('text-gray-900 dark:text-white');\n break;\n case 'complete':\n base.push('text-accent-700 dark:text-accent-400');\n break;\n case 'error':\n base.push('text-danger-700 dark:text-danger-400');\n break;\n case 'warning':\n base.push('text-warning-700 dark:text-warning-400');\n break;\n case 'disabled':\n base.push('text-gray-500 dark:text-gray-400');\n break;\n default:\n base.push('text-gray-600 dark:text-gray-300');\n break;\n }\n\n return base.join(' ');\n }\n\n descriptionClass(step: NormalizedStep): string {\n const base = [this.descriptionSizeClass(), 'mt-1 leading-relaxed'];\n\n if (step.state === 'disabled') {\n base.push('text-gray-500 dark:text-gray-400');\n } else {\n base.push('text-gray-500 dark:text-gray-400');\n }\n\n return base.join(' ');\n }\n\n private shapeClass(variant: StepperVariant): string {\n switch (variant) {\n case 'pill':\n return 'rounded-xl px-2.5';\n case 'dot':\n case 'minimal':\n return 'rounded-full';\n default:\n return 'rounded-full';\n }\n }\n\n private indicatorSizeClass(): string {\n const size = this.size();\n const variant = this.variant();\n\n if (variant === 'dot' || variant === 'minimal') {\n return size === 'sm' ? 'h-2.5 w-2.5' : size === 'lg' ? 'h-4 w-4' : 'h-3 w-3';\n }\n\n if (variant === 'pill') {\n return size === 'sm' ? 'h-7 min-w-7 text-xs' : size === 'lg' ? 'h-10 min-w-10 text-base' : 'h-8 min-w-8 text-sm';\n }\n\n return size === 'sm' ? 'h-7 w-7 text-xs' : size === 'lg' ? 'h-10 w-10 text-base' : 'h-8 w-8 text-sm';\n }\n\n private labelSizeClass(): string {\n return this.size() === 'sm' ? 'text-xs' : this.size() === 'lg' ? 'text-base' : 'text-sm';\n }\n\n private descriptionSizeClass(): string {\n return this.size() === 'sm' ? 'text-[11px]' : 'text-xs';\n }\n\n // ── Icon/text helpers ─────────────────────────────────────────────────────\n\n shouldShowText(_step: NormalizedStep): boolean {\n return !(this.variant() === 'dot' || this.variant() === 'minimal');\n }\n\n shouldShowIcon(step: NormalizedStep): boolean {\n if (step.state === 'complete' || step.state === 'error' || step.state === 'warning') return true;\n return this.variant() === 'icon' && !!step.item.iconPath;\n }\n\n iconPath(step: NormalizedStep): string {\n if (step.state === 'complete') return 'M5 13l4 4L19 7';\n if (step.state === 'error') return 'M12 9v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z';\n if (step.state === 'warning') return 'M12 8v4m0 4h.01M10.29 3.86 1.82 18a2 2 0 001.71 3h16.94a2 2 0 001.71-3L13.71 3.86a2 2 0 00-3.42 0z';\n return step.item.iconPath ?? '';\n }\n\n indicatorText(step: NormalizedStep): string {\n return String(step.index + 1);\n }\n\n shouldShowBadge(step: NormalizedStep): boolean {\n return !!step.item.badge || !!step.item.selectsBranch;\n }\n\n badgeText(step: NormalizedStep): string {\n return step.item.badge ?? step.item.selectsBranch ?? '';\n }\n\n branchShiftClass(): string {\n return this.branchShift() ? 'stp-branch-shift' : '';\n }\n\n private triggerBranchShift(): void {\n this.branchShift.set(false);\n queueMicrotask(() => {\n this.branchShift.set(true);\n setTimeout(() => this.branchShift.set(false), 280);\n });\n }\n\n private focusStep(id: string): void {\n const el = document.getElementById(`stepper-item-${id}`);\n el?.focus();\n }\n}\n","<div [ngClass]=\"[rootClasses(), branchShiftClass()]\" role=\"list\" aria-label=\"Progress steps\">\n\n @if (orientation() === 'horizontal') {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"flex min-w-0 flex-1\" role=\"listitem\">\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex min-w-0 flex-1 flex-col items-start gap-2 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <div class=\"flex w-full items-center\">\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (showConnectors() && !last) {\n <span\n class=\"ml-3 flex-1 rounded-full\"\n [ngClass]=\"[connectorClass(step), connectorSizeClass()]\"\n aria-hidden=\"true\"\n ></span>\n }\n </div>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n } @else {\n @for (step of normalizedSteps(); track step.item.id; let last = $last) {\n <div class=\"relative pb-6 last:pb-0\" [ngClass]=\"verticalRowPaddingClass()\" role=\"listitem\">\n @if (showConnectors() && !last) {\n <span\n class=\"absolute rounded-full\"\n [ngClass]=\"[verticalConnectorLayoutClass(), connectorClass(step)]\"\n aria-hidden=\"true\"\n ></span>\n }\n\n <button\n [id]=\"'stepper-item-' + step.item.id\"\n type=\"button\"\n class=\"group flex w-full items-start gap-3 text-left\"\n [class.cursor-pointer]=\"isClickable(step)\"\n [class.cursor-not-allowed]=\"!isClickable(step)\"\n [class.opacity-70]=\"!isClickable(step)\"\n [attr.aria-current]=\"step.index === clampedActiveIndex() ? 'step' : null\"\n [attr.aria-disabled]=\"!isClickable(step)\"\n [attr.tabindex]=\"step.index === clampedActiveIndex() ? 0 : -1\"\n (click)=\"onSelect(step)\"\n (keydown)=\"onStepKeydown($event, step)\"\n >\n <span [ngClass]=\"indicatorClass(step)\" aria-hidden=\"true\">\n @if (shouldShowIcon(step)) {\n <svg class=\"h-[65%] w-[65%]\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <path [attr.d]=\"iconPath(step)\" />\n </svg>\n } @else if (shouldShowText(step)) {\n {{ indicatorText(step) }}\n }\n </span>\n\n @if (variant() !== 'dot') {\n <span class=\"min-w-0 pt-0.5\">\n <span [ngClass]=\"labelClass(step)\">{{ step.item.label }}</span>\n @if (shouldShowBadge(step)) {\n <span class=\"ml-2 inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-gray-600 dark:bg-gray-800 dark:text-gray-300\">\n {{ badgeText(step) }}\n </span>\n }\n @if (step.item.optional) {\n <span class=\"ml-2 text-[10px] uppercase tracking-wide text-gray-500 dark:text-gray-400\">Optional</span>\n }\n @if (showDescriptions() && step.item.description) {\n <p [ngClass]=\"descriptionClass(step)\">{{ step.item.description }}</p>\n }\n </span>\n }\n </button>\n </div>\n }\n }\n\n</div>\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@styloviz/stepper",
3
+ "version": "0.1.1",
4
+ "description": "6 visual variants — default, pill, minimal, icon, dot, line — all free, plus horizontal/vertical orientation. Linear wizard mode, guard rules, and telemetry are available in the Pro package.",
5
+ "license": "MIT",
6
+ "author": "sazzad-bs23",
7
+ "homepage": "https://styloviz.dev",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/rhossain/angular-tailwind-dashboard-components.git",
11
+ "directory": "projects/stepper-free"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/rhossain/angular-tailwind-dashboard-components/issues"
15
+ },
16
+ "peerDependencies": {
17
+ "@angular/common": ">=21.0.0",
18
+ "@angular/core": ">=21.0.0"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "engines": {
24
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
25
+ },
26
+ "sideEffects": false,
27
+ "module": "fesm2022/styloviz-stepper.mjs",
28
+ "typings": "types/styloviz-stepper.d.ts",
29
+ "exports": {
30
+ "./package.json": {
31
+ "default": "./package.json"
32
+ },
33
+ ".": {
34
+ "types": "./types/styloviz-stepper.d.ts",
35
+ "default": "./fesm2022/styloviz-stepper.mjs"
36
+ }
37
+ },
38
+ "type": "module",
39
+ "dependencies": {
40
+ "tslib": "^2.3.0"
41
+ }
42
+ }
@@ -0,0 +1,127 @@
1
+ import * as _angular_core from '@angular/core';
2
+
3
+ type StepperOrientation = 'horizontal' | 'vertical';
4
+ type StepperVariant = 'default' | 'pill' | 'minimal' | 'icon' | 'dot' | 'line';
5
+ type StepperSize = 'sm' | 'md' | 'lg';
6
+ type StepperMode = 'standard' | 'branch';
7
+ type StepperStepStatus = 'pending' | 'current' | 'complete' | 'error' | 'warning' | 'disabled';
8
+ interface StepperItem {
9
+ id: string;
10
+ label: string;
11
+ description?: string;
12
+ iconPath?: string;
13
+ optional?: boolean;
14
+ status?: StepperStepStatus;
15
+ /** Branch key to which this step belongs (advanced branch mode). */
16
+ branch?: string;
17
+ /** Selecting this step sets the active branch to the provided value. */
18
+ selectsBranch?: string;
19
+ /** Optional short badge next to the step label. */
20
+ badge?: string;
21
+ }
22
+ interface StepperChangeEvent {
23
+ previousIndex: number;
24
+ currentIndex: number;
25
+ step: StepperItem;
26
+ }
27
+ interface StepperBlockedEvent {
28
+ index: number;
29
+ step: StepperItem;
30
+ reason: 'disabled';
31
+ }
32
+ interface StepperBranchChangeEvent {
33
+ branch: string | null;
34
+ step: StepperItem;
35
+ }
36
+ interface NormalizedStep {
37
+ index: number;
38
+ item: StepperItem;
39
+ state: StepperStepStatus;
40
+ }
41
+ /**
42
+ * Stepper — multi-step progress and workflow navigation component.
43
+ *
44
+ * Features:
45
+ * - 6 visual variants: default, pill, minimal, icon, dot, line
46
+ * - Horizontal and vertical orientation
47
+ * - Branch mode for conditional workflows (`mode="branch"`, `activeBranch`)
48
+ * - Validation states: complete, current, warning, error, disabled
49
+ * - Two-way active index via `[(activeIndex)]`
50
+ * - Full keyboard and screen-reader friendly semantics
51
+ *
52
+ * For linear (wizard-gated) sequencing, per-step guard rules, and telemetry
53
+ * events, see the Pro Stepper (`@styloviz-pro/stepper`).
54
+ */
55
+ declare class SvStepperComponent {
56
+ /** The steps to render (label, optional description, state). */
57
+ items: _angular_core.InputSignal<StepperItem[]>;
58
+ /** 0-based active step index. Supports two-way binding via `[(activeIndex)]`. */
59
+ activeIndex: _angular_core.ModelSignal<number>;
60
+ /** Layout: horizontal or vertical. @default 'horizontal' */
61
+ orientation: _angular_core.InputSignal<StepperOrientation>;
62
+ /** Visual variant (default, icon, dot, line, pill, minimal…). @default 'default' */
63
+ variant: _angular_core.InputSignal<StepperVariant>;
64
+ /** Step size: sm, md or lg. @default 'md' */
65
+ size: _angular_core.InputSignal<StepperSize>;
66
+ /** Navigation mode: standard sequence, or branch (conditional paths via `activeBranch`). @default 'standard' */
67
+ mode: _angular_core.InputSignal<StepperMode>;
68
+ /** Whether users can click steps to navigate. */
69
+ interactive: _angular_core.InputSignal<boolean>;
70
+ /** Show step descriptions under labels. */
71
+ showDescriptions: _angular_core.InputSignal<boolean>;
72
+ /** Render connectors between steps. */
73
+ showConnectors: _angular_core.InputSignal<boolean>;
74
+ /** Enables advanced keyboard shortcuts for rapid step navigation. */
75
+ enableHotkeys: _angular_core.InputSignal<boolean>;
76
+ /** Active branch key used in `mode='branch'`. Supports two-way binding. */
77
+ activeBranch: _angular_core.ModelSignal<string | null>;
78
+ /** Extra CSS classes for the root element. */
79
+ customClass: _angular_core.InputSignal<string>;
80
+ /** Emitted when the active step changes. */
81
+ stepChange: _angular_core.OutputEmitterRef<StepperChangeEvent>;
82
+ /** Emitted when a step is clicked. */
83
+ stepClick: _angular_core.OutputEmitterRef<{
84
+ index: number;
85
+ step: StepperItem;
86
+ }>;
87
+ /** Emitted when navigation to a step is blocked (e.g. linear validation). */
88
+ stepBlocked: _angular_core.OutputEmitterRef<StepperBlockedEvent>;
89
+ /** Emitted when the active branch changes (branch mode). */
90
+ branchChange: _angular_core.OutputEmitterRef<StepperBranchChangeEvent>;
91
+ private readonly branchShift;
92
+ visibleItems: _angular_core.Signal<StepperItem[]>;
93
+ normalizedSteps: _angular_core.Signal<NormalizedStep[]>;
94
+ clampedActiveIndex: _angular_core.Signal<number>;
95
+ rootClasses: _angular_core.Signal<string>;
96
+ private resolveState;
97
+ isClickable(step: NormalizedStep): boolean;
98
+ private blockReason;
99
+ onSelect(step: NormalizedStep): void;
100
+ onStepKeydown(event: KeyboardEvent, step: NormalizedStep): void;
101
+ indicatorClass(step: NormalizedStep): string;
102
+ connectorClass(step: NormalizedStep): string;
103
+ /** Connector thickness — `line` renders a bold, continuous progress bar; every other variant uses a thin hairline. */
104
+ connectorSizeClass(): string;
105
+ verticalRowPaddingClass(): string;
106
+ verticalConnectorLayoutClass(): string;
107
+ labelClass(step: NormalizedStep): string;
108
+ descriptionClass(step: NormalizedStep): string;
109
+ private shapeClass;
110
+ private indicatorSizeClass;
111
+ private labelSizeClass;
112
+ private descriptionSizeClass;
113
+ shouldShowText(_step: NormalizedStep): boolean;
114
+ shouldShowIcon(step: NormalizedStep): boolean;
115
+ iconPath(step: NormalizedStep): string;
116
+ indicatorText(step: NormalizedStep): string;
117
+ shouldShowBadge(step: NormalizedStep): boolean;
118
+ badgeText(step: NormalizedStep): string;
119
+ branchShiftClass(): string;
120
+ private triggerBranchShift;
121
+ private focusStep;
122
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<SvStepperComponent, never>;
123
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<SvStepperComponent, "sv-stepper", never, { "items": { "alias": "items"; "required": false; "isSignal": true; }; "activeIndex": { "alias": "activeIndex"; "required": false; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "showDescriptions": { "alias": "showDescriptions"; "required": false; "isSignal": true; }; "showConnectors": { "alias": "showConnectors"; "required": false; "isSignal": true; }; "enableHotkeys": { "alias": "enableHotkeys"; "required": false; "isSignal": true; }; "activeBranch": { "alias": "activeBranch"; "required": false; "isSignal": true; }; "customClass": { "alias": "customClass"; "required": false; "isSignal": true; }; }, { "activeIndex": "activeIndexChange"; "activeBranch": "activeBranchChange"; "stepChange": "stepChange"; "stepClick": "stepClick"; "stepBlocked": "stepBlocked"; "branchChange": "branchChange"; }, never, never, true, never>;
124
+ }
125
+
126
+ export { SvStepperComponent };
127
+ export type { StepperBlockedEvent, StepperBranchChangeEvent, StepperChangeEvent, StepperItem, StepperMode, StepperOrientation, StepperSize, StepperStepStatus, StepperVariant };