igniteui-angular
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1 lines • 86.9 kB
Source Map (JSON)
{"version":3,"file":"igniteui-angular-stepper.mjs","sources":["../../../projects/igniteui-angular/stepper/src/stepper/stepper.common.ts","../../../projects/igniteui-angular/stepper/src/stepper/stepper.service.ts","../../../projects/igniteui-angular/stepper/src/stepper/stepper.directive.ts","../../../projects/igniteui-angular/stepper/src/stepper/step/step.component.ts","../../../projects/igniteui-angular/stepper/src/stepper/step/step.component.html","../../../projects/igniteui-angular/stepper/src/stepper/stepper.component.ts","../../../projects/igniteui-angular/stepper/src/stepper/stepper.component.html","../../../projects/igniteui-angular/stepper/src/stepper/public_api.ts","../../../projects/igniteui-angular/stepper/src/stepper/stepper.module.ts","../../../projects/igniteui-angular/stepper/src/igniteui-angular-stepper.ts"],"sourcesContent":["import { ChangeDetectorRef, ElementRef, EventEmitter, InjectionToken, TemplateRef } from '@angular/core';\nimport { IBaseCancelableBrowserEventArgs, IBaseEventArgs } from 'igniteui-angular/core';\nimport { IgxStepperComponent } from './stepper.component';\nimport { IgxStepComponent } from './step/step.component';\nimport {\n IgxStepActiveIndicatorDirective, IgxStepCompletedIndicatorDirective, IgxStepContentDirective,\n IgxStepIndicatorDirective, IgxStepInvalidIndicatorDirective\n} from './stepper.directive';\nimport { ToggleAnimationPlayer, ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';\nimport { CarouselAnimationType, IgxCarouselComponentBase, CarouselAnimationDirection } from 'igniteui-angular/carousel';\n\n// Component interfaces\nexport interface IgxStepper extends IgxCarouselComponentBase {\n steps: IgxStepComponent[];\n /** @hidden @internal */\n nativeElement: HTMLElement;\n /** @hidden @internal */\n invalidIndicatorTemplate: TemplateRef<IgxStepInvalidIndicatorDirective>;\n /** @hidden @internal */\n completedIndicatorTemplate: TemplateRef<IgxStepCompletedIndicatorDirective>;\n /** @hidden @internal */\n activeIndicatorTemplate: TemplateRef<IgxStepActiveIndicatorDirective>;\n verticalAnimationType: VerticalAnimationType;\n horizontalAnimationType: HorizontalAnimationType;\n animationDuration: number;\n linear: boolean;\n orientation: IgxStepperOrientation;\n stepType: IgxStepType;\n contentTop: boolean;\n titlePosition: IgxStepperTitlePosition;\n /** @hidden @internal */\n verticalAnimationSettings: ToggleAnimationSettings;\n /** @hidden @internal */\n _defaultTitlePosition: IgxStepperTitlePosition;\n activeStepChanging: EventEmitter<IStepChangingEventArgs>;\n activeStepChanged: EventEmitter<IStepChangedEventArgs>;\n navigateTo(index: number): void;\n next(): void;\n prev(): void;\n reset(): void;\n /** @hidden @internal */\n playHorizontalAnimations(): void;\n}\n\n// Item interfaces\nexport interface IgxStep extends ToggleAnimationPlayer {\n id: string;\n /** @hidden @internal */\n contentTemplate: TemplateRef<any>;\n /** @hidden @internal */\n customIndicatorTemplate: TemplateRef<any>;\n /** @hidden @internal */\n contentContainer: ElementRef;\n /** @hidden @internal */\n indicator: IgxStepIndicatorDirective;\n /** @hidden @internal */\n content: IgxStepContentDirective;\n /** @hidden @internal */\n indicatorTemplate: TemplateRef<any>;\n index: number;\n disabled: boolean;\n completed: boolean;\n isValid: boolean;\n optional: boolean;\n active: boolean;\n tabIndex: number;\n /** @hidden @internal */\n contentId: string;\n /** @hidden @internal */\n generalDisabled: boolean;\n /** @hidden @internal */\n titlePositionTop: string;\n /** @hidden @internal */\n direction: CarouselAnimationDirection;\n /** @hidden @internal */\n isAccessible: boolean;\n /** @hidden @internal */\n isHorizontal: boolean;\n /** @hidden @internal */\n isTitleVisible: boolean;\n /** @hidden @internal */\n isIndicatorVisible: boolean;\n /** @hidden @internal */\n titlePosition: string;\n /** @hidden @internal */\n linearDisabled: boolean;\n /** @hidden @internal */\n collapsing: boolean;\n /** @hidden @internal */\n animationSettings: ToggleAnimationSettings;\n /** @hidden @internal */\n contentClasses: any;\n /** @hidden @internal */\n stepHeaderClasses: any;\n /** @hidden @internal */\n nativeElement: HTMLElement;\n /** @hidden @internal */\n previous: boolean;\n cdr: ChangeDetectorRef;\n activeChange: EventEmitter<boolean>;\n}\n\n// Events\nexport interface IStepChangingEventArgs extends IBaseEventArgs, IBaseCancelableBrowserEventArgs {\n newIndex: number;\n oldIndex: number;\n owner: IgxStepper;\n}\n\nexport interface IStepChangedEventArgs extends IBaseEventArgs {\n // Provides the index of the current active step within the stepper steps\n index: number;\n owner: IgxStepper;\n}\n\n// Enums\nexport const IgxStepperOrientation = {\n Horizontal: 'horizontal',\n Vertical: 'vertical'\n} as const;\nexport type IgxStepperOrientation = (typeof IgxStepperOrientation)[keyof typeof IgxStepperOrientation];\n\nexport const IgxStepType = {\n Indicator: 'indicator',\n Title: 'title',\n Full: 'full'\n} as const;\nexport type IgxStepType = (typeof IgxStepType)[keyof typeof IgxStepType];\n\nexport const IgxStepperTitlePosition = {\n Bottom: 'bottom',\n Top: 'top',\n End: 'end',\n Start: 'start'\n} as const;\nexport type IgxStepperTitlePosition = (typeof IgxStepperTitlePosition)[keyof typeof IgxStepperTitlePosition];\n\nexport const VerticalAnimationType = {\n Grow: 'grow',\n Fade: 'fade',\n None: 'none'\n} as const;\nexport type VerticalAnimationType = (typeof VerticalAnimationType)[keyof typeof VerticalAnimationType];\n\nexport const HorizontalAnimationType = {\n ...CarouselAnimationType\n} as const;\nexport type HorizontalAnimationType = (typeof HorizontalAnimationType)[keyof typeof HorizontalAnimationType];\n\n// Token\nexport const IGX_STEPPER_COMPONENT = /*@__PURE__*/new InjectionToken<IgxStepperComponent>('IgxStepperToken');\nexport const IGX_STEP_COMPONENT = /*@__PURE__*/new InjectionToken<IgxStepComponent>('IgxStepToken');\n","import { Injectable } from '@angular/core';\nimport { IgxStepper, IgxStepperOrientation, IStepChangingEventArgs } from './stepper.common';\nimport { IgxStepComponent } from './step/step.component';\n\n/** @hidden @internal */\n@Injectable()\nexport class IgxStepperService {\n public activeStep: IgxStepComponent;\n public previousActiveStep: IgxStepComponent;\n public focusedStep: IgxStepComponent;\n\n public collapsingSteps: Set<IgxStepComponent> = new Set<IgxStepComponent>();\n public linearDisabledSteps: Set<IgxStepComponent> = new Set<IgxStepComponent>();\n public visitedSteps: Set<IgxStepComponent> = new Set<IgxStepComponent>();\n public stepper: IgxStepper;\n\n /**\n * Activates the step, fires the steps change event and plays animations.\n */\n public expand(step: IgxStepComponent): void {\n if (this.activeStep === step) {\n return;\n }\n\n const cancel = this.emitActivatingEvent(step);\n if (cancel) {\n return;\n }\n\n this.collapsingSteps.delete(step);\n\n this.previousActiveStep = this.activeStep;\n this.activeStep = step;\n this.activeStep.activeChange.emit(true);\n\n this.collapsingSteps.add(this.previousActiveStep);\n this.visitedSteps.add(this.activeStep);\n\n if (this.stepper.orientation === IgxStepperOrientation.Vertical) {\n this.previousActiveStep.playCloseAnimation(\n this.previousActiveStep.contentContainer\n );\n this.activeStep.cdr.detectChanges();\n\n this.activeStep.playOpenAnimation(\n this.activeStep.contentContainer\n );\n } else {\n this.activeStep.cdr.detectChanges();\n this.stepper.playHorizontalAnimations();\n }\n }\n\n /**\n * Activates the step and fires the steps change event without playing animations.\n */\n public expandThroughApi(step: IgxStepComponent): void {\n if (this.activeStep === step) {\n return;\n }\n\n this.collapsingSteps.clear();\n\n this.previousActiveStep = this.activeStep;\n this.activeStep = step;\n\n if (this.previousActiveStep) {\n this.previousActiveStep.tabIndex = -1;\n }\n this.activeStep.tabIndex = 0;\n this.visitedSteps.add(this.activeStep);\n\n this.activeStep.cdr.markForCheck();\n this.previousActiveStep?.cdr.markForCheck();\n\n this.activeStep.activeChange.emit(true);\n this.previousActiveStep?.activeChange.emit(false);\n }\n\n /**\n * Collapses the currently active step and fires the change event.\n */\n public collapse(step: IgxStepComponent): void {\n if (this.activeStep === step) {\n return;\n }\n step.activeChange.emit(false);\n this.collapsingSteps.delete(step);\n }\n\n /**\n * Determines the steps that should be marked as visited based on the active step.\n */\n public calculateVisitedSteps(): void {\n this.stepper.steps.forEach(step => {\n if (step.index <= this.activeStep.index) {\n this.visitedSteps.add(step);\n } else {\n this.visitedSteps.delete(step);\n }\n });\n }\n\n /**\n * Determines the steps that should be disabled in linear mode based on the validity of the active step.\n */\n public calculateLinearDisabledSteps(): void {\n if (!this.activeStep) {\n return;\n }\n\n if (this.activeStep.isValid) {\n const firstRequiredIndex = this.getNextRequiredStep();\n if (firstRequiredIndex !== -1) {\n this.updateLinearDisabledSteps(firstRequiredIndex);\n } else {\n this.linearDisabledSteps.clear();\n }\n } else {\n this.stepper.steps.forEach(s => {\n if (s.index > this.activeStep.index) {\n this.linearDisabledSteps.add(s);\n }\n });\n }\n }\n\n public emitActivatingEvent(step: IgxStepComponent): boolean {\n const args: IStepChangingEventArgs = {\n owner: this.stepper,\n newIndex: step.index,\n oldIndex: this.activeStep.index,\n cancel: false\n };\n\n this.stepper.activeStepChanging.emit(args);\n return args.cancel;\n }\n\n /**\n * Updates the linearDisabled steps from the current active step to the next required invalid step.\n *\n * @param toIndex the index of the last step that should be enabled.\n */\n private updateLinearDisabledSteps(toIndex: number): void {\n this.stepper.steps.forEach(s => {\n if (s.index > this.activeStep.index) {\n if (s.index <= toIndex) {\n this.linearDisabledSteps.delete(s);\n } else {\n this.linearDisabledSteps.add(s);\n }\n }\n });\n }\n\n private getNextRequiredStep(): number {\n if (!this.activeStep) {\n return;\n }\n return this.stepper.steps.findIndex(s => s.index > this.activeStep.index && !s.optional && !s.disabled && !s.isValid);\n }\n}\n","import { Directive, ElementRef, HostBinding, Input, inject } from '@angular/core';\nimport { IgxStep, IGX_STEP_COMPONENT } from './stepper.common';\nimport { IgxStepperService } from './stepper.service';\n\n/**\n * Allows a custom element to be added as an active step indicator.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <ng-template igxStepActiveIndicator>\n * <igx-icon>edit</igx-icon>\n * </ng-template>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepActiveIndicator]',\n standalone: true\n})\nexport class IgxStepActiveIndicatorDirective { }\n\n/**\n * Allows a custom element to be added as a complete step indicator.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <ng-template igxStepCompletedIndicator>\n * <igx-icon>check</igx-icon>\n * </ng-template>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepCompletedIndicator]',\n standalone: true\n})\nexport class IgxStepCompletedIndicatorDirective { }\n\n/**\n * Allows a custom element to be added as an invalid step indicator.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <ng-template igxStepInvalidIndicator>\n * <igx-icon>error</igx-icon>\n * </ng-template>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepInvalidIndicator]',\n standalone: true\n})\nexport class IgxStepInvalidIndicatorDirective { }\n\n/**\n * Allows a custom element to be added as a step indicator.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <igx-step>\n * <igx-icon igxStepIndicator>home</igx-icon>\n * </igx-step>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepIndicator]',\n standalone: true\n})\nexport class IgxStepIndicatorDirective { }\n\n/**\n * Allows a custom element to be added as a step title.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <igx-step>\n * <p igxStepTitle>Home</p>\n * </igx-step>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepTitle]',\n standalone: true\n})\nexport class IgxStepTitleDirective {\n @HostBinding('class.igx-stepper__step-title')\n public defaultClass = true;\n}\n\n/**\n * Allows a custom element to be added as a step subtitle.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <igx-step>\n * <p igxStepSubtitle>Home Subtitle</p>\n * </igx-step>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepSubtitle]',\n standalone: true\n})\nexport class IgxStepSubtitleDirective {\n @HostBinding('class.igx-stepper__step-subtitle')\n public defaultClass = true;\n}\n\n/**\n * Allows a custom element to be added as a step content.\n *\n * @igxModule IgxStepperModule\n * @igxTheme igx-stepper-theme\n * @igxKeywords stepper \n * @igxGroup Layouts\n *\n * @example\n * <igx-stepper>\n * <igx-step>\n * <div igxStepContent>...</div>\n * </igx-step>\n * </igx-stepper>\n */\n@Directive({\n selector: '[igxStepContent]',\n standalone: true\n})\nexport class IgxStepContentDirective {\n private step = inject<IgxStep>(IGX_STEP_COMPONENT);\n private stepperService = inject(IgxStepperService);\n public elementRef = inject<ElementRef<HTMLElement>>(ElementRef);\n\n private get target(): IgxStep {\n return this.step;\n }\n\n @HostBinding('class.igx-stepper__step-content')\n public defaultClass = true;\n\n @HostBinding('attr.role')\n public role = 'tabpanel';\n\n @HostBinding('attr.aria-labelledby')\n public get stepId(): string {\n return this.target.id;\n }\n\n @HostBinding('attr.id')\n @Input()\n public id = this.target.id.replace('step', 'content');\n\n @HostBinding('attr.tabindex')\n @Input()\n public get tabIndex(): number {\n if (this._tabIndex !== null) {\n return this._tabIndex;\n }\n\n return this.stepperService.activeStep === this.target ? 0 : -1;\n }\n\n public set tabIndex(val: number) {\n this._tabIndex = val;\n }\n\n private _tabIndex = null;\n}\n","import { AfterViewInit, booleanAttribute, ChangeDetectorRef, Component, ContentChild, ElementRef, EventEmitter, forwardRef, HostBinding, HostListener, Input, OnDestroy, Output, Renderer2, TemplateRef, ViewChild, inject } from '@angular/core';\nimport { takeUntil } from 'rxjs/operators';\nimport { IgxStep, IgxStepper, IgxStepperOrientation, IgxStepType, IGX_STEPPER_COMPONENT, IGX_STEP_COMPONENT, HorizontalAnimationType } from '../stepper.common';\nimport { IgxStepContentDirective, IgxStepIndicatorDirective } from '../stepper.directive';\nimport { IgxStepperService } from '../stepper.service';\nimport { NgClass, NgTemplateOutlet } from '@angular/common';\nimport { IgxRippleDirective } from 'igniteui-angular/directives';\nimport { ToggleAnimationPlayer, ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';\nimport { CarouselAnimationDirection, IgxSlideComponentBase } from 'igniteui-angular/carousel';\nimport { ɵIgxDirectionality, PlatformUtil } from 'igniteui-angular/core';\n\nlet NEXT_ID = 0;\n\n/**\n * The IgxStepComponent is used within the `igx-stepper` element and it holds the content of each step.\n * It also supports custom indicators, title and subtitle.\n *\n * @igxModule IgxStepperModule\n *\n * @igxKeywords step\n *\n * @example\n * ```html\n * <igx-stepper>\n * ...\n * <igx-step [active]=\"true\" [completed]=\"true\">\n * ...\n * </igx-step>\n * ...\n * </igx-stepper>\n * ```\n */\n@Component({\n selector: 'igx-step',\n templateUrl: 'step.component.html',\n providers: [\n { provide: IGX_STEP_COMPONENT, useExisting: IgxStepComponent }\n ],\n imports: [NgClass, IgxRippleDirective, NgTemplateOutlet]\n})\nexport class IgxStepComponent extends ToggleAnimationPlayer implements IgxStep, AfterViewInit, OnDestroy, IgxSlideComponentBase {\n public stepper = inject<IgxStepper>(IGX_STEPPER_COMPONENT);\n public cdr = inject(ChangeDetectorRef);\n public renderer = inject(Renderer2);\n protected platform = inject(PlatformUtil);\n protected stepperService = inject(IgxStepperService);\n private element = inject<ElementRef<HTMLElement>>(ElementRef);\n private dir = inject(ɵIgxDirectionality);\n\n\n /**\n * Get/Set the `id` of the step component.\n * Default value is `\"igx-step-0\"`;\n * ```html\n * <igx-step id=\"my-first-step\"></igx-step>\n * ```\n * ```typescript\n * const stepId = this.step.id;\n * ```\n */\n @HostBinding('attr.id')\n @Input()\n public id = `igx-step-${NEXT_ID++}`;\n\n /**\n * Get/Set whether the step is interactable.\n *\n * ```html\n * <igx-stepper>\n * ...\n * <igx-step [disabled]=\"true\"></igx-step>\n * ...\n * </igx-stepper>\n * ```\n *\n * ```typescript\n * this.stepper.steps[1].disabled = true;\n * ```\n */\n @Input({ transform: booleanAttribute })\n public set disabled(value: boolean) {\n this._disabled = value;\n if (this.stepper.linear) {\n this.stepperService.calculateLinearDisabledSteps();\n }\n }\n\n public get disabled(): boolean {\n return this._disabled;\n }\n\n /**\n * Get/Set whether the step is completed.\n *\n * @remarks\n * When set to `true` the following separator is styled `solid`.\n *\n * ```html\n * <igx-stepper>\n * ...\n * <igx-step [completed]=\"true\"></igx-step>\n * ...\n * </igx-stepper>\n * ```\n *\n * ```typescript\n * this.stepper.steps[1].completed = true;\n * ```\n */\n @Input({ transform: booleanAttribute })\n @HostBinding('class.igx-stepper__step--completed')\n public completed = false;\n\n /**\n * Get/Set whether the step is valid.\n *```html\n * <igx-step [isValid]=\"form.form.valid\">\n * ...\n * <div igxStepContent>\n * <form #form=\"ngForm\">\n * ...\n * </form>\n * </div>\n * </igx-step>\n * ```\n */\n @Input({ transform: booleanAttribute })\n public get isValid(): boolean {\n return this._valid;\n }\n\n public set isValid(value: boolean) {\n this._valid = value;\n if (this.stepper.linear && this.index !== undefined) {\n this.stepperService.calculateLinearDisabledSteps();\n }\n }\n\n /**\n * Get/Set whether the step is optional.\n *\n * @remarks\n * Optional steps validity does not affect the default behavior when the stepper is in linear mode i.e.\n * if optional step is invalid the user could still move to the next step.\n *\n * ```html\n * <igx-step [optional]=\"true\"></igx-step>\n * ```\n * ```typescript\n * this.stepper.steps[1].optional = true;\n * ```\n */\n @Input({ transform: booleanAttribute })\n public optional = false;\n\n /**\n * Get/Set the active state of the step\n *\n * ```html\n * <igx-step [active]=\"true\"></igx-step>\n * ```\n *\n * ```typescript\n * this.stepper.steps[1].active = true;\n * ```\n *\n * @param value: boolean\n */\n @HostBinding('attr.aria-selected')\n @Input({ transform: booleanAttribute })\n public set active(value: boolean) {\n if (value) {\n this.stepperService.expandThroughApi(this);\n } else {\n this.stepperService.collapse(this);\n }\n }\n\n public get active(): boolean {\n return this.stepperService.activeStep === this;\n }\n\n /** @hidden @internal */\n @HostBinding('attr.tabindex')\n @Input()\n public set tabIndex(value: number) {\n this._tabIndex = value;\n }\n\n public get tabIndex(): number {\n return this._tabIndex;\n }\n\n /** @hidden @internal **/\n @HostBinding('attr.role')\n public role = 'tab';\n\n /** @hidden @internal */\n @HostBinding('attr.aria-controls')\n public get contentId(): string {\n return this.content?.id;\n }\n\n /** @hidden @internal */\n @HostBinding('class.igx-stepper__step')\n public cssClass = true;\n\n /** @hidden @internal */\n @HostBinding('class.igx-stepper__step--disabled')\n public get generalDisabled(): boolean {\n return this.disabled || this.linearDisabled;\n }\n\n /** @hidden @internal */\n @HostBinding('class')\n public get titlePositionTop(): string {\n if (this.stepper.stepType !== IgxStepType.Full) {\n return 'igx-stepper__step--simple';\n }\n\n return `igx-stepper__step--${this.titlePosition}`;\n }\n\n /**\n * Emitted when the step's `active` property changes. Can be used for two-way binding.\n *\n * ```html\n * <igx-step [(active)]=\"this.isActive\">\n * </igx-step>\n * ```\n *\n * ```typescript\n * const step: IgxStepComponent = this.stepper.step[0];\n * step.activeChange.subscribe((e: boolean) => console.log(\"Step active state change to \", e))\n * ```\n */\n @Output()\n public activeChange = new EventEmitter<boolean>();\n\n /** @hidden @internal */\n @ViewChild('contentTemplate', { static: true })\n public contentTemplate: TemplateRef<any>;\n\n /** @hidden @internal */\n @ViewChild('customIndicator', { static: true })\n public customIndicatorTemplate: TemplateRef<any>;\n\n /** @hidden @internal */\n @ViewChild('contentContainer')\n public contentContainer: ElementRef;\n\n /** @hidden @internal */\n @ContentChild(forwardRef(() => IgxStepIndicatorDirective))\n public indicator: IgxStepIndicatorDirective;\n\n /** @hidden @internal */\n @ContentChild(forwardRef(() => IgxStepContentDirective))\n public content: IgxStepContentDirective;\n\n /**\n * Get the step index inside of the stepper.\n *\n * ```typescript\n * const step = this.stepper.steps[1];\n * const stepIndex: number = step.index;\n * ```\n */\n public get index(): number {\n return this._index;\n }\n\n /** @hidden @internal */\n public get indicatorTemplate(): TemplateRef<any> {\n if (this.active && this.stepper.activeIndicatorTemplate) {\n return this.stepper.activeIndicatorTemplate;\n }\n\n if (!this.isValid && this.stepper.invalidIndicatorTemplate) {\n return this.stepper.invalidIndicatorTemplate;\n }\n\n if (this.completed && this.stepper.completedIndicatorTemplate) {\n return this.stepper.completedIndicatorTemplate;\n }\n\n if (this.indicator) {\n return this.customIndicatorTemplate;\n }\n\n return null;\n }\n\n /** @hidden @internal */\n public get direction(): CarouselAnimationDirection {\n return this.stepperService.previousActiveStep\n && this.stepperService.previousActiveStep.index > this.index\n ? CarouselAnimationDirection.PREV\n : CarouselAnimationDirection.NEXT;\n }\n\n /** @hidden @internal */\n public get isAccessible(): boolean {\n return !this.disabled && !this.linearDisabled;\n }\n\n /** @hidden @internal */\n public get isHorizontal(): boolean {\n return this.stepper.orientation === IgxStepperOrientation.Horizontal;\n }\n\n /** @hidden @internal */\n public get isTitleVisible(): boolean {\n return this.stepper.stepType !== IgxStepType.Indicator;\n }\n\n /** @hidden @internal */\n public get isIndicatorVisible(): boolean {\n return this.stepper.stepType !== IgxStepType.Title;\n }\n\n /** @hidden @internal */\n public get titlePosition(): string {\n return this.stepper.titlePosition ? this.stepper.titlePosition : this.stepper._defaultTitlePosition;\n }\n\n /** @hidden @internal */\n public get linearDisabled(): boolean {\n return this.stepperService.linearDisabledSteps.has(this);\n }\n\n /** @hidden @internal */\n public get collapsing(): boolean {\n return this.stepperService.collapsingSteps.has(this);\n }\n\n /** @hidden @internal */\n public override get animationSettings(): ToggleAnimationSettings {\n return this.stepper.verticalAnimationSettings;\n }\n\n /** @hidden @internal */\n public get contentClasses(): any {\n if (this.isHorizontal) {\n return { 'igx-stepper__body-content': true, 'igx-stepper__body-content--active': this.active };\n } else {\n return 'igx-stepper__step-content';\n }\n }\n\n /** @hidden @internal */\n public get stepHeaderClasses(): any {\n return {\n 'igx-stepper__step--optional': this.optional,\n 'igx-stepper__step-header--current': this.active,\n 'igx-stepper__step-header--invalid': !this.isValid\n && this.stepperService.visitedSteps.has(this) && !this.active && this.isAccessible\n };\n }\n\n /** @hidden @internal */\n public get nativeElement(): HTMLElement {\n return this.element.nativeElement;\n }\n /** @hidden @internal */\n public previous: boolean;\n /** @hidden @internal */\n public _index: number;\n private _tabIndex = -1;\n private _valid = true;\n private _focused = false;\n private _disabled = false;\n\n /** @hidden @internal */\n @HostListener('focus')\n public onFocus(): void {\n this._focused = true;\n this.stepperService.focusedStep = this;\n if (this.stepperService.focusedStep !== this.stepperService.activeStep) {\n this.stepperService.activeStep.tabIndex = -1;\n }\n }\n\n /** @hidden @internal */\n @HostListener('blur')\n public onBlur(): void {\n this._focused = false;\n this.stepperService.activeStep.tabIndex = 0;\n }\n\n /** @hidden @internal */\n @HostListener('keydown', ['$event'])\n public handleKeydown(event: KeyboardEvent): void {\n if (!this._focused) {\n return;\n }\n const key = event.key;\n if (this.stepper.orientation === IgxStepperOrientation.Horizontal) {\n if (key === this.platform.KEYMAP.ARROW_UP || key === this.platform.KEYMAP.ARROW_DOWN) {\n return;\n }\n }\n if (!(this.platform.isNavigationKey(key) || this.platform.isActivationKey(event))) {\n return;\n }\n event.preventDefault();\n this.handleNavigation(key);\n }\n\n /** @hidden @internal */\n public ngAfterViewInit(): void {\n this.openAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(\n () => {\n if (this.stepperService.activeStep === this) {\n this.stepper.activeStepChanged.emit({ owner: this.stepper, index: this.index });\n }\n }\n );\n this.closeAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {\n this.stepperService.collapse(this);\n this.cdr.markForCheck();\n });\n }\n\n /** @hidden @internal */\n public onPointerDown(event: MouseEvent): void {\n event.stopPropagation();\n if (this.isHorizontal) {\n this.changeHorizontalActiveStep();\n } else {\n this.changeVerticalActiveStep();\n }\n }\n\n /** @hidden @internal */\n public handleNavigation(key: string): void {\n switch (key) {\n case this.platform.KEYMAP.HOME:\n this.stepper.steps.filter(s => s.isAccessible)[0]?.nativeElement.focus();\n break;\n case this.platform.KEYMAP.END:\n this.stepper.steps.filter(s => s.isAccessible).pop()?.nativeElement.focus();\n break;\n case this.platform.KEYMAP.ARROW_UP:\n this.previousStep?.nativeElement.focus();\n break;\n case this.platform.KEYMAP.ARROW_LEFT:\n if (this.dir.rtl && this.stepper.orientation === IgxStepperOrientation.Horizontal) {\n this.nextStep?.nativeElement.focus();\n } else {\n this.previousStep?.nativeElement.focus();\n }\n break;\n case this.platform.KEYMAP.ARROW_DOWN:\n this.nextStep?.nativeElement.focus();\n break;\n case this.platform.KEYMAP.ARROW_RIGHT:\n if (this.dir.rtl && this.stepper.orientation === IgxStepperOrientation.Horizontal) {\n this.previousStep?.nativeElement.focus();\n } else {\n this.nextStep?.nativeElement.focus();\n }\n break;\n case this.platform.KEYMAP.SPACE:\n case this.platform.KEYMAP.ENTER:\n if (this.isHorizontal) {\n this.changeHorizontalActiveStep();\n } else {\n this.changeVerticalActiveStep();\n }\n break;\n default:\n return;\n }\n }\n\n /** @hidden @internal */\n public changeHorizontalActiveStep(): void {\n if (this.stepper.animationType === HorizontalAnimationType.none && this.stepperService.activeStep !== this) {\n const argsCanceled = this.stepperService.emitActivatingEvent(this);\n if (argsCanceled) {\n return;\n }\n\n this.active = true;\n this.stepper.activeStepChanged.emit({ owner: this.stepper, index: this.index });\n return;\n }\n this.stepperService.expand(this);\n if (this.stepper.animationType === HorizontalAnimationType.fade) {\n if (this.stepperService.collapsingSteps.has(this.stepperService.previousActiveStep)) {\n this.stepperService.previousActiveStep.active = false;\n }\n }\n }\n\n private get nextStep(): IgxStepComponent | null {\n const focusedStep = this.stepperService.focusedStep;\n if (focusedStep) {\n if (focusedStep.index === this.stepper.steps.length - 1) {\n return this.stepper.steps.find(s => s.isAccessible);\n }\n\n const nextAccessible = this.stepper.steps.find((s, i) => i > focusedStep.index && s.isAccessible);\n return nextAccessible ? nextAccessible : this.stepper.steps.find(s => s.isAccessible);\n }\n\n return null;\n }\n\n private get previousStep(): IgxStepComponent | null {\n const focusedStep = this.stepperService.focusedStep;\n if (focusedStep) {\n if (focusedStep.index === 0) {\n return this.stepper.steps.filter(s => s.isAccessible).pop();\n }\n\n let prevStep;\n for (let i = focusedStep.index - 1; i >= 0; i--) {\n const step = this.stepper.steps[i];\n if (step.isAccessible) {\n prevStep = step;\n break;\n }\n }\n\n return prevStep ? prevStep : this.stepper.steps.filter(s => s.isAccessible).pop();\n\n }\n\n return null;\n }\n\n private changeVerticalActiveStep(): void {\n this.stepperService.expand(this);\n\n if (!this.animationSettings.closeAnimation) {\n this.stepperService.previousActiveStep?.openAnimationPlayer?.finish();\n }\n\n if (!this.animationSettings.openAnimation) {\n this.stepperService.activeStep.closeAnimationPlayer?.finish();\n }\n }\n}\n","<ng-template #defaultTitle>\n @if (isTitleVisible) {\n <ng-content select=\"[igxStepTitle]\"></ng-content>\n }\n @if (isTitleVisible) {\n <ng-content select=\"[igxStepSubtitle]\"></ng-content>\n }\n</ng-template>\n\n<ng-template #contentTemplate>\n <div [ngClass]=\"contentClasses\" #contentContainer>\n @if (active || collapsing) {\n <ng-content select=\"[igxStepContent]\"></ng-content>\n }\n </div>\n</ng-template>\n\n<ng-template #defaultIndicator>\n <span>{{ index + 1 }}</span>\n</ng-template>\n\n<ng-template #customIndicator>\n <ng-content select=\"[igxStepIndicator]\"></ng-content>\n</ng-template>\n\n<div class=\"igx-stepper__step-header\" igxRipple [ngClass]=\"stepHeaderClasses\" (keydown)=\"handleKeydown($event)\"\n (click)=\"onPointerDown($event)\">\n\n @if (isIndicatorVisible) {\n <div class=\"igx-stepper__step-indicator\">\n <ng-container *ngTemplateOutlet=\"indicatorTemplate ? indicatorTemplate : defaultIndicator\"></ng-container>\n </div>\n }\n\n <div class=\"igx-stepper__step-title-wrapper\">\n <ng-container *ngTemplateOutlet=\"defaultTitle\"></ng-container>\n </div>\n</div>\n\n@if (!isHorizontal) {\n <div class=\"igx-stepper__step-content-wrapper\">\n <ng-container *ngTemplateOutlet=\"contentTemplate\"></ng-container>\n </div>\n}\n","import { AnimationReferenceMetadata, useAnimation } from '@angular/animations';\nimport { NgTemplateOutlet } from '@angular/common';\nimport { AfterContentInit, Component, ContentChild, ContentChildren, ElementRef, EventEmitter, HostBinding, Input, OnChanges, OnDestroy, OnInit, Output, QueryList, SimpleChanges, TemplateRef, booleanAttribute, inject } from '@angular/core';\nimport { Subject } from 'rxjs';\nimport { takeUntil } from 'rxjs/operators';\n\nimport { IgxCarouselComponentBase } from 'igniteui-angular/carousel';\nimport { IgxStepComponent } from './step/step.component';\nimport {\n IgxStepper, IgxStepperOrientation, IgxStepperTitlePosition, IgxStepType,\n IGX_STEPPER_COMPONENT, IStepChangedEventArgs, IStepChangingEventArgs, VerticalAnimationType,\n HorizontalAnimationType\n} from './stepper.common';\nimport {\n IgxStepActiveIndicatorDirective,\n IgxStepCompletedIndicatorDirective,\n IgxStepInvalidIndicatorDirective\n} from './stepper.directive';\nimport { IgxStepperService } from './stepper.service';\nimport { fadeIn, growVerIn, growVerOut } from 'igniteui-angular/animations';\nimport { ToggleAnimationSettings } from 'igniteui-angular/expansion-panel';\n\n\n// TODO: common interface between IgxCarouselComponentBase and ToggleAnimationPlayer?\n\n/**\n * IgxStepper provides a wizard-like workflow by dividing content into logical steps.\n *\n * @igxModule IgxStepperModule\n *\n * @igxKeywords stepper\n *\n * @igxGroup Layouts\n *\n * @remarks\n * The Ignite UI for Angular Stepper component allows the user to navigate between multiple steps.\n * It supports horizontal and vertical orientation as well as keyboard navigation and provides API methods to control the active step.\n * The component offers keyboard navigation and API to control the active step.\n *\n * @example\n * ```html\n * <igx-stepper>\n * <igx-step [active]=\"true\">\n * <igx-icon igxStepIndicator>home</igx-icon>\n * <p igxStepTitle>Home</p>\n * <div igxStepContent>\n * ...\n * </div>\n * </igx-step>\n * <igx-step [optional]=\"true\">\n * <div igxStepContent>\n * ...\n * </div>\n * </igx-step>\n * <igx-step>\n * <div igxStepContent>\n * ...\n * </div>\n * </igx-step>\n * </igx-stepper>\n * ```\n */\n@Component({\n selector: 'igx-stepper',\n templateUrl: 'stepper.component.html',\n providers: [\n IgxStepperService,\n { provide: IGX_STEPPER_COMPONENT, useExisting: IgxStepperComponent },\n ],\n imports: [NgTemplateOutlet]\n})\nexport class IgxStepperComponent extends IgxCarouselComponentBase implements IgxStepper, OnChanges, OnInit, AfterContentInit, OnDestroy {\n private stepperService = inject(IgxStepperService);\n private element = inject<ElementRef<HTMLElement>>(ElementRef);\n\n\n /**\n * Get/Set the animation type of the stepper when the orientation direction is vertical.\n *\n * @remarks\n * Default value is `grow`. Other possible values are `fade` and `none`.\n *\n * ```html\n * <igx-stepper verticalAnimationType=\"none\">\n * <igx-stepper>\n * ```\n */\n @Input()\n public get verticalAnimationType(): VerticalAnimationType {\n return this._verticalAnimationType;\n }\n\n public set verticalAnimationType(value: VerticalAnimationType) {\n // TODO: activeChange event is not emitted for the collapsing steps (loop through collapsing steps and emit)\n this.stepperService.collapsingSteps.clear();\n this._verticalAnimationType = value;\n\n switch (value) {\n case 'grow':\n this.verticalAnimationSettings = this.updateVerticalAnimationSettings(growVerIn, growVerOut);\n break;\n case 'fade':\n this.verticalAnimationSettings = this.updateVerticalAnimationSettings(fadeIn, null);\n break;\n case 'none':\n this.verticalAnimationSettings = this.updateVerticalAnimationSettings(null, null);\n break;\n }\n }\n\n /**\n * Get/Set the animation type of the stepper when the orientation direction is horizontal.\n *\n * @remarks\n * Default value is `grow`. Other possible values are `fade` and `none`.\n *\n * ```html\n * <igx-stepper animationType=\"none\">\n * <igx-stepper>\n * ```\n */\n @Input()\n public get horizontalAnimationType(): HorizontalAnimationType {\n return this.animationType;\n }\n\n public set horizontalAnimationType(value: HorizontalAnimationType) {\n // TODO: activeChange event is not emitted for the collapsing steps (loop through collapsing steps and emit)\n this.stepperService.collapsingSteps.clear();\n this.animationType = value;\n }\n\n /**\n * Get/Set the animation duration.\n * ```html\n * <igx-stepper [animationDuration]=\"500\">\n * <igx-stepper>\n * ```\n */\n @Input()\n public get animationDuration(): number {\n return this.defaultAnimationDuration;\n }\n\n public set animationDuration(value: number) {\n if (value && value > 0) {\n this.defaultAnimationDuration = value;\n return;\n }\n this.defaultAnimationDuration = this._defaultAnimationDuration;\n }\n\n /**\n * Get/Set whether the stepper is linear.\n *\n * @remarks\n * If the stepper is in linear mode and if the active step is valid only then the user is able to move forward.\n *\n * ```html\n * <igx-stepper [linear]=\"true\"></igx-stepper>\n * ```\n */\n @Input({ transform: booleanAttribute })\n public get linear(): boolean {\n return this._linear;\n }\n\n public set linear(value: boolean) {\n this._linear = value;\n if (this._linear && this.steps.length > 0) {\n // when the stepper is in linear mode we should calculate which steps should be disabled\n // and which are visited i.e. their validity should be correctly displayed.\n this.stepperService.calculateVisitedSteps();\n this.stepperService.calculateLinearDisabledSteps();\n } else {\n this.stepperService.linearDisabledSteps.clear();\n }\n }\n\n /**\n * Get/Set the stepper orientation.\n *\n * ```typescript\n * this.stepper.orientation = IgxStepperOrientation.Vertical;\n * ```\n */\n @HostBinding('attr.aria-orientation')\n @Input()\n public get orientation(): IgxStepperOrientation {\n return this._orientation;\n }\n\n public set orientation(value: IgxStepperOrientation) {\n if (this._orientation === value) {\n return;\n }\n\n // TODO: activeChange event is not emitted for the collapsing steps\n this.stepperService.collapsingSteps.clear();\n this._orientation = value;\n this._defaultTitlePosition = this._orientation === IgxStepperOrientation.Horizontal ?\n IgxStepperTitlePosition.Bottom : IgxStepperTitlePosition.End;\n }\n\n /**\n * Get/Set the type of the steps.\n *\n * ```typescript\n * this.stepper.stepType = IgxStepType.Indicator;\n * ```\n */\n @Input()\n public stepType: IgxStepType = IgxStepType.Full;\n\n /**\n * Get/Set whether the content is displayed above the steps.\n *\n * @remarks\n * Default value is `false` and the content is below the steps.\n *\n * ```typescript\n * this.stepper.contentTop = true;\n * ```\n */\n @Input({ transform: booleanAttribute })\n public contentTop = false;\n\n /**\n * Get/Set the position of the steps title.\n *\n * @remarks\n * The default value when the stepper is horizontally orientated is `bottom`.\n * In vertical layout the default title position is `end`.\n *\n * ```typescript\n * this.stepper.titlePosition = IgxStepperTitlePosition.Top;\n * ```\n */\n @Input()\n public titlePosition: IgxStepperTitlePosition = null;\n\n /** @hidden @internal **/\n @HostBinding('class.igx-stepper')\n public cssClass = 'igx-stepper';\n\n /** @hidden @internal **/\n @HostBinding('attr.role')\n public role = 'tablist';\n\n /** @hidden @internal **/\n @HostBinding('class.igx-stepper--horizontal')\n public get directionClass() {\n return this.orientation === IgxStepperOrientation.Horizontal;\n }\n\n /**\n * Emitted when the stepper's active step is changing.\n *\n *```html\n * <igx-stepper (activeStepChanging)=\"handleActiveStepChanging($event)\">\n * </igx-stepper>\n * ```\n *\n *```typescript\n * public handleActiveStepChanging(event: IStepChangingEventArgs) {\n * if (event.newIndex < event.oldIndex) {\n * event.cancel = true;\n * }\n * }\n *```\n */\n @Output()\n public activeStepChanging = new EventEmitter<IStepChangingEventArgs>();\n\n /**\n * Emitted when the active step is changed.\n *\n * @example\n * ```\n * <igx-stepper (activeStepChanged)=\"handleActiveStepChanged($event)\"></igx-stepper>\n * ```\n */\n @Output()\n public activeStepChanged = new EventEmitter<IStepChangedEventArgs>();\n\n /** @hidden @internal */\n @ContentChild(IgxStepInvalidIndicatorDirective, { read: TemplateRef })\n public invalidIndicatorTemplate: TemplateRef<IgxStepInvalidIndicatorDirective>;\n\n /** @hidden @internal */\n @ContentChild(IgxStepCompletedIndicatorDirective, { read: TemplateRef })\n public completedIndicatorTemplate: TemplateRef<IgxStepCompletedIndicatorDirective>;\n\n /** @hidden @internal */\n @ContentChild(IgxStepActiveIndicatorDirective, { read: TemplateRef })\n public activeIndicatorTemplate: TemplateRef<IgxStepActiveIndicatorDirective>;\n\n /** @hidden @internal */\n @ContentChildren(IgxStepComponent, { descendants: false })\n private _steps: QueryList<IgxStepComponent>;\n\n /**\n * Get all steps.\n *\n * ```typescript\n * const steps: IgxStepComponent[] = this.stepper.steps;\n * ```\n */\n public get steps(): IgxStepComponent[] {\n return this._steps?.toArray() || [];\n }\n\n /** @hidden @internal */\n public get nativeElement(): HTMLElement {\n return this.element.nativeElement;\n }\n\n /** @hidden @internal */\n public verticalAnimationSettings: ToggleAnimationSettings = {\n openAnimation: growVerIn,\n closeAnimation: growVerOut,\n };\n /** @hidden @internal */\n public _defaultTitlePosition: IgxStepperTitlePosition = IgxStepperTitlePosition.Bottom;\n private destroy$ = new Subject<void>();\n private _orientation: IgxStepperOrientation = IgxStepperOrientation.Horizontal;\n private _verticalAnimationType: VerticalAnimationType = VerticalAnimationType.Grow;\n private _linear = false;\n private readonly _defaultAnimationDuration = 350;\n\n constructor() {\n super();\n this.stepperService.stepper = this;\n }\n\n /** @hidden @internal */\n public ngOnChanges(changes: SimpleChanges): void {\n if (changes['animationDuration']) {\n this.verticalAnimationType = this._verticalAnimationType;\n }\n }\n\n /** @hidden @internal */\n public ngOnInit(): void {\n this.enterAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {\n this.activeStepChanged.emit({ owner: this, index: this.stepperService.activeStep.index });\n });\n this.leaveAnimationDone.pipe(takeUntil(this.destroy$)).subscribe(() => {\n if (this.stepperService.collapsingSteps.size === 1) {\n this.stepperService.collapse(this.stepperService.previousActiveStep);\n } else {\n Array.from(this.stepperService.collapsingSteps).slice(0, this.stepperService.collapsingSteps.size - 1)\n .forEach(step => this.stepperService.collapse(step));\n }\n });\n\n\n }\n\n /** @hidden @internal */\n public ngAfterContentInit(): void {\n let activeStep;\n this.steps.forEach((step, index) => {\n this.updateStepAria(step, index);\n if (!activeStep && step.active) {\n activeStep = step;\n }\n });\n if (!activeStep) {\n this.activateFirstStep(true);\n }\n\n if (this.linear) {\n this.stepperService.calculateLinearDisabledSteps();\n }\n\n this.handleStepChanges();\n }\n\n /** @hidden @internal */\n public override ngOnDestroy(): void {\n super.ngOnDestroy();\n this.destroy$.next();\n this.destroy$.complete();\n }\n\n /**\n * Activates the step at a given index.\n *\n *```typescript\n * this.stepper.navigateTo(1);\n *```\n */\n public navigateTo(index: number): void {\n const step = this.steps[index];\n if (!step || this.stepperService.activeStep === step) {\n return;\n }\n this.activateStep(step);\n }\n\n /**\n * Activates the next enabled step.\n *\n *```typescript\n * this.stepper.next();\n *```\n */\n public next(): void {\n this.moveToNextStep();\n }\n\n /**\n * Activates the previous enabled step.\n *\n *```typescript\n * this.stepper.prev();\n *```\n */\n public prev(): void {\n this.moveToNextStep(false);\n }\n\n /**\n * Resets the stepper to its initial state i.e. activates the first step.\n *\n * @remarks\n * The steps' content will not be automatically reset.\n *```typescript\n * this.stepper.reset();\n *```\n */\n public reset(): void {\n this.stepperService.visitedSteps.clear();\n const activeStep = this.steps.find(s => !s.disabled);\n if (activeStep) {\n this.activateStep(activeStep);\n }\n }\n\n /** @hidden @internal */\n public playHorizontalAnimations(): void {\n this.previousItem = this.stepperService.previousActiveStep;\n this.currentItem = this.stepperService.activeStep;\n this.triggerAnimations();\n }\n\n protected getPreviousElement(): HTMLElement {\n return this.stepperService.previousActiveStep?.contentContainer.nativeElement;\n }\n\n protected getCurrentElement(): HTMLElement {\n return this.stepperService.activeStep.contentContainer.nativeElement;\n }\n\n private updateVerticalAnimationSettings(\n openAnimation: AnimationReferenceMetadata,\