UNPKG

@sixbell-telco/sdk

Version:

A collection of reusable components designed for use in Sixbell Telco Angular projects

935 lines (928 loc) 52.9 kB
import * as i0 from '@angular/core'; import { signal, computed, Injectable, input, ChangeDetectionStrategy, Component, inject, contentChildren, output } from '@angular/core'; import { CommonModule, NgClass } from '@angular/common'; import { cn } from '@sixbell-telco/sdk/utils/cn'; import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { matChevronLeftOutline, matChevronRightOutline, matKeyboardReturnOutline, matSaveOutline } from '@ng-icons/material-icons/outline'; import { TranslateService, _, TranslatePipe } from '@ngx-translate/core'; import { ButtonComponent } from '@sixbell-telco/sdk/components/button'; import { IconComponent } from '@sixbell-telco/sdk/components/icon'; import { TypographyDirective } from '@sixbell-telco/sdk/directives/typography'; import { matCheckOutline } from '@sixbell-telco/sdk/components/icon/material/outline'; /** * Enum-like object representing possible wizard step statuses * @readonly * @enum {string} */ const WIZARD_STEP_STATUS = { IN_PROGRESS: 'in progress', PENDING: 'pending', COMPLETED: 'completed', }; /** * Service for managing wizard state and navigation * @Injectable */ class WizardService { // State signals /** @private Internal signal for wizard steps */ _steps = signal([]); /** @private Internal signal for current step index */ _currentStepIndex = signal(0); /** @private Internal signal for first step index */ _fistStepIndex = signal(0); /** @private Internal signal for last step index */ _lastStepIndex = signal(0); /** @private Internal signal for transition state */ _isTransitioning = signal(false); // Readonly signals /** Readonly array of wizard steps */ steps = this._steps.asReadonly(); /** * Current step index * @readonly */ currentStepIndex = this._currentStepIndex.asReadonly(); /** * Currently active step * @readonly */ currentStep = computed(() => this.steps()[this.currentStepIndex()]); /** * Array of valid steps * @readonly */ validSteps = computed(() => this.steps().filter((step) => step.isValid)); /** * Indicates if all steps are valid * @readonly */ areAllStepsValid = computed(() => this.validSteps().length === this.steps().length); /** * First step index * @readonly */ fistStepIndex = this._fistStepIndex.asReadonly(); /** * First step in the wizard * @readonly */ firstStep = computed(() => this.steps()[this.fistStepIndex()]); /** * Last step index * @readonly */ lastStepIndex = this._lastStepIndex.asReadonly(); /** * Last step in the wizard * @readonly */ lastStep = computed(() => this.steps()[this.lastStepIndex()]); /** * Completed steps * @readonly */ completedSteps = computed(() => this.steps().filter((step) => step.status === 'completed')); /** * Pending steps * @readonly */ pendingSteps = computed(() => this.steps().filter((step) => step.status === 'pending')); /** * In-progress steps * @readonly */ inProgressSteps = computed(() => this.steps().filter((step) => step.status === 'in progress')); /** * Indicates if all steps are completed * @readonly */ isCompleted = computed(() => this.completedSteps().length === this.steps().length); /** * Indicates if current step is the first step * @readonly */ isCurrentStepFirst = computed(() => this.currentStepIndex() === this.fistStepIndex()); /** * Indicates if current step is the last step * @readonly */ isCurrentStepLast = computed(() => this.currentStepIndex() === this.lastStepIndex()); /** * Indicates if wizard is transitioning between steps * @readonly */ isTransitioning = this._isTransitioning.asReadonly(); /** * Initializes the wizard with given steps * @param {WizardStep[]} steps - Array of step configurations */ init(steps) { this.setSteps(steps.map((step, index) => ({ ...step, status: index === 0 ? 'in progress' : 'pending', isValid: false, }))); this.setFistStepIndex(0); this.setLastStepIndex(steps.length - 1); } /** * Replaces all steps with new configuration * @param {WizardStepConfig[]} steps - New array of step configurations */ setSteps(steps) { this._steps.set(steps); } /** * Adds a new step to the end of the wizard * @param {WizardStepConfig} step - Step configuration to add */ addStep(step) { this.setSteps([...this.steps(), step]); } /** * Inserts a step at specific index * @param {number} index - Position to insert the step * @param {WizardStepConfig} step - Step configuration to add */ addStepAtIndex(index, step) { const newSteps = this.steps().reduce((acc, s, i) => { if (i === index) { acc.push(step); } acc.push(s); return acc; }, []); this.setSteps(newSteps); } /** * Removes step at specified index * @param {number} index - Index of step to remove */ removeStep(index) { const newSteps = this.steps().filter((_, i) => i !== index); this.setSteps(newSteps); } /** * Updates name of step at specified index * @param {number} index - Index of step to update * @param {string} name - New name for the step */ updateStepName(index, name) { const newSteps = this.steps().map((s, i) => (i === index ? { ...s, name } : s)); this.setSteps(newSteps); } /** * Updates label of step at specified index * @param {number} index - Index of step to update * @param {string} label - New label for the step */ updateStepLabel(index, label) { const newSteps = this.steps().map((s, i) => (i === index ? { ...s, label } : s)); this.setSteps(newSteps); } /** * Updates status of step at specified index * @param {number} index - Index of step to update * @param {WizardStepStatus} status - New status for the step * @param {number} [stale] - Optional delay in milliseconds before applying changes */ updateStepStatus(index, status, stale) { const newSteps = this.steps().map((s, i) => (i === index ? { ...s, status } : s)); this._isTransitioning.set(true); if (stale === undefined) { this._isTransitioning.set(false); this.setSteps(newSteps); return; } setTimeout(() => { this._isTransitioning.set(false); this.setSteps(newSteps); }, stale); } /** * Updates validation status of step at specified index * @param {number} index - Index of step to update * @param {boolean} isValid - New validation status */ updateStepIsValid(index, isValid) { const newSteps = this.steps().map((s, i) => (i === index ? { ...s, isValid } : s)); this.setSteps(newSteps); } /** * Replaces entire step configuration at specified index * @param {number} index - Index of step to replace * @param {WizardStepConfig} step - New step configuration */ updateStep(index, step) { const newSteps = this.steps().map((s, i) => (i === index ? step : s)); this.setSteps(newSteps); } /** * Sets current active step index * @param {number} index - New current step index */ setCurrentStepIndex(index) { this._currentStepIndex.set(index); } /** * Sets index for first step in wizard * @param {number} index - New first step index */ setFistStepIndex(index) { this._fistStepIndex.set(index); } /** * Sets index for last step in wizard * @param {number} index - New last step index */ setLastStepIndex(index) { this._lastStepIndex.set(index); } /** * Navigates to next valid step */ nextStep() { if (!this.currentStep().isValid) return; const lastIndex = this._steps().length - 1; const nextIndex = this.currentStepIndex() + 1; if (nextIndex > lastIndex) return; const nextStepIndex = nextIndex; this.updateStepStatus(this.currentStepIndex(), 'completed'); this._currentStepIndex.set(nextStepIndex); this.updateStepStatus(nextStepIndex, 'in progress', 300); } /** * Navigates to previous step */ previousStep() { if (this.currentStepIndex() <= 0) return; const previousStepIndex = this.currentStepIndex() - 1; this.updateStepStatus(this.currentStepIndex(), 'pending'); this._currentStepIndex.set(previousStepIndex); this.updateStepStatus(previousStepIndex, 'in progress', 300); } /** * Jumps to specified step index * @param {number} index - Target step index to navigate to */ gotoStep(index) { if (index < 0 || index >= this.steps().length) return; const newSteps = this.steps().map((step, i) => { if (i === index) { return { ...step, status: WIZARD_STEP_STATUS.IN_PROGRESS }; } if (index === this.fistStepIndex()) { if (i === this.fistStepIndex()) { return { ...step, status: WIZARD_STEP_STATUS.IN_PROGRESS }; } return { ...step, status: WIZARD_STEP_STATUS.PENDING }; } if (index === this.lastStepIndex()) { if (i === this.lastStepIndex()) { return { ...step, status: WIZARD_STEP_STATUS.IN_PROGRESS }; } return { ...step, status: WIZARD_STEP_STATUS.COMPLETED }; } if (i < index) { return { ...step, status: WIZARD_STEP_STATUS.COMPLETED }; } if (i > index) { return { ...step, status: WIZARD_STEP_STATUS.PENDING }; } return step; }); this.setSteps(newSteps); this._currentStepIndex.set(index); } /** * Resets wizard to initial state */ reset() { this._steps.set([]); this._currentStepIndex.set(0); this._fistStepIndex.set(0); this._lastStepIndex.set(0); this._isTransitioning.set(false); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardService, providedIn: 'root' }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class WizardActionsComponent { // Base table properties alignment = input('left'); componentClass = computed(() => { return cn('flex flex-w items-center gap-2', { 'justify-start': this.alignment() === 'left', 'justify-end': this.alignment() === 'right', 'justify-center': this.alignment() === 'center', }); }); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardActionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.0", type: WizardActionsComponent, isStandalone: true, selector: "st-wizard-actions", inputs: { alignment: { classPropertyName: "alignment", publicName: "alignment", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<div [class]=\"componentClass()\">\n\t<ng-content></ng-content>\n</div>\n", dependencies: [{ kind: "ngmodule", type: CommonModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardActionsComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard-actions', imports: [CommonModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"componentClass()\">\n\t<ng-content></ng-content>\n</div>\n" }] }] }); /** * Component wrapper for individual wizard step content with validation integration. * * @remarks * Manages content visibility and validation state synchronization with the wizard service. * Automatically handles: * - Step activation status * - Validation state propagation * - Fade animations * - Destruction cleanup * * @example * ```html * <st-wizard-content * [stepIndex]="0" * [isValid]="form.valid" * > * <!-- Step content --> * </st-wizard-content> * ``` */ class WizardContentComponent { /** @internal Injected wizard state service */ wizardService = inject(WizardService); /** * Position index in the wizard sequence (0-based) * @default 0 */ stepIndex = input(0); /** * Current validation state of the step * @default false * @remarks Updates wizard service automatically when changed */ isValid = input(false); /** * Computed status from wizard service * @internal */ status = computed(() => this.wizardService.steps()[this.stepIndex()].status); /** * Observable version of isValid input * @internal */ isValid$ = toObservable(this.isValid); /** * Current active step index from service * @internal */ currentStep = computed(() => this.wizardService.currentStepIndex()); /** * Checks if this content's step is currently active * @returns {boolean} True if stepIndex matches current active step * @internal */ isStepActive = computed(() => this.stepIndex() === this.wizardService.currentStepIndex()); /** * Computed CSS classes for content container * @returns {string} Animation classes * @internal */ componentClass = computed(() => cn('animate-fade-in animate-duration-300')); /** * Sets up validation state synchronization * @remarks Uses takeUntilDestroyed for automatic cleanup */ constructor() { this.isValid$.pipe(takeUntilDestroyed()).subscribe((isValid) => { this.wizardService.updateStepIsValid(this.stepIndex(), isValid); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardContentComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: WizardContentComponent, isStandalone: true, selector: "st-wizard-content", inputs: { stepIndex: { classPropertyName: "stepIndex", publicName: "stepIndex", isSignal: true, isRequired: false, transformFunction: null }, isValid: { classPropertyName: "isValid", publicName: "isValid", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (this.isStepActive()) {\n\t<div [class]=\"componentClass()\">\n\t\t<ng-content></ng-content>\n\t</div>\n}\n" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardContentComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard-content', imports: [], template: "@if (this.isStepActive()) {\n\t<div [class]=\"componentClass()\">\n\t\t<ng-content></ng-content>\n\t</div>\n}\n" }] }], ctorParameters: () => [] }); /** * Component that displays a status badge for wizard steps with translated labels. * * @remarks * Shows visual indicators for different step states with automatic translations. * Supports three status types with corresponding styling: * - Completed (green background) * - In progress (neutral border) * - Pending (dimmed appearance) * * Uses NGX-Translate for localization of status labels. * * @example * ```html * <!-- Completed state --> * <st-wizard-marker [status]="'completed'"></st-wizard-marker> * * <!-- In progress state --> * <st-wizard-marker [status]="'in progress'"></st-wizard-marker> * ``` */ class WizardMarkerComponent { /** @internal Translation service instance */ translateService = inject(TranslateService); /** @internal Wizard state service */ wizardService = inject(WizardService); /** * Current status of the associated wizard step * @remarks Determines badge styling and label */ status = input.required(); /** * Computed translated status message * @internal */ statusMarkerMessage = computed(() => this.getStatusMarkerMessage()); /** * Computed CSS classes for the status badge * @internal */ statusMarkerClasses = computed(() => this.getStepStatusMarkerClasses()); /** @internal Signal holding translated "completed" text */ completed = signal('completed'); /** @internal Signal holding translated "in progress" text */ inProgress = signal('in progress'); /** @internal Signal holding translated "pending" text */ pending = signal('pending'); /** @internal Base CSS classes for the marker */ baseClasses = 'rounded-field transition-colors duration-300 ease-out delay-150'; /** @internal Status-to-class mapping */ statusClassMap = { completed: 'bg-primary/20 text-primary px-3 py-[5px]', 'in progress': 'bg-neutral text-neutral-content border-[2px] border-neutral px-[10px] py-[3px]', pending: 'bg-neutral/20 text-neutral-content/20 border-[2px] border-neutral px-[10px] py-[3px]', }; /** * Initializes component translations * @remarks Loads translated status labels using NGX-Translate */ ngOnInit() { this.initTranslatations(); } /** * Gets the appropriate translated status message * @returns {string} Translated status label * @internal */ getStatusMarkerMessage() { return this.status() === 'completed' ? this.completed() : this.status() === 'in progress' ? this.inProgress() : this.pending(); } /** * Computes CSS classes based on current status * @returns {string} Combined Tailwind classes * @internal */ getStepStatusMarkerClasses() { return cn(this.baseClasses, this.statusClassMap[this.status()]); } /** * Initializes translations for status labels * @remarks Uses translation keys: * - 'sdk.wizard.wizardMarker.completed' * - 'sdk.wizard.wizardMarker.inProgress' * - 'sdk.wizard.wizardMarker.pending' * @internal */ initTranslatations() { this.translateService.get(_('sdk.wizard.wizardMarker.completed')).subscribe((res) => { this.completed.set(res); }); this.translateService.get(_('sdk.wizard.wizardMarker.inProgress')).subscribe((res) => { this.inProgress.set(res); }); this.translateService.get(_('sdk.wizard.wizardMarker.pending')).subscribe((res) => { this.pending.set(res); }); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardMarkerComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "19.2.0", type: WizardMarkerComponent, isStandalone: true, selector: "st-wizard-marker", inputs: { status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [class]=\"statusMarkerClasses()\">\n\t<span typography [tyVariant]=\"'body-xxs'\" [tyColor]=\"'inherit'\" [tyFontWeight]=\"'medium'\">{{ statusMarkerMessage() }}</span>\n</div>\n", dependencies: [{ kind: "directive", type: TypographyDirective, selector: "[typography]", inputs: ["tyVariant", "tyColor", "tyFontWeight", "tyTextOverflow"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardMarkerComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard-marker', imports: [TypographyDirective], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"statusMarkerClasses()\">\n\t<span typography [tyVariant]=\"'body-xxs'\" [tyColor]=\"'inherit'\" [tyFontWeight]=\"'medium'\">{{ statusMarkerMessage() }}</span>\n</div>\n" }] }] }); /** * Visual indicator component for individual wizard steps with status-dependent styling and animations. * * @remarks * Displays step progress through dynamic border and background colors. Supports three states: * - Completed (checkmark icon) * - In progress (highlighted border) * - Pending (dimmed appearance) * * Uses Angular signals for optimized change detection and smooth transitions. * * @example * ```html * <!-- Completed step with custom icon --> * <st-wizard-step * [stepIndex]="1" * [status]="'completed'" * icon="user-profile" * ></st-wizard-step> * * <!-- Current active step --> * <st-wizard-step * [stepIndex]="2" * [status]="'in progress'" * ></st-wizard-step> * ``` */ class WizardStepComponent { /** @internal Injected wizard state service */ wizardService = inject(WizardService); /** * Optional custom icon name for completed state * @default undefined (uses checkmark) */ icon = input(); /** * Current progress status of the step * @remarks Determines visual state and transitions */ status = input.required(); /** * Position in wizard sequence (0-based index) * @remarks Used for current step comparison */ stepIndex = input.required(); /** @internal Predefined checkmark icon */ iconCheck = matCheckOutline; /** * Computed CSS classes for step containers * @returns {Object} Classes for outer/inner elements * @remarks Applies: * - 300ms color transitions * - 300ms delay for pending states * - Primary colors for active/completed states * @internal */ stepClasses = computed(() => { // Base classes const baseOuterClasses = 'flex h-[50px] min-h-[50px] w-[50px] min-w-[50px] items-center justify-center rounded-full border-[1px] border-solid p-[2px] font-medium text-neutral-content'; const baseInnerClasses = 'flex h-full w-full items-center justify-center rounded-full bg-secondary'; // State variables const isCurrentStep = this.wizardService.currentStepIndex() === this.stepIndex(); const currentStatus = this.status(); const isCompleted = currentStatus === 'completed'; const isInProgress = currentStatus === 'in progress'; // Conditions const isCurrentActive = isCurrentStep && (isInProgress || isCompleted); const isCompletedNotCurrent = !isCurrentStep && isCompleted; const isPendingNotCurrent = !isCurrentStep && currentStatus === 'pending'; const outerClasses = cn(baseOuterClasses, { 'border-primary currentStep transition-colors duration-300 ease-out': isCurrentActive, 'border-primary notCurrentStep transition-colors duration-300 ease-out': isCompletedNotCurrent, 'border-secondary notCurrentStep transition-colors duration-300 ease-out delay-[300ms]': isPendingNotCurrent, }); const innerClasses = cn(baseInnerClasses, { 'bg-primary text-primary-content currentStep transition-colors duration-300 ease-out': isCurrentActive, 'bg-primary text-primary-content notCurrentStep transition-colors duration-300 ease-out': isCompletedNotCurrent, 'bg-secondary text-secondary-content notCurrentStep transition-colors duration-300 ease-out delay-[300ms]': isPendingNotCurrent, }); return { outerClasses, innerClasses }; }); /** * Step number visibility classes * @returns {string} Tailwind classes with fade animation * @remarks * - Hidden when completed * - 350ms fade-in delay for smooth transitions * @internal */ stepNumberClasses = computed(() => { const isCompleted = this.status() === 'completed'; return cn({ 'hidden opacity-0': isCompleted, 'animate-fade-in animate-duration-300 delay-[350ms] opacity-100': !isCompleted, }); }); /** * Completion icon visibility classes * @returns {string} Tailwind classes with fade animation * @remarks * - Only visible when completed * - 350ms fade-in delay for smooth transition * @internal */ stepIconClasses = computed(() => { const isCompleted = this.status() === 'completed'; return cn({ 'animate-fade-in animate-duration-300 delay-[350ms] opacity-100': isCompleted, 'hidden opacity-0': !isCompleted, }); }); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: WizardStepComponent, isStandalone: true, selector: "st-wizard-step", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, status: { classPropertyName: "status", publicName: "status", isSignal: true, isRequired: true, transformFunction: null }, stepIndex: { classPropertyName: "stepIndex", publicName: "stepIndex", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "<div [class]=\"stepClasses().outerClasses\">\n\t<div [class]=\"stepClasses().innerClasses\">\n\t\t<div [class]=\"stepNumberClasses()\">\n\t\t\t@let iconVar = icon();\n\t\t\t@if (iconVar) {\n\t\t\t\t<st-icon [icon]=\"iconVar\" color=\"inherit\" size=\"md\"></st-icon>\n\t\t\t} @else {\n\t\t\t\t<span typography [tyVariant]=\"'body'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'semibold'\">\n\t\t\t\t\t{{ stepIndex() + 1 }}\n\t\t\t\t</span>\n\t\t\t}\n\t\t</div>\n\t\t<div [class]=\"stepIconClasses()\">\n\t\t\t<st-icon [icon]=\"iconCheck\" color=\"inherit\" size=\"md\"></st-icon>\n\t\t</div>\n\t</div>\n</div>\n", dependencies: [{ kind: "directive", type: TypographyDirective, selector: "[typography]", inputs: ["tyVariant", "tyColor", "tyFontWeight", "tyTextOverflow"] }, { kind: "component", type: IconComponent, selector: "st-icon", inputs: ["color", "size", "icon"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardStepComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard-step', imports: [TypographyDirective, IconComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div [class]=\"stepClasses().outerClasses\">\n\t<div [class]=\"stepClasses().innerClasses\">\n\t\t<div [class]=\"stepNumberClasses()\">\n\t\t\t@let iconVar = icon();\n\t\t\t@if (iconVar) {\n\t\t\t\t<st-icon [icon]=\"iconVar\" color=\"inherit\" size=\"md\"></st-icon>\n\t\t\t} @else {\n\t\t\t\t<span typography [tyVariant]=\"'body'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'semibold'\">\n\t\t\t\t\t{{ stepIndex() + 1 }}\n\t\t\t\t</span>\n\t\t\t}\n\t\t</div>\n\t\t<div [class]=\"stepIconClasses()\">\n\t\t\t<st-icon [icon]=\"iconCheck\" color=\"inherit\" size=\"md\"></st-icon>\n\t\t</div>\n\t</div>\n</div>\n" }] }] }); /** * A component that visualizes the connection between wizard steps with animated progress indicators. * * @remarks * Handles both horizontal and vertical layouts with status-dependent animations. Manages three segments: * - Left connector (before the step) * - Center indicator (current step marker) * - Right connector (after the step) * * @example * ```html * <!-- Horizontal sub-step --> * <st-wizard-sub-step * [stepIndex]="1" * [stepStatus]="'completed'" * ></st-wizard-sub-step> * * <!-- Vertical sub-step --> * <st-wizard-sub-step * variant="vertical" * [stepIndex]="2" * [stepStatus]="'in progress'" * ></st-wizard-sub-step> * ``` */ class WizardSubStepComponent { /** * Layout direction for the sub-step connectors * @default 'horizontal' */ variant = input('horizontal'); /** @internal Injected wizard state service */ wizardService = inject(WizardService); /** * Index of the parent step in the wizard sequence * @remarks Required for position calculation */ stepIndex = input.required(); /** * Current status of the associated wizard step * @remarks Determines visual state and animations */ stepStatus = input.required(); /** * Checks if this sub-step's index matches the current active step * @returns {boolean} True if this is the active step * @internal */ isCurrentStep = computed(() => this.wizardService.currentStepIndex() === this.stepIndex()); /** * Computed classes for horizontal left connector segment * @returns {string} Tailwind classes with animated width transition * @internal */ horizontalLeftClasses = computed(() => { const isCompleted = this.stepStatus() === 'completed'; const isInProgress = this.stepStatus() === 'in progress'; const afterWidthClass = isCompleted || (this.isCurrentStep() && isInProgress) ? 'after:w-full' : 'after:w-0'; return cn('h-[2px] w-full bg-secondary relative', 'after:absolute after:left-0 after:bottom-0 after:bg-primary after:h-[2px]', 'after:transition-[width] after:duration-300 after:ease-out', afterWidthClass); }); /** * Computed classes for horizontal center indicator * @returns {string} Tailwind classes with color transition delays * @internal */ horizontalCenterClasses = computed(() => { const isPrimaryBg = (this.isCurrentStep() && this.stepStatus() !== 'pending') || (!this.isCurrentStep() && this.stepStatus() === 'completed'); return cn('h-3 min-h-3 w-3 min-w-3 rounded-full', 'transition-colors duration-300 ease-out', isPrimaryBg ? 'bg-primary' : 'bg-secondary', isPrimaryBg && !this.isCurrentStep() ? '' : 'delay-300'); }); /** * Computed classes for horizontal right connector segment * @returns {string} Tailwind classes with completion-based width * @internal */ horizontalRightClasses = computed(() => { const isCompleted = this.stepStatus() === 'completed'; const afterWidthClass = isCompleted ? 'after:w-full' : 'after:w-0'; return cn('h-[2px] w-full bg-secondary relative', 'after:absolute after:left-0 after:bottom-0 after:bg-primary after:h-[2px]', 'after:transition-[width] after:duration-300 after:ease-out', afterWidthClass); }); /** * Computed classes for vertical left connector segment * @returns {string} Tailwind classes with animated height transition * @internal */ verticalLeftClasses = computed(() => { const isCompleted = this.stepStatus() === 'completed'; const isInProgress = this.stepStatus() === 'in progress'; const afterHeightClass = isCompleted || (this.isCurrentStep() && isInProgress) ? 'after:h-full' : 'after:h-0'; return cn('w-[2px] h-full bg-secondary relative rotate-180', 'after:absolute after:left-0 after:bottom-0 after:bg-primary after:w-[2px]', 'after:transition-[height] after:duration-300 after:ease-out', afterHeightClass); }); /** * Computed classes for vertical center indicator * @returns {string} Tailwind classes with color transition logic * @internal */ verticalCenterClasses = computed(() => { const isPrimaryBg = (this.isCurrentStep() && this.stepStatus() !== 'pending') || (!this.isCurrentStep() && this.stepStatus() === 'completed'); return cn('h-3 min-h-3 w-3 min-w-3 rounded-full', 'transition-colors duration-300 ease-out', isPrimaryBg ? 'bg-primary' : 'bg-secondary', isPrimaryBg && !this.isCurrentStep() ? '' : 'delay-300'); }); /** * Computed classes for vertical right connector segment * @returns {string} Tailwind classes with completion-based height * @internal */ verticalRightClasses = computed(() => { const isCompleted = this.stepStatus() === 'completed'; const afterHeightClass = isCompleted ? 'after:h-full' : 'after:h-0'; return cn('w-[2px] h-full bg-secondary relative rotate-180', 'after:absolute after:left-0 after:bottom-0 after:bg-primary after:w-[2px]', 'after:transition-[height] after:duration-300 after:ease-out', afterHeightClass); }); static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardSubStepComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: WizardSubStepComponent, isStandalone: true, selector: "st-wizard-sub-step", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, stepIndex: { classPropertyName: "stepIndex", publicName: "stepIndex", isSignal: true, isRequired: true, transformFunction: null }, stepStatus: { classPropertyName: "stepStatus", publicName: "stepStatus", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: "@if (variant() === 'horizontal') {\n\t<div class=\"flex min-w-[max(100%,80px)] flex-1 -translate-y-[18px] items-center\">\n\t\t<div [class]=\"horizontalLeftClasses()\"></div>\n\t\t<div [class]=\"horizontalCenterClasses()\"></div>\n\t\t<div [class]=\"horizontalRightClasses()\"></div>\n\t</div>\n} @else {\n\t<div class=\"flex h-20 min-h-20 w-fit translate-x-[18px] flex-col items-center\">\n\t\t<div [class]=\"verticalLeftClasses()\"></div>\n\t\t<div [class]=\"verticalCenterClasses()\"></div>\n\t\t<div [class]=\"verticalRightClasses()\"></div>\n\t</div>\n}\n" }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardSubStepComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard-sub-step', imports: [], template: "@if (variant() === 'horizontal') {\n\t<div class=\"flex min-w-[max(100%,80px)] flex-1 -translate-y-[18px] items-center\">\n\t\t<div [class]=\"horizontalLeftClasses()\"></div>\n\t\t<div [class]=\"horizontalCenterClasses()\"></div>\n\t\t<div [class]=\"horizontalRightClasses()\"></div>\n\t</div>\n} @else {\n\t<div class=\"flex h-20 min-h-20 w-fit translate-x-[18px] flex-col items-center\">\n\t\t<div [class]=\"verticalLeftClasses()\"></div>\n\t\t<div [class]=\"verticalCenterClasses()\"></div>\n\t\t<div [class]=\"verticalRightClasses()\"></div>\n\t</div>\n}\n" }] }] }); /** * A container component for multi-step workflows with navigation controls and layout management. * * @remarks * Integrates with WizardService for state management and provides template structure for wizard steps. * Supports both horizontal and vertical layouts with customizable navigation controls. * * @example * ```html * <!-- Basic horizontal wizard --> * <st-wizard> * <st-wizard-content [stepIndex]="0" name="Step 1" [isValid]="true"> * <!-- Step content --> * </st-wizard-content> * <st-wizard-content [stepIndex]="1" name="Step 2"> * <!-- Step content --> * </st-wizard-content> * </st-wizard> * * <!-- Vertical wizard with external controls --> * <st-wizard variant="vertical" [showControls]="false"> * <!-- Step contents --> * </st-wizard> * <div class="controls"> * <st-button (click)="handlePrevious()">Previous</st-button> * <st-button (click)="handleNext()">Next</st-button> * </div> * ``` */ class WizardWrapperComponent { /** * Injected wizard state management service instance * @internal */ wizardService = inject(WizardService); /** * Controls the layout direction of the wizard steps * @default 'horizontal' * @example * <st-wizard variant="vertical">...</st-wizard> */ variant = input('horizontal'); /** * Add a card style to the wizard * @default false * @example * <st-wizard [card]="true">...</st-wizard> */ card = input(false); /** * Query list of contained wizard content components * @internal */ contents = contentChildren(WizardContentComponent); /** * Controls visibility of built-in navigation controls * @default true * @example * <st-wizard [showControls]="false">...</st-wizard> */ showControls = input(true); /** * Event emitted when the back button is clicked * @example * <st-wizard (back)="handleCancel()">...</st-wizard> */ back = output(); /** * Event emitted when navigating to the previous step * @example * <st-wizard (previous)="logStepChange('previous')">...</st-wizard> */ previous = output(); /** * Event emitted when navigating to the next valid step * @example * <st-wizard (next)="logStepChange('next')">...</st-wizard> */ next = output(); /** * Event emitted when all steps are completed * @example * <st-wizard (complete)="showCompletionModal()">...</st-wizard> */ complete = output(); /** * Controls visibility of the loading spinner for completition button * @default false * @example * <st-wizard [loading]="true">...</st-wizard> */ loading = input(false); // Icon definitions /** @internal Material design chevron left icon */ iconChevronLeft = matChevronLeftOutline; /** @internal Material design chevron right icon */ iconChevronRight = matChevronRightOutline; /** @internal Material design return arrow icon */ iconReturn = matKeyboardReturnOutline; /** @internal Material design save/floppy disk icon */ iconSave = matSaveOutline; /** * Handles back navigation and emits onBack event * @emits back * @example * // Template * <button (click)="handleBack()">Cancel</button> * * // Component * handleBack() { * this.back.emit(); * } */ handleBack() { this.back.emit(); } /** * Navigates to previous step and emits previous event * @emits previous * @example * // Manually trigger previous step * previousStep() { * this.wizardService.previousStep(); * this.previous.emit(); * } */ handlePrevious() { this.wizardService.previousStep(); this.previous.emit(); } /** * Navigates to next valid step and emits next event * @emits next * @example * // Custom next step handler * handleNext() { * if (form.valid) { * this.wizardService.nextStep(); * this.next.emit(); * } * } */ handleNext() { this.wizardService.nextStep(); this.next.emit(); } /** * Completes wizard flow and emits complete event * @emits complete * @example * // Final step handler * handleComplete() { * if (allStepsValid) { * this.complete.emit(); * } * } */ handleComplete() { this.complete.emit(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardWrapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "19.2.0", type: WizardWrapperComponent, isStandalone: true, selector: "st-wizard", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, showControls: { classPropertyName: "showControls", publicName: "showControls", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { back: "back", previous: "previous", next: "next", complete: "complete" }, queries: [{ propertyName: "contents", predicate: WizardContentComponent, isSignal: true }], ngImport: i0, template: "@let contentsVar = contents();\n@let varinatVar = variant();\n\n<div\n\t[class]=\"varinatVar === 'horizontal' ? 'flex flex-col gap-6' : 'flex gap-6'\"\n\t[ngClass]=\"{ 'rounded-box bg-base-200 shadow-main p-6': card() && varinatVar === 'horizontal' }\"\n>\n\t<!-- Steps -->\n\t@if (varinatVar === 'horizontal') {\n\t\t<ul class=\"flex gap-4 px-[20%] pt-6\">\n\t\t\t@for (step of wizardService.steps(); track $index) {\n\t\t\t\t<li [ngClass]=\"{ 'flex-1': contentsVar.length - 1 !== $index }\">\n\t\t\t\t\t<div class=\"flex items-center gap-4\">\n\t\t\t\t\t\t<st-wizard-step [icon]=\"step.icon\" [stepIndex]=\"$index\" [status]=\"step.status\"></st-wizard-step>\n\t\t\t\t\t\t@if (contentsVar.length - 1 !== $index) {\n\t\t\t\t\t\t\t<div class=\"flex flex-1 flex-col justify-center\">\n\t\t\t\t\t\t\t\t<div class=\"flex -translate-y-6 justify-center\">\n\t\t\t\t\t\t\t\t\t<st-wizard-marker [status]=\"step.status\"></st-wizard-marker>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<st-wizard-sub-step [stepIndex]=\"$index\" [stepStatus]=\"step.status\"></st-wizard-sub-step>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"mt-1 w-[50px]\">\n\t\t\t\t\t\t<div class=\"relative flex max-w-fit flex-col items-center\">\n\t\t\t\t\t\t\t<div class=\"min-w-max\">\n\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'medium'\">{{ step.label }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"text-secondary relative -top-1 min-w-max\">\n\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-xs'\" [tyColor]=\"'inherit'\" [tyFontWeight]=\"'normal'\">\n\t\t\t\t\t\t\t\t\t{{ 'sdk.wizard.wizardWrapper.step' | translate: { index: $index + 1 } }}</span\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t}\n\t\t</ul>\n\t} @else {\n\t\t<ul class=\"grid h-min max-h-min gap-4\" [ngClass]=\"{ 'rounded-box bg-base-200 shadow-main p-6': card() }\">\n\t\t\t@for (step of wizardService.steps(); track $index) {\n\t\t\t\t<li>\n\t\t\t\t\t<!-- step pogress -->\n\t\t\t\t\t<div class=\"grid grid-rows-[min-content_min-content] gap-4\">\n\t\t\t\t\t\t<div class=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t<st-wizard-step [icon]=\"step.icon\" [stepIndex]=\"$index\" [status]=\"step.status\"></st-wizard-step>\n\t\t\t\t\t\t\t<!-- step info -->\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<div class=\"relative flex max-w-fit flex-col\">\n\t\t\t\t\t\t\t\t\t<div class=\"min-w-max\">\n\t\t\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'medium'\">{{ step.label }}</span>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t\t<div class=\"text-neutral relative -top-1 min-w-max\">\n\t\t\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-xs'\" [tyColor]=\"'inherit'\" [tyFontWeight]=\"'normal'\">\n\t\t\t\t\t\t\t\t\t\t\t{{ 'sdk.wizard.wizardWrapper.step' | translate: { index: $index + 1 } }}</span\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t@if (contentsVar.length - 1 !== $index) {\n\t\t\t\t\t\t\t<div class=\"flex flex-col justify-center\">\n\t\t\t\t\t\t\t\t<st-wizard-sub-step variant=\"vertical\" [stepIndex]=\"$index\" [stepStatus]=\"step.status\"></st-wizard-sub-step>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t}\n\t\t</ul>\n\t}\n\n\t<div class=\"flex flex-1 flex-col gap-6\" [ngClass]=\"{ 'rounded-box bg-base-200 shadow-main p-6': card() && varinatVar === 'vertical' }\">\n\t\t<!-- Content -->\n\t\t<div>\n\t\t\t<ng-content></ng-content>\n\t\t</div>\n\n\t\t<!-- Actions -->\n\t\t@if (this.showControls()) {\n\t\t\t<div class=\"flex items-center justify-end gap-3\">\n\t\t\t\t@if (wizardService.isCurrentStepFirst()) {\n\t\t\t\t\t<st-button variant=\"secondary\" [icon]=\"iconReturn\" (click)=\"handleBack()\">{{ 'sdk.wizard.wizardWrapper.back' | translate }}</st-button>\n\t\t\t\t} @else {\n\t\t\t\t\t<st-button [icon]=\"iconChevronLeft\" variant=\"secondary\" (click)=\"handlePrevious()\">{{\n\t\t\t\t\t\t'sdk.wizard.wizardWrapper.previous' | translate\n\t\t\t\t\t}}</st-button>\n\t\t\t\t}\n\t\t\t\t@if (wizardService.isCurrentStepLast()) {\n\t\t\t\t\t<st-button\n\t\t\t\t\t\t[disabled]=\"!wizardService.areAllStepsValid()\"\n\t\t\t\t\t\tvariant=\"primary\"\n\t\t\t\t\t\t(click)=\"handleComplete()\"\n\t\t\t\t\t\t[disabled]=\"loading()\"\n\t\t\t\t\t\t[loader]=\"loading()\"\n\t\t\t\t\t>\n\t\t\t\t\t\t@if (!loading()) {\n\t\t\t\t\t\t\t<st-icon [icon]=\"iconSave\"></st-icon>\n\n\t\t\t\t\t\t\t{{ 'sdk.wizard.wizardWrapper.end' | translate }}\n\t\t\t\t\t\t}\n\t\t\t\t\t</st-button>\n\t\t\t\t} @else {\n\t\t\t\t\t<st-button [icon]=\"iconChevronRight\" iconPosition=\"right\" [disabled]=\"!wizardService.currentStep().isValid\" (click)=\"handleNext()\">{{\n\t\t\t\t\t\t'sdk.wizard.wizardWrapper.next' | translate\n\t\t\t\t\t}}</st-button>\n\t\t\t\t}\n\t\t\t</div>\n\t\t} @else {\n\t\t\t<ng-content select=\"st-wizard-actions\"></ng-content>\n\t\t}\n\t</div>\n</div>\n", dependencies: [{ kind: "component", type: WizardStepComponent, selector: "st-wizard-step", inputs: ["icon", "status", "stepIndex"] }, { kind: "component", type: ButtonComponent, selector: "st-button", inputs: ["variant", "ghost", "outline", "link", "soft", "dash", "wide", "circle", "square", "glass", "block", "loader", "size", "shadow", "focusable", "icon", "iconPosition", "type", "disabled"] }, { kind: "directive", type: NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: TypographyDirective, selector: "[typography]", inputs: ["tyVariant", "tyColor", "tyFontWeight", "tyTextOverflow"] }, { kind: "component", type: WizardMarkerComponent, selector: "st-wizard-marker", inputs: ["status"] }, { kind: "component", type: WizardSubStepComponent, selector: "st-wizard-sub-step", inputs: ["variant", "stepIndex", "stepStatus"] }, { kind: "pipe", type: TranslatePipe, name: "translate" }, { kind: "component", type: IconComponent, selector: "st-icon", inputs: ["color", "size", "icon"] }] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.0", ngImport: i0, type: WizardWrapperComponent, decorators: [{ type: Component, args: [{ selector: 'st-wizard', imports: [ WizardStepComponent, ButtonComponent, NgClass, TypographyDirective, WizardMarkerComponent, WizardSubStepComponent, TranslatePipe, IconComponent, ], template: "@let contentsVar = contents();\n@let varinatVar = variant();\n\n<div\n\t[class]=\"varinatVar === 'horizontal' ? 'flex flex-col gap-6' : 'flex gap-6'\"\n\t[ngClass]=\"{ 'rounded-box bg-base-200 shadow-main p-6': card() && varinatVar === 'horizontal' }\"\n>\n\t<!-- Steps -->\n\t@if (varinatVar === 'horizontal') {\n\t\t<ul class=\"flex gap-4 px-[20%] pt-6\">\n\t\t\t@for (step of wizardService.steps(); track $index) {\n\t\t\t\t<li [ngClass]=\"{ 'flex-1': contentsVar.length - 1 !== $index }\">\n\t\t\t\t\t<div class=\"flex items-center gap-4\">\n\t\t\t\t\t\t<st-wizard-step [icon]=\"step.icon\" [stepIndex]=\"$index\" [status]=\"step.status\"></st-wizard-step>\n\t\t\t\t\t\t@if (contentsVar.length - 1 !== $index) {\n\t\t\t\t\t\t\t<div class=\"flex flex-1 flex-col justify-center\">\n\t\t\t\t\t\t\t\t<div class=\"flex -translate-y-6 justify-center\">\n\t\t\t\t\t\t\t\t\t<st-wizard-marker [status]=\"step.status\"></st-wizard-marker>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t<st-wizard-sub-step [stepIndex]=\"$index\" [stepStatus]=\"step.status\"></st-wizard-sub-step>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t}\n\t\t\t\t\t</div>\n\t\t\t\t\t<div class=\"mt-1 w-[50px]\">\n\t\t\t\t\t\t<div class=\"relative flex max-w-fit flex-col items-center\">\n\t\t\t\t\t\t\t<div class=\"min-w-max\">\n\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-sm'\" [tyColor]=\"'base'\" [tyFontWeight]=\"'medium'\">{{ step.label }}</span>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t<div class=\"text-secondary relative -top-1 min-w-max\">\n\t\t\t\t\t\t\t\t<span typography [tyVariant]=\"'body-xs'\" [tyColor]=\"'inherit'\" [tyFontWeight]=\"'normal'\">\n\t\t\t\t\t\t\t\t\t{{ 'sdk.wizard.wizardWrapper.step' | translate: { index: $index + 1 } }}</span\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t</li>\n\t\t\t}\n\t\t</ul>\n\t} @else {\n\t\t<ul class=\"grid h-min max-h-min gap-4\" [ngClass]=\"{ 'rounded-box bg-base-200 shadow-main p-6': card() }\">\n\t\t\t@for (step of wizardService.steps(); track $index) {\n\t\t\t\t<li>\n\t\t\t\t\t<!-- step pogress -->\n\t\t\t\t\t<div class=\"grid grid-rows-[min-content_min-content] gap-4\">\n\t\t\t\t\t\t<div class=\"flex items-center gap-3\">\n\t\t\t\t\t\t\t<st-wizard-step [icon]=\"step.icon\" [stepIndex]=\"$index\" [status]=\"step.status\"></st-wizard-step>\n\t\t\t\t\t\t\t<!-- step info -->\n\t\t\t\t\t\t\t<div>\n\t\t\t\t\t\t\t\t<div class=\"relative flex max-w-fit flex-col\">\n\t\t\t\t\t\t\t\t\t<div class=\"min-w-max\">\n\t\t\t\t\t\t\