UNPKG

@clr/angular

Version:

Angular components for Clarity

1 lines 167 kB
{"version":3,"file":"clr-angular-wizard.mjs","sources":["../../../projects/angular/wizard/interfaces/wizard-footer-align.ts","../../../projects/angular/wizard/interfaces/wizard-stepnav-layout.ts","../../../projects/angular/wizard/providers/button-hub.service.ts","../../../projects/angular/wizard/providers/page-collection.service.ts","../../../projects/angular/wizard/providers/wizard-navigation.service.ts","../../../projects/angular/wizard/providers/header-actions.service.ts","../../../projects/angular/wizard/wizard-button.ts","../../../projects/angular/wizard/wizard-header-action.ts","../../../projects/angular/wizard/wizard-page-buttons.ts","../../../projects/angular/wizard/wizard-page-header-actions.ts","../../../projects/angular/wizard/wizard-page-navtitle.ts","../../../projects/angular/wizard/wizard-page-title.ts","../../../projects/angular/wizard/wizard-page.ts","../../../projects/angular/wizard/wizard-title.ts","../../../projects/angular/wizard/wizard-stepnav-item.ts","../../../projects/angular/wizard/wizard-stepnav.ts","../../../projects/angular/wizard/wizard.ts","../../../projects/angular/wizard/wizard.html","../../../projects/angular/wizard/wizard.module.ts","../../../projects/angular/wizard/index.ts","../../../projects/angular/wizard/clr-angular-wizard.ts"],"sourcesContent":["/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nexport enum ClrWizardFooterAlign {\n START = 'start',\n END = 'end',\n}\n\nconst CLR_WIZARD_FOOTER_ALIGN_VALUES = new Set<string>(Object.values(ClrWizardFooterAlign));\n\nexport function footerAlignAttribute(value: ClrWizardFooterAlign | string): ClrWizardFooterAlign {\n if (CLR_WIZARD_FOOTER_ALIGN_VALUES.has(value as string)) {\n return value as ClrWizardFooterAlign;\n }\n throw new Error(\n `Invalid ClrWizardFooterAlign: \"${value}\". Expected one of: ${[...CLR_WIZARD_FOOTER_ALIGN_VALUES].join(', ')}`\n );\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nexport enum ClrWizardStepnavLayout {\n VERTICAL = 'vertical',\n HORIZONTAL = 'horizontal',\n}\n\nconst CLR_WIZARD_STEPNAV_LAYOUT_VALUES = new Set<string>(Object.values(ClrWizardStepnavLayout));\n\nexport function stepnavLayoutAttribute(value: ClrWizardStepnavLayout | string): ClrWizardStepnavLayout {\n if (CLR_WIZARD_STEPNAV_LAYOUT_VALUES.has(value as string)) {\n return value as ClrWizardStepnavLayout;\n }\n throw new Error(\n `Invalid ClrWizardStepnavLayout: \"${value}\". Expected one of: ${[...CLR_WIZARD_STEPNAV_LAYOUT_VALUES].join(', ')}`\n );\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Injectable } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\n\n@Injectable()\nexport class ButtonHubService {\n buttonsReady = false;\n\n private _previousBtnClicked = new Subject<void>();\n private _nextBtnClicked = new Subject<void>();\n private _dangerBtnClicked = new Subject<void>();\n private _cancelBtnClicked = new Subject<void>();\n private _finishBtnClicked = new Subject<void>();\n private _customBtnClicked = new Subject<string>();\n\n get previousBtnClicked(): Observable<void> {\n return this._previousBtnClicked.asObservable();\n }\n\n get nextBtnClicked(): Observable<void> {\n return this._nextBtnClicked.asObservable();\n }\n\n get dangerBtnClicked(): Observable<void> {\n return this._dangerBtnClicked.asObservable();\n }\n\n get cancelBtnClicked(): Observable<void> {\n return this._cancelBtnClicked.asObservable();\n }\n\n get finishBtnClicked(): Observable<void> {\n return this._finishBtnClicked.asObservable();\n }\n\n get customBtnClicked(): Observable<string> {\n return this._customBtnClicked.asObservable();\n }\n\n buttonClicked(buttonType: string): void {\n if ('previous' === buttonType) {\n this._previousBtnClicked.next();\n } else if ('next' === buttonType) {\n this._nextBtnClicked.next();\n } else if ('finish' === buttonType) {\n this._finishBtnClicked.next();\n } else if ('danger' === buttonType) {\n this._dangerBtnClicked.next();\n } else if ('cancel' === buttonType) {\n this._cancelBtnClicked.next();\n } else {\n this._customBtnClicked.next(buttonType);\n }\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Injectable, QueryList } from '@angular/core';\nimport { Observable, Subject } from 'rxjs';\n\nimport { ClrWizardPage } from '../wizard-page';\n\n/**\n * PageCollectionService manages the collection of pages assigned to the wizard and offers\n * a number of functions useful across the wizards providers and subcomponents -- all related\n * to essentially lookups on the collection of pages.\n *\n * The easiest way to access PageCollectionService is via the wizard. The\n * following example would allow you to access your instance of the wizard from your host\n * component and thereby access the page collection via YourHostComponent.wizard.pageCollection.\n *\n * @example\n * <clr-wizard #wizard ...>\n *\n * @example\n * export class YourHostComponent {\n * @ViewChild(\"wizard\") wizard: Wizard;\n * ...\n * }\n *\n * The heart of the page collection is the query list of pages, which it is assigned as a\n * reference to the Wizard.pages QueryList when the wizard is created.\n *\n */\n@Injectable()\nexport class PageCollectionService {\n /**\n * A reference to the Wizard.pages QueryList.\n *\n * Populated when the wizard is created.\n *\n * @memberof PageCollectionService\n */\n pages: QueryList<ClrWizardPage>;\n\n /**\n *\n * @memberof PageCollectionService\n */\n private _pagesReset = new Subject<boolean>();\n\n /**\n * Converts the PageCollectionService.pages QueryList to an array and returns it.\n *\n * Useful for many instances when you would prefer a QueryList to act like an array.\n *\n * @memberof PageCollectionService\n */\n get pagesAsArray(): ClrWizardPage[] {\n return this.pages ? this.pages.toArray() : [];\n }\n\n /**\n * Returns the length of the pages query list.\n *\n * @memberof PageCollectionService\n */\n get pagesCount(): number {\n return this.pages ? this.pages.length : 0;\n }\n\n /**\n * Returns the next-to-last page in the query list of pages. Operates as a getter\n * so that it isn't working with stale data.\n *\n * @memberof PageCollectionService\n */\n get penultimatePage(): ClrWizardPage {\n const pageCount = this.pagesCount;\n\n if (pageCount < 2) {\n return null;\n }\n\n return this.pagesAsArray[pageCount - 2];\n }\n\n /**\n * Returns the last page in the query list of pages. Operates as a getter\n * so that it isn't working with stale data.\n *\n * @memberof PageCollectionService\n */\n get lastPage(): ClrWizardPage {\n const pageCount = this.pagesCount;\n\n if (pageCount < 1) {\n return null;\n }\n\n return this.pagesAsArray[pageCount - 1];\n }\n\n /**\n * Returns the first page in the query list of pages. Operates as a getter\n * so that it isn't working with stale data.\n *\n * @memberof PageCollectionService\n */\n get firstPage(): ClrWizardPage {\n if (!this.pagesCount) {\n return null;\n }\n\n return this.pagesAsArray[0];\n }\n\n /**\n * An observable that the navigation service listens to in order to know when\n * the page collection completed states have been reset to false so that way it\n * can also reset the navigation to make the first page in the page collection\n * current/active.\n *\n * @memberof PageCollectionService\n */\n get pagesReset(): Observable<boolean> {\n return this._pagesReset.asObservable();\n }\n\n /**\n * Used mostly internally, but accepts a string ID and returns a ClrWizardPage\n * object that matches the ID passed. Note that IDs here should include the prefix\n * \"clr-wizard-page-\".\n *\n * Returns the next-to-last page in the query list of pages. Operates as a getter\n * so that it isn't working with stale data.\n *\n * @memberof PageCollectionService\n */\n getPageById(id: string): ClrWizardPage {\n const foundPages: ClrWizardPage[] = this.pages.filter((page: ClrWizardPage) => id === page.id);\n return this.checkResults(foundPages, id);\n }\n\n /**\n * Accepts s number as a parameter and treats that number as the index of the page\n * you're looking for in the collection of pages. Returns a wizard page object.\n *\n * @memberof PageCollectionService\n */\n getPageByIndex(index: number): ClrWizardPage {\n const pageCount = this.pagesCount;\n const pagesLastIndex: number = pageCount > 1 ? pageCount - 1 : 0;\n\n if (index < 0) {\n throw new Error('Cannot retrieve page with index of ' + index);\n }\n\n if (index > pagesLastIndex) {\n throw new Error('Page index is greater than length of pages array.');\n }\n\n return this.pagesAsArray[index];\n }\n\n /**\n * Takes a wizard page object as a parameter and returns its index in the\n * collection of pages.\n *\n * @memberof PageCollectionService\n */\n getPageIndex(page: ClrWizardPage): number {\n const index = this.pagesAsArray.indexOf(page);\n\n if (index < 0) {\n throw new Error('Requested page cannot be found in collection of pages.');\n }\n\n return index;\n }\n\n /**\n * Accepts two numeric indexes and returns an array of wizard page objects that include\n * all wizard pages in the page collection from the first index to the second.\n *\n * @memberof PageCollectionService\n */\n pageRange(start: number, end: number): ClrWizardPage[] {\n let pages: ClrWizardPage[] = [];\n\n if (start < 0 || end < 0) {\n return [];\n }\n\n if (start === null || typeof start === 'undefined' || isNaN(start)) {\n return [];\n }\n\n if (end === null || typeof end === 'undefined' || isNaN(end)) {\n return [];\n }\n\n if (end > this.pagesCount) {\n end = this.pagesCount;\n }\n\n pages = this.pagesAsArray;\n\n if (end - start === 0) {\n // just return the one page they want\n return [this.getPageByIndex(start)];\n }\n\n // slice end does not include item referenced by end index, which is weird for users\n // incrementing end index here to correct that so users and other methods\n // don't have to think about it\n end = end + 1;\n\n // slice does not return the last one in the range but it does include the first one\n // does not modify original array\n return pages.slice(start, end);\n }\n\n /**\n * Accepts two wizard page objects and returns those page objects with all other page\n * objects between them in the page collection. It doesn't care which page is ahead of the\n * other in the parameters. It will be smart enough to figure that out on its own.\n *\n * @memberof PageCollectionService\n */\n getPageRangeFromPages(page: ClrWizardPage, otherPage: ClrWizardPage): ClrWizardPage[] {\n const pageIndex = this.getPageIndex(page);\n const otherPageIndex = this.getPageIndex(otherPage);\n let startIndex: number;\n let endIndex: number;\n\n if (pageIndex <= otherPageIndex) {\n startIndex = pageIndex;\n endIndex = otherPageIndex;\n } else {\n startIndex = otherPageIndex;\n endIndex = pageIndex;\n }\n return this.pageRange(startIndex, endIndex);\n }\n\n /**\n * Takes a wizard page object as a parameter and returns the wizard page object of\n * the page immediately before it in the page collection. Returns null if there is\n * no page before the page it is passed.\n *\n * @memberof PageCollectionService\n */\n getPreviousPage(page: ClrWizardPage) {\n const myPageIndex = this.getPageIndex(page);\n const previousPageIndex = myPageIndex - 1;\n if (previousPageIndex < 0) {\n return null;\n }\n return this.getPageByIndex(previousPageIndex);\n }\n\n /**\n * Accepts a wizard page object as a parameter and returns a Boolean that says if\n * the page you sent it is complete.\n *\n * @memberof PageCollectionService\n */\n previousPageIsCompleted(page: ClrWizardPage) {\n if (!page) {\n return false;\n }\n\n const previousPage = this.getPreviousPage(page);\n\n if (null === previousPage) {\n // page is the first page. no previous page.\n return true;\n }\n\n return previousPage.completed;\n }\n\n /**\n * Takes a wizard page object as a parameter and returns the wizard page object of\n * the page immediately after it in the page collection. Returns null if there is\n * no page after the page it is passed.\n *\n * @memberof PageCollectionService\n */\n getNextPage(page: ClrWizardPage) {\n const myPageIndex = this.getPageIndex(page);\n const nextPageIndex = myPageIndex + 1;\n\n if (nextPageIndex >= this.pagesAsArray.length) {\n return null;\n }\n return this.getPageByIndex(nextPageIndex);\n }\n\n /**\n * Takes a wizard page object as a parameter and generates a step item id from the\n * page ID. Returns the generated step item ID as a string.\n *\n * @memberof PageCollectionService\n */\n getStepItemIdForPage(page: ClrWizardPage) {\n const pageId = page.id;\n const pageIdParts = pageId.split('-').reverse();\n\n pageIdParts[1] = 'step';\n return pageIdParts.reverse().join('-');\n }\n\n /**\n * Generally only used internally to mark that a specific page has been \"committed\".\n * This involves marking the page complete and firing the ClrWizardPage.onCommit\n * (clrWizardPageOnCommit) output. Takes the wizard page object that you intend to\n * mark completed as a parameter.\n *\n * @memberof PageCollectionService\n */\n commitPage(page: ClrWizardPage) {\n const pageHasOverrides = page.stopNext || page.preventDefault;\n page.completed = true;\n\n if (!pageHasOverrides) {\n // prevent loop of event emission; alternate flows work off\n // of event emitters this is how they break that cycle.\n page.onCommit.emit(page.id);\n }\n }\n\n /**\n * Sets all completed states of the pages in the page collection to false and\n * notifies the navigation service to likewise reset the navigation.\n *\n * @memberof PageCollectionService\n */\n reset() {\n this.pagesAsArray.forEach((page: ClrWizardPage) => {\n page.completed = false;\n });\n this._pagesReset.next(true);\n }\n\n /**\n * Rolls through all the pages in the page collection to make sure there are no\n * incomplete pages sandwiched between completed pages in the workflow. Identifies\n * the first incomplete page index and sets all pages behind it to a completed\n * state of false.\n *\n * @memberof PageCollectionService\n */\n updateCompletedStates(): void {\n const firstIncompleteIndex = this.findFirstIncompletePageIndex();\n\n if (firstIncompleteIndex === this.pagesAsArray.length - 1) {\n // all complete no need to do anything\n return;\n }\n\n this.pagesAsArray.forEach((page: ClrWizardPage, index: number) => {\n if (index > firstIncompleteIndex) {\n page.completed = false;\n }\n });\n }\n\n /**\n * Retrieves the index of the first incomplete page in the page collection.\n *\n * @memberof PageCollectionService\n */\n findFirstIncompletePageIndex(): number {\n let returnIndex: number = null;\n this.pagesAsArray.forEach((page: ClrWizardPage, index: number) => {\n if (null === returnIndex && false === page.completed) {\n returnIndex = index;\n }\n });\n\n // fallthrough, all completed, return last page\n if (null === returnIndex) {\n returnIndex = this.pagesCount - 1;\n }\n\n return returnIndex;\n }\n\n findFirstIncompletePage(): ClrWizardPage {\n const myIncompleteIndex = this.findFirstIncompletePageIndex();\n return this.pagesAsArray[myIncompleteIndex];\n }\n\n /**\n * Consolidates guard logic that prevents a couple of unfortunate edge cases with\n * look ups on the collection of pages.\n *\n * @memberof PageCollectionService\n */\n private checkResults(results: ClrWizardPage[], requestedPageId: string) {\n const foundPagesCount: number = results.length || 0;\n\n if (foundPagesCount > 1) {\n throw new Error('More than one page has the requested id ' + requestedPageId + '.');\n } else if (foundPagesCount < 1) {\n throw new Error('No page can be found with the id ' + requestedPageId + '.');\n } else {\n return results[0];\n }\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Injectable, OnDestroy, TemplateRef } from '@angular/core';\nimport { Observable, Subject, Subscription } from 'rxjs';\n\nimport { ClrWizardStepnavLayout } from '../interfaces/wizard-stepnav-layout';\nimport { ClrWizardPage } from '../wizard-page';\nimport { ButtonHubService } from './button-hub.service';\nimport { PageCollectionService } from './page-collection.service';\n\n/**\n * Performs navigation functions for a wizard and manages the current page. Presented as a\n * separate service to encapsulate the behavior of navigating and completing the wizard so\n * that it can be shared across the wizard and its sub-components.\n *\n * The easiest way to access the navigation service is there a reference on your wizard. The\n * Following example would allow you to access your instance of the wizard from your host\n * component and thereby access the navigation service via YourHostComponent.wizard.navService.\n *\n * @example\n * <clr-wizard #wizard ...>\n *\n * @example\n * export class YourHostComponent {\n * @ViewChild(\"wizard\") wizard: Wizard;\n * ...\n * }\n *\n */\n@Injectable()\nexport class WizardNavigationService implements OnDestroy {\n /**\n * Is notified when a previous button is clicked in the wizard. Performs checks\n * before alerting the current page of the button click. Enacts navigation to\n * the previous page if not overridden at the page level.\n *\n * @memberof WizardNavigationService\n */\n previousButtonSubscription: Subscription;\n\n /**\n * Is notified when a Next button is clicked in the wizard.\n *\n * @memberof WizardNavigationService\n */\n nextButtonSubscription: Subscription;\n\n /**\n * Is notified when a danger button is clicked in the wizard.\n *\n * @memberof WizardNavigationService\n */\n dangerButtonSubscription: Subscription;\n\n /**\n * Is notified when a finish button is clicked in the wizard.\n *\n * @memberof WizardNavigationService\n */\n finishButtonSubscription: Subscription;\n\n /**\n * Is notified when a Custom button is clicked in the wizard.\n *\n * @memberof WizardNavigationService\n */\n customButtonSubscription: Subscription;\n\n /**\n * Is notified when a Cancel button is clicked in the wizard. Notifies the wizard,\n * which handles all cancel functionality, if cancel is not overridden at the page\n * level.\n *\n * @memberof WizardNavigationService\n */\n cancelButtonSubscription: Subscription;\n\n /**\n * Resets navigation to make the first page current when the page collection service\n * emits an event notifying WizardNavigationService that it has reset all pages\n * to their pristine, incomplete state.\n *\n * @memberof WizardNavigationService\n */\n pagesResetSubscription: Subscription;\n\n /**\n * A Boolean flag used by the ClrWizardPage to avoid a race condition when pages are\n * loading and there is no current page defined.\n *\n * @memberof WizardNavigationService\n */\n navServiceLoaded = false;\n\n /**\n * A boolean flag shared across the Wizard subcomponents that follows the value\n * of the Wizard.forceForward (clrWizardForceForwardNavigation) input. When true,\n * navigating backwards in the stepnav menu will reset any skipped pages' completed\n * state to false.\n *\n * This is useful when a wizard executes validation on a page-by-page basis when\n * the next button is clicked.\n *\n * @memberof WizardNavigationService\n */\n forceForwardNavigation = false;\n\n /**\n * A boolean flag shared across the Wizard subcomponents that follows the value\n * of the Wizard.stopCancel (clrWizardPreventDefaultCancel) input. When true, the cancel\n * routine is subverted and must be reinstated in the host component calling Wizard.close()\n * at some point.\n *\n * @memberof WizardNavigationService\n */\n wizardHasAltCancel = false;\n\n /**\n * A boolean flag shared across the Wizard subcomponents that follows the value\n * of the Wizard.stopNext (clrWizardPreventDefaultNext) input. When true, the next and finish\n * routines are subverted and must be reinstated in the host component calling Wizard.next(),\n * Wizard.forceNext(), Wizard.finish(), or Wizard.forceFinish().\n *\n * @memberof WizardNavigationService\n */\n wizardHasAltNext = false;\n\n /**\n * A boolean flag shared across the Wizard subcomponents that follows the value\n * of the Wizard.stopNavigation (clrWizardPreventNavigation) input. When true, all\n * navigational elements in the wizard are disabled.\n *\n * This is intended to freeze the wizard in place. Events are not fired so this is\n * not a way to implement alternate functionality for navigation.\n *\n * @memberof WizardNavigationService\n */\n wizardStopNavigation = false;\n\n /**\n * A boolean flag shared with the stepnav items that prevents user clicks on\n * stepnav items from navigating the wizard.\n *\n * @memberof WizardNavigationService\n */\n wizardDisableStepnav = false;\n\n /**\n * The layout of the wizard stepnav, either 'vertical' or 'horizontal'.\n */\n stepnavLayout: ClrWizardStepnavLayout = ClrWizardStepnavLayout.VERTICAL;\n\n /**\n * @memberof WizardNavigationService\n */\n private _currentPage: ClrWizardPage;\n\n /**\n *\n * @memberof WizardNavigationService\n */\n private _currentChanged = new Subject<ClrWizardPage>();\n\n /**\n * @memberof WizardNavigationService\n */\n private _movedToNextPage = new Subject<boolean>();\n\n /**\n * @memberof WizardNavigationService\n */\n private _wizardFinished = new Subject<void>();\n\n /**\n * @memberof WizardNavigationService\n */\n private _movedToPreviousPage = new Subject<boolean>();\n\n /**\n * @memberof WizardNavigationService\n */\n private _cancelWizard = new Subject<void>();\n\n /**\n * Creates an instance of WizardNavigationService. Also sets up subscriptions\n * that listen to the button service to determine when a button has been clicked\n * in the wizard. Is also responsible for taking action when the page collection\n * requests that navigation be reset to its pristine state.\n *\n * @memberof WizardNavigationService\n */\n constructor(\n public pageCollection: PageCollectionService,\n public buttonService: ButtonHubService\n ) {\n this.previousButtonSubscription = buttonService.previousBtnClicked.subscribe(() => {\n const currentPage = this.currentPage;\n if (this.currentPageIsFirst || currentPage.previousStepDisabled) {\n return;\n }\n currentPage.previousButtonClicked.emit(currentPage);\n if (!currentPage.preventDefault) {\n this.previous();\n }\n });\n\n this.nextButtonSubscription = buttonService.nextBtnClicked.subscribe(() => {\n this.checkAndCommitCurrentPage('next');\n });\n\n this.dangerButtonSubscription = buttonService.dangerBtnClicked.subscribe(() => {\n this.checkAndCommitCurrentPage('danger');\n });\n\n this.finishButtonSubscription = buttonService.finishBtnClicked.subscribe(() => {\n this.checkAndCommitCurrentPage('finish');\n });\n\n this.customButtonSubscription = buttonService.customBtnClicked.subscribe((type: string) => {\n if (!this.wizardStopNavigation) {\n this.currentPage.customButtonClicked.emit(type);\n }\n });\n\n this.cancelButtonSubscription = buttonService.cancelBtnClicked.subscribe(() => {\n if (this.wizardStopNavigation) {\n return;\n }\n\n if (this.currentPage.preventDefault) {\n this.currentPage.pageOnCancel.emit(this.currentPage);\n } else {\n this.cancel();\n }\n });\n\n this.pagesResetSubscription = pageCollection.pagesReset.subscribe(() => {\n this.setFirstPageCurrent();\n });\n }\n\n /**\n * An Observable that is predominantly used amongst the subcomponents and services\n * of the wizard. It is recommended that users listen to the ClrWizardPage.onLoad\n * (clrWizardPageOnLoad) output instead of this Observable.\n *\n * @memberof WizardNavigationService\n */\n get currentPageChange(): Observable<ClrWizardPage> {\n return this._currentChanged.asObservable();\n }\n\n /**\n * @memberof WizardNavigationService\n */\n get currentPageTitle(): TemplateRef<any> {\n // when the querylist of pages is empty. this is the first place it fails...\n if (!this.currentPage) {\n return null;\n }\n return this.currentPage.title;\n }\n\n /**\n * Returns a Boolean that tells you whether or not the current page is the first\n * page in the Wizard.\n *\n * This is helpful for determining whether a page is navigable.\n *\n * @memberof WizardNavigationService\n */\n get currentPageIsFirst(): boolean {\n return this.pageCollection.firstPage === this.currentPage;\n }\n\n /**\n * Returns a Boolean that tells you whether or not the current page is the\n * last page in the Wizard.\n *\n * This is used to determine which buttons should display in the wizard footer.\n *\n * @memberof WizardNavigationService\n */\n get currentPageIsLast(): boolean {\n return this.pageCollection.lastPage === this.currentPage;\n }\n\n /**\n * Returns the ClrWizardPage object of the current page or null.\n *\n * @memberof WizardNavigationService\n */\n get currentPage(): ClrWizardPage {\n if (!this._currentPage) {\n return null;\n }\n return this._currentPage;\n }\n\n /**\n * Accepts a ClrWizardPage object, since that object to be the current/active\n * page in the wizard, and emits the ClrWizardPage.onLoad (clrWizardPageOnLoad)\n * event for that page.\n *\n * Note that all of this work is bypassed if the ClrWizardPage object is already\n * the current page.\n *\n * @memberof WizardNavigationService\n */\n set currentPage(page: ClrWizardPage) {\n if (this._currentPage !== page && !this.wizardStopNavigation) {\n this._currentPage = page;\n page.onLoad.emit(page.id);\n this._currentChanged.next(page);\n }\n }\n\n /**\n * An observable used internally to alert the wizard that forward navigation\n * has occurred. It is recommended that you use the Wizard.onMoveNext\n * (clrWizardOnNext) output instead of this one.\n *\n * @memberof WizardNavigationService\n */\n get movedToNextPage(): Observable<boolean> {\n return this._movedToNextPage.asObservable();\n }\n\n /**\n * An observable used internally to alert the wizard that the nav service\n * has approved completion of the wizard.\n *\n * It is recommended that you use the Wizard.wizardFinished (clrWizardOnFinish)\n * output instead of this one.\n *\n * @memberof WizardNavigationService\n */\n get wizardFinished(): Observable<void> {\n return this._wizardFinished.asObservable();\n }\n\n /**\n * Notifies the wizard when backwards navigation has occurred via the\n * previous button.\n *\n * @memberof WizardNavigationService\n */\n get movedToPreviousPage(): Observable<boolean> {\n return this._movedToPreviousPage.asObservable();\n }\n\n /**\n * Notifies the wizard that a user is trying to cancel it.\n *\n * @memberof WizardNavigationService\n */\n get notifyWizardCancel(): Observable<any> {\n return this._cancelWizard.asObservable();\n }\n\n /**\n *\n * @memberof WizardNavigationService\n */\n ngOnDestroy(): void {\n this.previousButtonSubscription.unsubscribe();\n this.nextButtonSubscription.unsubscribe();\n this.dangerButtonSubscription.unsubscribe();\n this.finishButtonSubscription.unsubscribe();\n this.customButtonSubscription.unsubscribe();\n this.cancelButtonSubscription.unsubscribe();\n this.pagesResetSubscription.unsubscribe();\n }\n\n /**\n * This is a public function that can be used to programmatically advance\n * the user to the next page.\n *\n * When invoked, this method will move the wizard to the next page after\n * successful validation. Note that this method goes through all checks\n * and event emissions as if Wizard.next(false) had been called.\n *\n * In most cases, it makes more sense to use Wizard.next(false).\n *\n * @memberof WizardNavigationService\n */\n next(): void {\n if (this.currentPageIsLast) {\n this.checkAndCommitCurrentPage('finish');\n } else {\n this.checkAndCommitCurrentPage('next');\n }\n }\n\n /**\n * Bypasses checks and most event emissions to force a page to navigate forward.\n *\n * Comparable to calling Wizard.next() or Wizard.forceNext().\n *\n * @memberof WizardNavigationService\n */\n forceNext(): void {\n const currentPage: ClrWizardPage = this.currentPage;\n const nextPage: ClrWizardPage = this.pageCollection.getNextPage(currentPage);\n\n // catch errant null or undefineds that creep in\n if (!nextPage) {\n throw new Error('The wizard has no next page to go to.');\n }\n\n if (this.wizardStopNavigation) {\n return;\n }\n\n if (!currentPage.completed) {\n // this is a state that alt next flows can get themselves in...\n this.pageCollection.commitPage(currentPage);\n }\n this.currentPage = nextPage;\n }\n\n /**\n * Accepts a button/action type as a parameter. Encapsulates all logic for\n * event emissions, state of the current page, and wizard and page level overrides.\n *\n * Avoid calling this function directly unless you really know what you're doing.\n *\n * @memberof WizardNavigationService\n */\n checkAndCommitCurrentPage(buttonType: string): void {\n const currentPage: ClrWizardPage = this.currentPage;\n\n if (!currentPage.readyToComplete || this.wizardStopNavigation) {\n return;\n }\n\n const iAmTheLastPage = this.currentPageIsLast;\n\n const isNext = buttonType === 'next';\n const isDanger = buttonType === 'danger';\n const isDangerNext = isDanger && !iAmTheLastPage;\n const isDangerFinish = isDanger && iAmTheLastPage;\n const isFinish = buttonType === 'finish' || isDangerFinish;\n\n if (isFinish && !iAmTheLastPage) {\n return;\n }\n\n currentPage.primaryButtonClicked.emit(buttonType);\n\n if (isFinish) {\n currentPage.finishButtonClicked.emit(currentPage);\n } else if (isDanger) {\n currentPage.dangerButtonClicked.emit();\n } else if (isNext) {\n currentPage.nextButtonClicked.emit();\n }\n\n if (currentPage.stopNext || currentPage.preventDefault) {\n currentPage.onCommit.emit(currentPage.id);\n return;\n }\n\n // order is very important with these emitters!\n if (isFinish) {\n // mark page as complete\n if (!this.wizardHasAltNext) {\n this.pageCollection.commitPage(currentPage);\n }\n this._wizardFinished.next();\n }\n\n if (this.wizardHasAltNext) {\n this.pageCollection.commitPage(currentPage);\n\n if (isNext || isDangerNext) {\n this._movedToNextPage.next(true);\n }\n // jump out here, no matter what type we're looking at\n return;\n }\n\n if (isNext || isDangerNext) {\n this.forceNext();\n }\n\n if (!this.wizardHasAltNext && !this.wizardStopNavigation) {\n this._movedToNextPage.next(true);\n }\n }\n\n /**\n * This is a public function that can be used to programmatically conclude\n * the wizard.\n *\n * When invoked, this method will initiate the work involved with finalizing\n * and finishing the wizard workflow. Note that this method goes through all\n * checks and event emissions as if Wizard.finish(false) had been called.\n *\n * In most cases, it makes more sense to use Wizard.finish(false).\n *\n * @memberof WizardNavigationService\n */\n finish(): void {\n this.checkAndCommitCurrentPage('finish');\n }\n\n /**\n * Programmatically moves the wizard to the page before the current page.\n *\n * In most instances, it makes more sense to call Wizard.previous()\n * which does the same thing.\n *\n * @memberof WizardNavigationService\n */\n previous(): void {\n if (this.currentPageIsFirst || this.wizardStopNavigation) {\n return;\n }\n\n const previousPage = this.pageCollection.getPreviousPage(this.currentPage);\n\n if (!previousPage) {\n return;\n }\n\n this._movedToPreviousPage.next(true);\n\n if (this.forceForwardNavigation) {\n this.currentPage.completed = false;\n }\n\n this.currentPage = previousPage;\n }\n\n /**\n * Allows a hook into the cancel workflow of the wizard from the nav service. Note that\n * this route goes through all checks and event emissions as if a cancel button had\n * been clicked.\n *\n * In most cases, users looking for a hook into the cancel routine are actually looking\n * for a way to close the wizard from their host component because they have prevented\n * the default cancel action.\n *\n * In this instance, it is recommended that you use Wizard.close() to avoid any event\n * emission loop resulting from an event handler calling back into routine that will\n * again evoke the events it handles.\n *\n * @memberof WizardNavigationService\n */\n cancel(): void {\n this._cancelWizard.next();\n }\n\n /**\n * Performs all required checks to determine if a user can navigate to a page. Checking at each\n * point if a page is navigable -- completed where the page immediately after the last completed\n * page.\n *\n * Takes two parameters. The first one must be either the ClrWizardPage object or the ID of the\n * ClrWizardPage object that you want to make the current page.\n *\n * The second parameter is optional and is a Boolean flag for \"lazy completion\". What this means\n * is the Wizard will mark all pages between the current page and the page you want to navigate\n * to as completed. This is useful for informational wizards that do not require user action,\n * allowing an easy means for users to jump ahead.\n *\n * To avoid checks on navigation, use ClrWizardPage.makeCurrent() instead.\n *\n * @memberof WizardNavigationService\n */\n goTo(pageToGoToOrId: any, lazyComplete = false) {\n const myPages = this.pageCollection;\n const pageToGoTo = typeof pageToGoToOrId === 'string' ? myPages.getPageById(pageToGoToOrId) : pageToGoToOrId;\n const currentPage = this.currentPage;\n\n // no point in going to the current page. you're there already!\n // also hard block on any navigation when stopNavigation is true\n if (pageToGoTo === currentPage || this.wizardStopNavigation) {\n return;\n }\n\n const currentPageIndex = myPages.getPageIndex(currentPage);\n const goToPageIndex = myPages.getPageIndex(pageToGoTo);\n const goingForward = goToPageIndex > currentPageIndex;\n const pagesToCheck = myPages.getPageRangeFromPages(this.currentPage, pageToGoTo);\n const okayToMove = lazyComplete || this.canGoTo(pagesToCheck);\n\n if (!okayToMove) {\n return;\n }\n\n if (goingForward && lazyComplete) {\n pagesToCheck.forEach((page: ClrWizardPage) => {\n if (page !== pageToGoTo) {\n page.completed = true;\n }\n });\n } else if (!goingForward && this.forceForwardNavigation) {\n pagesToCheck.forEach((page: ClrWizardPage) => {\n page.completed = false;\n });\n }\n\n this.currentPage = pageToGoTo;\n }\n\n /**\n * Accepts a range of ClrWizardPage objects as a parameter. Performs the work of checking\n * those objects to determine if navigation can be accomplished.\n *\n * @memberof WizardNavigationService\n */\n canGoTo(pagesToCheck: ClrWizardPage[]): boolean {\n let okayToMove = true;\n const myPages = this.pageCollection;\n\n // previous page can be important when moving because if it's completed it\n // allows us to move to the page even if it's incomplete...\n let previousPagePasses: boolean;\n\n if (!pagesToCheck || pagesToCheck.length < 1) {\n return false;\n }\n\n pagesToCheck.forEach((page: ClrWizardPage) => {\n if (!okayToMove) {\n return;\n }\n\n if (page.completed) {\n // default is true. just jump out instead of complicating it.\n return;\n }\n\n // so we know our page is not completed...\n const previousPage = myPages.getPageIndex(page) > 0 ? myPages.getPreviousPage(page) : null;\n previousPagePasses = previousPage === null || previousPage.completed === true;\n\n // we are false if not the current page AND previous page is not completed\n // (but must have a previous page)\n if (!page.current && !previousPagePasses) {\n okayToMove = false;\n }\n // falls through to true as default\n });\n\n return okayToMove;\n }\n\n /**\n * Looks through the collection of pages to find the first one that is incomplete\n * and makes that page the current/active page.\n *\n * @memberof WizardNavigationService\n */\n setLastEnabledPageCurrent(): void {\n const allPages: ClrWizardPage[] = this.pageCollection.pagesAsArray;\n let lastCompletedPageIndex: number = null;\n\n allPages.forEach((page: ClrWizardPage, index: number) => {\n if (page.completed) {\n lastCompletedPageIndex = index;\n }\n });\n\n if (lastCompletedPageIndex === null) {\n // always is at least the first item...\n lastCompletedPageIndex = 0;\n } else if (lastCompletedPageIndex + 1 < allPages.length) {\n lastCompletedPageIndex = lastCompletedPageIndex + 1;\n }\n\n this.currentPage = allPages[lastCompletedPageIndex];\n }\n\n /**\n * Finds the first page in the collection of pages and makes that page the\n * current/active page.\n *\n * @memberof WizardNavigationService\n */\n setFirstPageCurrent(): void {\n this.currentPage = this.pageCollection.pagesAsArray[0];\n }\n\n /**\n * Updates the stepnav on the left side of the wizard when pages are dynamically\n * added or removed from the collection of pages.\n *\n * @memberof WizardNavigationService\n */\n updateNavigation(): void {\n let toSetCurrent: ClrWizardPage;\n\n this.pageCollection.updateCompletedStates();\n\n const currentPageRemoved = this.pageCollection.pagesAsArray.indexOf(this.currentPage) < 0;\n if (currentPageRemoved) {\n toSetCurrent = this.pageCollection.findFirstIncompletePage();\n this.currentPage = toSetCurrent;\n }\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Injectable, QueryList } from '@angular/core';\n\nimport { ClrWizardHeaderAction } from '../wizard-header-action';\nimport { WizardNavigationService } from './wizard-navigation.service';\n\n@Injectable()\nexport class HeaderActionService {\n // this service communicates information about the presence/display of header actions\n // across the wizard\n\n wizardHeaderActions: QueryList<ClrWizardHeaderAction>;\n\n constructor(public navService: WizardNavigationService) {}\n\n get wizardHasHeaderActions(): boolean {\n const wizardHdrActions = this.wizardHeaderActions;\n if (!wizardHdrActions) {\n return false;\n }\n return wizardHdrActions.toArray().length > 0;\n }\n\n get currentPageHasHeaderActions(): boolean {\n return this.navService.currentPage ? this.navService.currentPage.hasHeaderActions : false;\n }\n\n get showWizardHeaderActions(): boolean {\n return !this.currentPageHasHeaderActions && this.wizardHasHeaderActions;\n }\n\n get displayHeaderActionsWrapper(): boolean {\n return this.currentPageHasHeaderActions || this.wizardHasHeaderActions;\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\n\nimport { ButtonHubService } from './providers/button-hub.service';\nimport { WizardNavigationService } from './providers/wizard-navigation.service';\n\nexport const DEFAULT_BUTTON_TYPES: any = {\n cancel: 'cancel',\n previous: 'previous',\n next: 'next',\n finish: 'finish',\n danger: 'danger',\n};\n\nexport const CUSTOM_BUTTON_TYPES: any = {\n cancel: 'custom-cancel',\n previous: 'custom-previous',\n next: 'custom-next',\n finish: 'custom-finish',\n danger: 'custom-danger',\n};\n\n@Component({\n selector: 'clr-wizard-button',\n template: `\n <button\n type=\"button\"\n class=\"btn clr-wizard-btn\"\n [class.btn-link]=\"isCancel\"\n [class.clr-wizard-btn--tertiary]=\"isCancel\"\n [class.btn-outline]=\"isPrevious\"\n [class.clr-wizard-btn--secondary]=\"isPrevious\"\n [class.btn-primary]=\"isPrimaryAction\"\n [class.clr-wizard-btn--primary]=\"isPrimaryAction\"\n [class.btn-success]=\"isFinish\"\n [class.btn-danger]=\"isDanger\"\n [class.disabled]=\"isDisabled\"\n [attr.disabled]=\"_disabledAttribute\"\n (click)=\"click()\"\n >\n <ng-content></ng-content>\n </button>\n `,\n host: { class: 'clr-wizard-btn-wrapper', '[attr.aria-hidden]': 'isHidden' },\n standalone: false,\n})\nexport class ClrWizardButton {\n @Input('type') type = '';\n\n @Input('clrWizardButtonDisabled') disabled = false;\n\n @Input('clrWizardButtonHidden') hidden = false;\n\n // EventEmitter which is emitted when a button is clicked.\n @Output('clrWizardButtonClicked') wasClicked = new EventEmitter<string>(false);\n\n constructor(\n public navService: WizardNavigationService,\n public buttonService: ButtonHubService\n ) {}\n\n get isCancel(): boolean {\n return this.checkDefaultAndCustomType(this.type, 'cancel');\n }\n\n get isNext(): boolean {\n return this.checkDefaultAndCustomType(this.type, 'next');\n }\n\n get isPrevious(): boolean {\n return this.checkDefaultAndCustomType(this.type, 'previous');\n }\n\n get isFinish(): boolean {\n return this.checkDefaultAndCustomType(this.type, 'finish');\n }\n\n get isDanger(): boolean {\n return this.checkDefaultAndCustomType(this.type, 'danger');\n }\n\n get isPrimaryAction(): boolean {\n return this.isNext || this.isDanger || this.isFinish;\n }\n\n get _disabledAttribute(): string | null {\n if (this.isDisabled) {\n return '';\n }\n return null;\n }\n\n get isDisabled(): boolean {\n // dealing with negatives here. cognitively easier to think of it like this...\n const disabled = true;\n const nav = this.navService;\n const page = this.navService.currentPage;\n\n // Ensure we don't change the response until buttons are ready to avoid chocolate\n if (!this.buttonService.buttonsReady) {\n return !disabled;\n }\n\n if (this.disabled || nav.wizardStopNavigation || !page) {\n return true;\n }\n\n if (this.isCancel) {\n return !disabled;\n }\n\n if (this.isPrevious && (nav.currentPageIsFirst || page.previousStepDisabled)) {\n return disabled;\n }\n\n if (this.isDanger && !page.readyToComplete) {\n return disabled;\n }\n\n if (this.isNext && (nav.currentPageIsLast || !page.readyToComplete)) {\n return disabled;\n }\n\n if (this.isFinish && (!nav.currentPageIsLast || !page.readyToComplete)) {\n return disabled;\n }\n\n return !disabled;\n }\n\n get isHidden(): boolean {\n // dealing with negatives here. cognitively easier to think of it like this...\n const hidden = true;\n const nav = this.navService;\n\n // Ensure we don't change the response until buttons are ready to avoid chocolate\n if (!this.buttonService.buttonsReady) {\n return !hidden;\n }\n\n if (this.hidden) {\n return true;\n }\n\n if (this.isCancel) {\n return !hidden;\n }\n\n if (this.isPrevious && nav.currentPageIsFirst) {\n return hidden;\n }\n\n if (this.isNext && nav.currentPageIsLast) {\n return hidden;\n }\n\n if (this.isFinish && !nav.currentPageIsLast) {\n return hidden;\n }\n\n return !hidden;\n }\n\n click(): void {\n if (this.isDisabled) {\n return;\n }\n\n this.wasClicked.emit(this.type);\n this.buttonService.buttonClicked(this.type);\n }\n\n private checkDefaultAndCustomType(valueToCheck = '', typeToLookUp: string) {\n if (DEFAULT_BUTTON_TYPES[typeToLookUp] === valueToCheck) {\n return true;\n }\n if (CUSTOM_BUTTON_TYPES[typeToLookUp] === valueToCheck) {\n return true;\n }\n return false;\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\n\nlet wizardHeaderActionIndex = 0;\n\n@Component({\n selector: 'clr-wizard-header-action',\n template: `\n <button\n type=\"button\"\n class=\"btn clr-wizard-header-action btn-link\"\n [id]=\"id\"\n [class.disabled]=\"disabled\"\n (click)=\"click()\"\n [title]=\"title\"\n >\n <ng-content></ng-content>\n </button>\n `,\n host: { class: 'clr-wizard-header-action-wrapper' },\n standalone: false,\n})\nexport class ClrWizardHeaderAction {\n // title is explanatory text added to the header action\n @Input('title') title = '';\n\n // If our host has an ID attribute, we use this instead of our index.\n @Input('id') _id: string = (wizardHeaderActionIndex++).toString();\n\n @Input('clrWizardHeaderActionDisabled') disabled = false;\n\n @Output('actionClicked') headerActionClicked = new EventEmitter<string>(false);\n\n get id(): string {\n return `clr-wizard-header-action-${this._id}`;\n }\n\n click(): void {\n if (this.disabled) {\n return;\n }\n\n // passing the header action id allows users to have one method that\n // routes to many different actions based on the type of header action\n // clicked. this is further aided by users being able to specify ids\n // for their header actions.\n this.headerActionClicked.emit(this._id);\n }\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Directive, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[clrPageButtons]',\n standalone: false,\n})\nexport class ClrWizardPageButtons {\n constructor(public pageButtonsTemplateRef: TemplateRef<any>) {}\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Directive, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[clrPageHeaderActions]',\n standalone: false,\n})\nexport class ClrWizardPageHeaderActions {\n constructor(public pageHeaderActionsTemplateRef: TemplateRef<any>) {}\n}\n","/*\n * Copyright (c) 2016-2026 Broadcom. All Rights Reserved.\n * The term \"Broadcom\" refers to Broadcom Inc. and/or its subsidiaries.\n * This software is released under MIT license.\n * The full license information can be found in LICENSE in the root directory of this project.\n */\n\nimport { Directive, TemplateRef } from '@angular/core';\n\n@Directive({\n selector: '[clrPageNavTitle]',\n standalone: false,\n})\nexport class ClrWizardPageNavTitle {\n constructor(public pageNavTitleTemplateRef: TemplateRef<any>) {}\n}\n","/*\n