UNPKG

@microsoft/windows-admin-center-sdk

Version:

Microsoft - Windows Admin Center Shell

1,003 lines 149 kB
import { __decorate, __metadata } from "tslib"; import { ChangeDetectorRef, Component, ComponentFactoryResolver, ElementRef, EventEmitter, forwardRef, Inject, Injector, Input, Optional, Output, SkipSelf, ViewChild } from '@angular/core'; import { Debounce } from '@microsoft/windows-admin-center-sdk/core/base/decorators/debounce.decorators'; import { Yield } from '@microsoft/windows-admin-center-sdk/core/base/decorators/yield.decorator'; import { RxjsLifetimeManager } from '@microsoft/windows-admin-center-sdk/core/base/rxjs-lifetime-manager'; import { Logging } from '@microsoft/windows-admin-center-sdk/core/diagnostics/logging'; import { SmeWebTelemetry } from '@microsoft/windows-admin-center-sdk/core/diagnostics/sme-web-telemetry'; import { TelemetryActionTypes } from '@microsoft/windows-admin-center-sdk/core/diagnostics/sme-web-telemetry-models'; import { EnvironmentModule } from '@microsoft/windows-admin-center-sdk/core/manifest/environment-modules'; import { take } from 'rxjs/operators'; import { AppContextService } from '../../service/app-context.service'; import { DynamicComponentBase } from '../common/dynamic.component'; import { SME_LAYOUT_PROVIDER } from '../common/layout'; import { HealthAlertSeverity } from '../page-alert-bar/models/page-alert'; import { WizardBuilder } from './wizard-builder'; import { WizardNavigationInformation } from './wizard-navigation-information'; import { WizardResponsiveWindowManager } from './wizard-responsive-window-manager'; import * as i0 from "@angular/core"; import * as i1 from "../../service/app-context.service"; import * as i2 from "@angular/common"; import * as i3 from "../header/header.component"; import * as i4 from "../loading-wheel/loading-wheel.component"; import * as i5 from "../../directives/telemetry/telemetry.directive"; import * as i6 from "../tooltip/tooltip.directive"; import * as i7 from "../page-alert-bar/page-alert-bar.component"; import * as i8 from "../badge/preview-badge.component"; import * as i9 from "../banner/banner.component"; const _c0 = ["wizardFooter"]; function WizardComponent_div_1_sme_badge_3_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "sme-badge", 14); } } function WizardComponent_div_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 11)(1, "h3", 12); i0.ɵɵtext(2); i0.ɵɵelementEnd(); i0.ɵɵtemplate(3, WizardComponent_div_1_sme_badge_3_Template, 1, 0, "sme-badge", 13); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r0 = i0.ɵɵnextContext(); i0.ɵɵattribute("aria-readonly", true); i0.ɵɵadvance(2); i0.ɵɵtextInterpolate(ctx_r0.heading); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx_r0.isInPreview); } } function WizardComponent_sme_header_2_sme_badge_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelement(0, "sme-badge", 17); } } function WizardComponent_sme_header_2_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "sme-header", 15); i0.ɵɵtemplate(1, WizardComponent_sme_header_2_sme_badge_1_Template, 1, 0, "sme-badge", 16); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r1 = i0.ɵɵnextContext(); i0.ɵɵproperty("text", ctx_r1.headerData.text)("smeIconClassName", ctx_r1.headerData.smeIconClassName)("subtitle", ctx_r1.headerData.subtitle); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx_r1.isInPreview); } } function WizardComponent_div_3_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div"); i0.ɵɵelement(1, "sme-banner", 18); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵadvance(1); i0.ɵɵproperty("items", ctx_r2.bannerItems); } } const _c1 = function (a0, a1, a2) { return { "sme-wizard-stage-completed": a0, "sme-wizard-stage-clicked": a1, "sme-wizard-stage-active": a2 }; }; const _c2 = function (a0) { return { "sme-icon sme-icon-accept sme-icon-size-xxs": a0 }; }; const _c3 = function (a0, a1, a2, a3) { return { "sme-font-emphasis1 sme-wizard-stage-active": a0, "sme-wizard-stage-completed": a1, "sme-margin-left-xs": a2, "sme-layout-none": a3 }; }; function WizardComponent_div_4_button_1_Template(rf, ctx) { if (rf & 1) { const _r14 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "button", 21); i0.ɵɵlistener("click", function WizardComponent_div_4_button_1_Template_button_click_0_listener() { const restoredCtx = i0.ɵɵrestoreView(_r14); const val_r12 = restoredCtx.index; const ctx_r13 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r13.onStageClick(val_r12)); }); i0.ɵɵelementStart(1, "span", 22); i0.ɵɵtext(2); i0.ɵɵelementEnd(); i0.ɵɵelementStart(3, "span", 23); i0.ɵɵtext(4); i0.ɵɵelementEnd()(); } if (rf & 2) { const stage_r11 = ctx.$implicit; const val_r12 = ctx.index; const ctx_r10 = i0.ɵɵnextContext(2); i0.ɵɵclassProp("sme-active", val_r12 === ctx_r10.stagesArrayIndex)("sme-selected", val_r12 === ctx_r10.stagesArrayIndex)("aria-selected", val_r12 === ctx_r10.stagesArrayIndex)("sme-wizard-stage-focus", val_r12 === ctx_r10.stagesArrayIndex); i0.ɵɵproperty("title", stage_r11.name)("smeTelemetryDataBlob", ctx_r10.telemetryData)("smeTelemetryId", "wizardStage" + (val_r12 + 1))("disabled", ctx_r10.stageIndexDisabled(stage_r11, val_r12))("smeTelemetryDisableElement", ctx_r10.stageIndexDisabled(stage_r11, val_r12))("ngClass", i0.ɵɵpureFunction3(19, _c1, stage_r11.completed, val_r12 === ctx_r10.stagesArrayIndex, stage_r11.active)); i0.ɵɵattribute("aria-label", stage_r11.name); i0.ɵɵadvance(1); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(23, _c2, stage_r11.completed)); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate1(" ", !stage_r11.completed ? val_r12 + 1 : "", " "); i0.ɵɵadvance(1); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction4(25, _c3, val_r12 === ctx_r10.stagesArrayIndex || stage_r11.active, stage_r11.completed, !stage_r11.completed, !ctx_r10.shouldRenderTextOnSteps)); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate1(" ", stage_r11.name, " "); } } function WizardComponent_div_4_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 19); i0.ɵɵtemplate(1, WizardComponent_div_4_button_1_Template, 5, 30, "button", 20); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r3 = i0.ɵɵnextContext(); i0.ɵɵadvance(1); i0.ɵɵproperty("ngForOf", ctx_r3.stages); } } function WizardComponent_div_6_div_1_button_1_span_3_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "span", 34); i0.ɵɵtext(1); i0.ɵɵelementEnd(); } if (rf & 2) { const step_r17 = i0.ɵɵnextContext().$implicit; i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(step_r17.name); } } const _c4 = function (a0) { return { "sme-step-active sme-scheme-nav-list-item sme-active sme-color-base-5": a0 }; }; function WizardComponent_div_6_div_1_button_1_Template(rf, ctx) { if (rf & 1) { const _r22 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "button", 31); i0.ɵɵlistener("click", function WizardComponent_div_6_div_1_button_1_Template_button_click_0_listener() { const restoredCtx = i0.ɵɵrestoreView(_r22); const index_r18 = restoredCtx.index; const ctx_r21 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r21.onStepClick(index_r18)); }); i0.ɵɵelementStart(1, "span", 32); i0.ɵɵtext(2); i0.ɵɵelementEnd(); i0.ɵɵtemplate(3, WizardComponent_div_6_div_1_button_1_span_3_Template, 2, 1, "span", 33); i0.ɵɵelementEnd(); } if (rf & 2) { const step_r17 = ctx.$implicit; const index_r18 = ctx.index; const ctx_r16 = i0.ɵɵnextContext(3); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(10, _c4, index_r18 === ctx_r16.stepIndex))("disabled", ctx_r16.stepIndexDisabled(step_r17, index_r18))("smeTelemetryDisableElement", ctx_r16.stepIndexDisabled(step_r17, index_r18))("smeTelemetryDataBlob", ctx_r16.telemetryData)("smeTelemetryId", "wizardStep" + (index_r18 + 1))("title", ctx_r16.isExpanded ? null : step_r17.name); i0.ɵɵattribute("aria-label", ctx_r16.isExpanded ? null : step_r17.name); i0.ɵɵadvance(2); i0.ɵɵtextInterpolate2("", ctx_r16.stagesArrayIndex + 1, ".", index_r18 + 1, ""); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx_r16.isExpanded); } } const _c5 = function (a0) { return { "sme-wizard-left-pane-min-width-expanded": a0 }; }; function WizardComponent_div_6_div_1_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 29); i0.ɵɵtemplate(1, WizardComponent_div_6_div_1_button_1_Template, 4, 12, "button", 30); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r15 = i0.ɵɵnextContext(2); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(2, _c5, ctx_r15.isExpanded)); i0.ɵɵadvance(1); i0.ɵɵproperty("ngForOf", ctx_r15.getStepsForStage()); } } const _c6 = function (a0, a1) { return { "sme-icon-chevronLeft": a0, "sme-icon-chevronRight": a1 }; }; function WizardComponent_div_6_Template(rf, ctx) { if (rf & 1) { const _r24 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "div", 24); i0.ɵɵtemplate(1, WizardComponent_div_6_div_1_Template, 2, 4, "div", 25); i0.ɵɵelementStart(2, "div", 26)(3, "button", 27); i0.ɵɵlistener("click", function WizardComponent_div_6_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r24); const ctx_r23 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r23.togglePane()); }); i0.ɵɵelement(4, "span", 28); i0.ɵɵelementEnd()()(); } if (rf & 2) { const ctx_r4 = i0.ɵɵnextContext(); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", !ctx_r4.wizardIsFinished && !ctx_r4.hideStepsListSidePane); i0.ɵɵadvance(2); i0.ɵɵproperty("title", ctx_r4.isExpanded ? ctx_r4.strings.MsftSmeShell.Angular.Wizard.collapseStepsList : ctx_r4.strings.MsftSmeShell.Angular.Wizard.expandStepsList); i0.ɵɵattribute("aria-expanded", ctx_r4.isExpanded)("aria-label", ctx_r4.isExpanded ? ctx_r4.strings.MsftSmeShell.Angular.Wizard.collapseStepsList : ctx_r4.strings.MsftSmeShell.Angular.Wizard.expandStepsList); i0.ɵɵadvance(1); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction2(5, _c6, ctx_r4.isExpanded, !ctx_r4.isExpanded)); } } function WizardComponent_div_8_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div"); i0.ɵɵelement(1, "sme-page-alert-bar", 35); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r5 = i0.ɵɵnextContext(); i0.ɵɵadvance(1); i0.ɵɵproperty("alert", ctx_r5.alert); } } function WizardComponent_div_11_div_7_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 44); i0.ɵɵelement(1, "sme-loading-wheel", 45); i0.ɵɵelementEnd(); } } function WizardComponent_div_11_button_10_Template(rf, ctx) { if (rf & 1) { const _r30 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "button", 46, 47); i0.ɵɵlistener("click", function WizardComponent_div_11_button_10_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r30); const ctx_r29 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r29.onExitClick()); }); i0.ɵɵtext(2); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r27 = i0.ɵɵnextContext(2); i0.ɵɵproperty("smeTelemetryDataBlob", ctx_r27.telemetryData); i0.ɵɵadvance(2); i0.ɵɵtextInterpolate(ctx_r27.exitButtonText()); } } function WizardComponent_div_11_Template(rf, ctx) { if (rf & 1) { const _r32 = i0.ɵɵgetCurrentView(); i0.ɵɵelementStart(0, "div", 36, 37)(2, "div", 38)(3, "button", 39); i0.ɵɵlistener("click", function WizardComponent_div_11_Template_button_click_3_listener() { i0.ɵɵrestoreView(_r32); const ctx_r31 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r31.onBackClick()); }); i0.ɵɵtext(4); i0.ɵɵelementEnd(); i0.ɵɵelementStart(5, "button", 40); i0.ɵɵlistener("click", function WizardComponent_div_11_Template_button_click_5_listener() { i0.ɵɵrestoreView(_r32); const ctx_r33 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r33.onNextClick()); }); i0.ɵɵtext(6); i0.ɵɵelementEnd(); i0.ɵɵtemplate(7, WizardComponent_div_11_div_7_Template, 2, 0, "div", 41); i0.ɵɵelementEnd(); i0.ɵɵelementStart(8, "button", 42); i0.ɵɵlistener("click", function WizardComponent_div_11_Template_button_click_8_listener() { i0.ɵɵrestoreView(_r32); const ctx_r34 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r34.onSkipClick()); }); i0.ɵɵtext(9); i0.ɵɵelementEnd(); i0.ɵɵtemplate(10, WizardComponent_div_11_button_10_Template, 3, 2, "button", 43); i0.ɵɵelementEnd(); } if (rf & 2) { const ctx_r7 = i0.ɵɵnextContext(); i0.ɵɵadvance(2); i0.ɵɵclassProp("sme-padding-right-xl", !ctx_r7.validating); i0.ɵɵadvance(1); i0.ɵɵclassProp("sme-layout-none", !ctx_r7.canNavigateToPreviousStep); i0.ɵɵproperty("smeTelemetryDataBlob", ctx_r7.telemetryData)("disabled", ctx_r7.disableBackButton); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(ctx_r7.strings.MsftSmeShell.Angular.Common.back); i0.ɵɵadvance(1); i0.ɵɵproperty("smeTelemetryDataBlob", ctx_r7.telemetryData)("disabled", ctx_r7.disableNextButton)("smeTelemetryDisableElement", ctx_r7.disableNextButton); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate1(" ", ctx_r7.nextButtonText(), ""); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx_r7.validating); i0.ɵɵadvance(1); i0.ɵɵclassProp("sme-layout-none", !ctx_r7.showSkipButton); i0.ɵɵproperty("smeTelemetryDataBlob", ctx_r7.telemetryData); i0.ɵɵadvance(1); i0.ɵɵtextInterpolate(ctx_r7.strings.MsftSmeShell.Angular.Common.skip); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx_r7.showExitButton); } } const _c7 = function (a0) { return { "sme-padding-inset-lg": a0 }; }; /** * Whatever state the wizard is in */ var WizardState; (function (WizardState) { WizardState["Exit"] = "Exit"; WizardState["Entry"] = "Entry"; WizardState["Finished"] = "Finished"; WizardState["Active"] = "Active"; })(WizardState || (WizardState = {})); /** * Data to collect in wizard */ class WizardTelemetryData { } /** * @smeDoc {@label Wizard @id sme-wizard} * * @overview * @file {@filepath ./examples/overview.md} * * @example {@label Example Wizard @id simple-wizard} * @file {@filename component.html @filepath ./examples/wizard-example.component.html} * @file {@filename component.ts @filepath ./examples/wizard-example.component.ts} * @file {@filename data-model.ts @filepath ./examples/example-model.ts} * @file {@filename finish-component.ts @filepath ./examples/finish.component.ts} * @file {@filename finish-component.html @filepath ./examples/finish.component.html} * @file {@filename simple-static-text.html @filepath ./examples/simple-static-text.component.html} * @file {@filename simple-static-text.ts @filepath ./examples/simple-static-text.ts} * @file {@filename simple-text-input.component.html @filepath ./examples/simple-text-input.component.html} * @file {@filename simple-text-input.component.ts @filepath ./examples/simple-text-input.component.ts} */ /** * The component class definition for the wizard component. */ export class WizardComponent extends DynamicComponentBase { /** * Initializes a new instance of the WizardComponent class. * @param appContext - The application context service class. * @param changeDetectorRef - The change detector. * @param componentFactoryResolver - The component factory resolver. */ constructor(injector, hostElement, appContext, changeDetectorRef, componentFactoryResolver, layout) { super(componentFactoryResolver, injector); this.hostElement = hostElement; this.appContext = appContext; this.changeDetectorRef = changeDetectorRef; /** * Telemetry data collected while navigating through wizard. By default, start at 1.1 step index and * Entry state. * Keep same format as telemetry event timestamp */ this.telemetryData = { stepIndexNumber: '1.1', stepName: '', stepIndexStartTime: (new Date()).toISOString(), entryPoint: EnvironmentModule.createEntrypointId(MsftSme.self().Init.moduleName, MsftSme.self().Init.entryPointName), state: WizardState.Entry }; /** * Occurs when the wizard is exited. */ this.exit = new EventEmitter(); /** * Occurs whenever a step is submitted. */ this.stepSubmitted = new EventEmitter(); /** * Occurs whenever a step index is changed. */ this.stepChanged = new EventEmitter(); /** * Occurs whenever step validation fails */ this.stepInvalidated = new EventEmitter(); /** * Occurs when the wizard is finished */ this.finished = new EventEmitter(); /** * It implements the ILayout interface. It's triggered when the layout is changed. * It's used to tell the child components to coordinate with the layout change. */ this.layoutChanged = new EventEmitter(); /** * Implementation of the Layout interface */ this.windowBreakpointChanged = new EventEmitter(); /** * Resource strings for the component. */ this.strings = MsftSme.getStrings(); /** * It dynamically check if any of the header step content will fail to render. */ this.shouldRenderTextOnSteps = true; /** * The alert for a disabled step. */ this.alert = { severity: HealthAlertSeverity.Info, message: this.strings.MsftSmeShell.Angular.Wizard.disabledStepAlert }; this.modelChange = new EventEmitter(); this.isExpanded = true; if (layout) { this.wizardResponsiveWindowManager = new WizardResponsiveWindowManager(layout); // on parent layout changes, re-emit the layout change to our event this.subscriptions.push(layout.layoutChanged.subscribe((() => this.onLayoutChanged()))); // on window break point changes, emit the event about critical classes and styling updates signal. this.subscriptions.push(layout.windowBreakpointChanged.subscribe(() => this.onWindowBreakpointChanged())); this.deferredOnLayoutChanged(); } } /** * Sets the data model for sharing data across the wizard. */ set model(value) { this.internalModel = value; if (this.currentStepComponent) { this.currentStepComponent.onWizardModelChanged(this.model); } } /** * Gets the data model for sharing data across the wizard. */ get model() { return this.internalModel; } /** * Whether an exit button will be shown throughout the wizard. */ get showExitButton() { return !!this.wizardBuilder.showExitButton; } /** * Create and return the current telemetry event structure for wizard telemetry. This can be used for any automatic * actions not triggered by a DOM interaction. */ get wizardTelemetryEventBlob() { const wizardTelemetryBase = { contentId: 'sme-wizard', areaName: 'WizardComponent' }; MsftSme.deepAssign(wizardTelemetryBase, this.telemetryData); return wizardTelemetryBase; } /** * Gets the index of the current stage of the wizard. */ get stagesArrayIndex() { return this.stageIndex; } /** * Gets the index of the current step of the wizard. */ get stepIndex() { return this.index; } /** * Gets the current stage of the wizard. */ get currentStage() { return this.stages[this.stagesArrayIndex]; } /** * Gets the current step of the wizard. */ get currentStep() { return this.currentStage.steps[this.stepIndex]; } /** * Gets the finish view of the wizard. */ get finishStep() { return this.finishView; } /** * Gets the current steps for the current stage. */ getStepsForStage() { return this.currentStage.steps; } /** * Gets the dynamically rendered component of the current step. */ get currentStepComponent() { return this.ref ? this.ref.instance : null; } get onLastStepOfStage() { return this.currentStage.steps.length - 1 === this.stepIndex; } get onLastStage() { return this.stages.length - 1 === this.stageIndex; } get onLastStepOfLastStage() { return this.onLastStage && this.onLastStepOfStage; } deferredOnLayoutChanged() { this.onLayoutChanged(); } onLayoutChanged() { this.layoutChanged.emit(); } /** * The on window breakpoint changed handler. * Adding yield for component to get ElementRef after initialization. */ onWindowBreakpointChanged() { if (!this.wizardFooter) { return; } this.wizardResponsiveWindowManager.onWindowSizeChanged(this.wizardFooter); setTimeout(() => { this.isExpanded = this.wizardResponsiveWindowManager.isExpanded; }); } // Function to determine if stage is disabled or not. stageIndexDisabled(stage, index) { return !stage.completed && index !== this.stagesArrayIndex && !stage.active; } // Function to determine if step is disabled or not. stepIndexDisabled(step, index) { return !step.completed && index !== this.stepIndex && !step.disabled; } /** * Gets the text for the next button. */ nextButtonText() { if (this.stages[this.stageIndex].steps.length - 1 === this.stepIndex) { if (this.stageIndex === this.stages.length - 1) { return this.finishButtonTitle ? this.finishButtonTitle : this.strings.MsftSmeShell.Angular.Common.finish; } else { return this.strings.MsftSmeShell.Angular.Wizard.stageName.format(this.stages[this.stageIndex + 1].name); } } else { return this.strings.MsftSmeShell.Angular.Common.next; } } /** * Gets the text for the exit/finish button. */ exitButtonText() { return this.exitButtonTitle ? this.exitButtonTitle : this.strings.MsftSmeShell.Angular.Common.exit; } /** * Gets the disabled state of the wizard buttons */ get disableBackButton() { return (this.stagesArrayIndex === 0 && this.stepIndex === 0) || this.validating; } get disableNextButton() { if (this.currentStepComponent.disabled) { return false; } else { return !this.currentStepComponent.valid || this.validating || this.currentStepComponent.isBusy; } } get showSkipButton() { return !this.onLastStepOfLastStage && this.currentStepComponent.canBeSkipped; } /** * Sets the index of the current step of the wizard. */ set stepIndex(value) { if (!this.currentStage.steps[value]) { Logging.logError('WizardComponent.stepIndex setter', 'Attempted to set stepIndex to invalid value.', { targetIndex: value, steps: this.currentStage.steps }); } else { this.index = value; this.cleanComponent(); this.componentType = this.currentStep.dynamicStepComponent; this.createComponent(); this.emitStepChanged(); this.preserveModelForCurrentStep(); this.changeDetectorRef.detectChanges(); } } /** * Sets the index of the current stage of the wizard. */ set stagesArrayIndex(value) { if (!this.stages[value]) { Logging.logError('WizardComponent.stageStepIndex setter', 'Attempted to set stagesArrayIndex to invalid value.', { targetIndex: value, steps: this.stages }); } else { this.stageIndex = value; } } /** * Marks the current stage of the wizard as complete. * * @returns True if the stage was completed and false if not. */ completeCurrentStage() { if (this.currentStage) { this.currentStage.complete(); } return this.currentStage.completed; } /** * Marks the current step of the wizard as complete. * * @returns True if the step was completed and false if not. */ completeCurrentStep() { if (this.currentStep && !this.currentStep.disabled) { this.currentStep.complete(); } return this.currentStep.completed; } /** * Marks the current step of the wizard as incomplete. * * @returns True if the step was failed and false if not. */ failCurrentStep() { if (this.currentStep) { this.currentStep.fail(); } return !this.currentStep.completed; } /** * Moves the wizard to the next visible step in the list. */ moveToNextStep() { this.navigateToNextStep(); while (this.currentStepComponent.disabled) { this.currentStep.disabled = true; if (this.onLastStepOfLastStage) { this.handleFinishNavigation(); return; } this.navigateToNextStep(); } if (this.telemetryData.state === WizardState.Entry) { // Switch to active after our first move to next or previous step // Manually call it here b/c some modules will update stepIndex after loading, which triggers emitStepChanged. this.updateTelemetryTags(WizardState.Active); } this.emitStepChanged(); } /** * Handles the indexing for moving to the next step. */ navigateToNextStep() { if (!this.onLastStepOfStage) { this.stepIndex++; } else if (!this.onLastStage) { this.stageIndex++; while (this.stages[this.stageIndex].steps.length === 0) { this.stageIndex++; } this.stepIndex = 0; } this.emitStepChanged(); } /** * Moves the wizard to the previous (visible) step in the list. */ moveToPreviousStep() { this.navigateToPreviousStep(); while (this.currentStepComponent.disabled) { this.navigateToPreviousStep(); } if (this.telemetryData.state === WizardState.Entry) { // Switch to active after our first move to next or previous step this.telemetryData.state = WizardState.Active; } this.emitStepChanged(); } /** * Handles the indexing for moving back to last step. */ navigateToPreviousStep() { if (this.stepIndex === 0) { if (this.stageIndex !== 0) { this.stages[this.stageIndex].active = false; this.stageIndex--; while (this.stages[this.stageIndex].steps.length === 0) { this.stageIndex--; } this.stepIndex = this.stages[this.stageIndex].steps.length - 1; this.stages[this.stageIndex].completed = false; } } else { this.stepIndex--; } this.emitStepChanged(); } /** * The method that focuses on an active step or stage */ focusOnActiveStep() { const selector = this.hideStepsListSidePane ? 'sme-wizard-stage-focus' : 'sme-step-active'; const focusable = document.getElementsByClassName(selector); if (focusable.length > 0) { focusable[0].focus(); } } /** * The method to run when the component is initialized. */ ngOnInit() { this.navigationInformation = new WizardNavigationInformation(this, this.wizardBuilder); this.stages = this.wizardBuilder.stages; this.bannerItems = this.wizardBuilder.bannerItems; this.heading = this.wizardBuilder.heading; this.headerData = this.wizardBuilder.headerData; this.exitButtonTitle = this.wizardBuilder.exitButtonTitle; this.finishButtonTitle = this.wizardBuilder.finishButtonTitle; this.finishView = this.wizardBuilder.finishView; this.hideStepsListSidePane = this.wizardBuilder.hideStepsListSidePane; this.showExitConfirmationDialog = this.wizardBuilder.showExitConfirmationDialog; this.isInPreview = this.wizardBuilder.isInPreviewState; this.canNavigateToPreviousStep = this.wizardBuilder.canNavigateToPreviousStep; this.stagesArrayIndex = 0; this.stepIndex = 0; super.ngOnInit(); if (!this.stages) { Logging.logError('WizardComponent.ngOnInit', `The 'stages' array is not valid!.`, { steps: this.stages }); } if (!this.model) { Logging.logError('WizardComponent.ngOnInit', `The 'model' attribute is not valid.`, { model: this.model }); } this.validating = false; this.wizardIsFinished = false; this.currentStage.active = true; const filledModel = this.internalModel; if (filledModel.wizardId) { this.telemetryData.wizardId = filledModel.wizardId; } if (filledModel.wizardType) { this.telemetryData.wizardType = filledModel.wizardType; } this.navigationInformation.resetHistory(); // update here first to get updated telemetry info this.updateTelemetryTags(); SmeWebTelemetry.traceAction(null, { content: this.wizardTelemetryEventBlob, actionType: TelemetryActionTypes.Automatic }); this.updateTelemetryTags(WizardState.Active); window.addEventListener('pagehide', () => { if (this.telemetryData.state !== WizardState.Finished) { // manually set at this point b/c view is destructed. this.telemetryData.state = WizardState.Exit; } // actionType A for Auto (according to AWA dictionary) to differentiate from CL SmeWebTelemetry.traceAction(null, { content: this.wizardTelemetryEventBlob, actionType: TelemetryActionTypes.Automatic }); }); } /** * Open or close side pane */ togglePane() { this.isExpanded = !this.isExpanded; } /** * The method called when the back button is clicked. */ onBackClick() { this.showDialogIfBusyOrJustComplete(() => { this.currentStep.complete(); this.currentStage.active = true; this.moveToPreviousStep(); this.focusOnActiveStep(); }); } /** * The method called when the skip button is clicked. */ onSkipClick() { this.showDialogIfBusyOrJustComplete(() => { this.restoreModelForCurrentStep(); if (this.onLastStepOfLastStage) { this.handleFinishNavigation(); } else { this.handleNextNavigation(); } }); } /** * The method called when the Exit / Finish button is clicked. */ onExitClick() { if (this.showExitConfirmationDialog) { this.appContext.frame.showDialogConfirmation({ cancelButtonText: this.strings.MsftSmeShell.Angular.Common.no, confirmButtonText: this.strings.MsftSmeShell.Angular.Common.yes, message: this.strings.MsftSmeShell.Angular.Wizard.Exit.Dialog.message, title: this.strings.MsftSmeShell.Angular.Wizard.Exit.Dialog.title }).subscribe((result) => { if (result.confirmed) { this.exit.emit(); } }); } else { this.exit.emit(); } } /** * The method that handles completion of the wizard, * and renders the finishView if provided. */ handleFinishNavigation() { this.completeCurrentStep(); this.completeCurrentStage(); this.updateTelemetryTags(WizardState.Finished); this.finished.emit(); if (this.finishView) { this.wizardIsFinished = true; this.cleanComponent(); this.componentType = this.finishView.dynamicStepComponent; this.createComponent(); this.changeDetectorRef.detectChanges(); } } /** * The method called when the next button is clicked. */ onNextClick() { // if finishing if (this.onLastStepOfLastStage) { if (!this.currentStep.disabled) { this.runStepValidation(); } else { this.handleFinishNavigation(); } } else { if (!this.currentStep.disabled) { this.runStepValidation(); } else { this.handleNextNavigation(); } this.stepSubmitted.emit(); } } /** * The method that validates step. */ runStepValidation() { this.validating = true; this.currentStepComponent.validate().pipe(take(1)).subscribe((validationResult) => { if (validationResult.isValid) { if (this.onLastStepOfLastStage) { this.handleFinishNavigation(); } else { this.handleNextNavigation(); } } else { this.failCurrentStep(); this.stepInvalidated.emit(validationResult); } this.validating = false; }); } /** * The method that handles indexes and * step and stage completion for the Next button. */ handleNextNavigation() { this.completeCurrentStep(); if (this.onLastStepOfStage) { this.completeCurrentStage(); } this.moveToNextStep(); this.currentStage.active = true; this.focusOnActiveStep(); } showPreviousStepDialog() { this.appContext.frame.showDialogMessage({ buttonText: this.strings.MsftSmeShell.Angular.Common.OK.affirmative, message: this.strings.MsftSmeShell.Angular.Wizard.PreviousStep.Dialog.message, title: this.strings.MsftSmeShell.Angular.Wizard.PreviousStep.Dialog.title }).pipe(take(1)).subscribe(); } showDialogIfBusyOrJustComplete(onConfirmationCallback) { const isBusy = this.currentStepComponent.isBusy; const busyMessage = this.currentStepComponent.busyMessage; if (isBusy) { this.appContext.frame.showDialogConfirmation({ cancelButtonText: this.strings.MsftSmeShell.Angular.Common.no, confirmButtonText: this.strings.MsftSmeShell.Angular.Common.yes, message: busyMessage ? busyMessage : this.strings.MsftSmeShell.Angular.Wizard.Busy.Dialog.message, title: this.strings.MsftSmeShell.Angular.Wizard.Busy.Dialog.title }).pipe(take(1)).subscribe((result) => { if (result.confirmed) { onConfirmationCallback(); } }); } else { onConfirmationCallback(); } } /** * The method called when a stage is clicked. * * @param clickedStepIndex - The index of the clicked stage. */ onStageClick(clickedStageIndex) { const targetStage = this.stages[clickedStageIndex]; if (!this.canNavigateToPreviousStep && clickedStageIndex <= this.stageIndex) { // show dialog when only clicking a previous stage if (targetStage.completed && this.stageIndex !== clickedStageIndex) { this.showPreviousStepDialog(); } } else if (targetStage.completed || targetStage.active) { this.showDialogIfBusyOrJustComplete(() => { this.stageIndex = clickedStageIndex; this.stepIndex = 0; this.emitStepChanged(); this.stages[this.stageIndex].completed = false; for (let i = this.stageIndex + 1; i < this.stages.length; i++) { this.stages[i].active = false; this.stages[i].completed = false; } }); } } /** * The method called when a step is clicked. * * @param clickedStepIndex - The index of the clicked step. */ onStepClick(clickedStepIndex) { const stepIsActive = this.stepIndex === clickedStepIndex; const targetStep = this.currentStage.steps[clickedStepIndex]; if (!this.canNavigateToPreviousStep && clickedStepIndex <= this.stepIndex) { // show dialog when only clicking a previous step if (!stepIsActive) { this.showPreviousStepDialog(); } } else { if (targetStep && !stepIsActive && (targetStep.completed || targetStep.disabled)) { this.showDialogIfBusyOrJustComplete(() => { this.currentStep.complete(); this.stepIndex = clickedStepIndex; this.emitStepChanged(); }); } } } createComponent() { if ((this.currentStep || this.finishStep) && (!this.currentStepComponent)) { super.createComponent(); this.currentStepRxjsLifetime = new RxjsLifetimeManager(); this.currentStepRxjsLifetime.addSubscriptions(this.currentStepComponent.submitted.subscribe(() => this.onNextClick()), this.currentStepComponent.modelChanged.subscribe(newValue => { this.internalModel = newValue; this.modelChange.emit(this.model); })); this.currentStepComponent.onWizardModelChanged(this.model); } } cleanComponent() { super.cleanComponent(); if (this.currentStepRxjsLifetime) { this.currentStepRxjsLifetime.dispose(); this.currentStepRxjsLifetime = null; } } /** * Saves the state of the model at the beginning * of the current step. */ preserveModelForCurrentStep() { this.currentStepModelCopy = MsftSme.deepCopy(this.internalModel || {}); } /** * Resets the model back to the state at the * beginning of the current step. */ restoreModelForCurrentStep() { this.model = this.currentStepModelCopy; this.currentStepModelCopy = null; } emitStepChanged() { const changed = this.navigationInformation.pushHistory({ stage: this.stageIndex, step: this.stepIndex }); // emit only when there is a change. if (changed) { this.updateTelemetryTags(); this.stepChanged.emit({ stageIndex: this.stageIndex, stageName: this.currentStage ? this.currentStage.name : null, stepIndex: this.stepIndex, stepName: this.currentStep ? this.currentStep.name : null }); } } /** * Update telemetry string used to populate telemetry action events. Updates wizard state, step index, and time step index started. * Step index start time should be used together with event timestamp to more easily calculate time spent in stage. * @param state The state to change to, if provided. */ updateTelemetryTags(state) { this.telemetryData.state = state ? state : this.telemetryData.state; this.telemetryData.stepIndexNumber = `${this.stageIndex + 1}.${this.stepIndex + 1}`; this.telemetryData.stepName = `${this.currentStage.name}.${this.currentStep.name}`; this.telemetryData.stepIndexStartTime = (new Date()).toISOString(); // keep same format as event timestamp this.changeDetectorRef.detectChanges(); } } /** @nocollapse */ WizardComponent.ɵfac = function WizardComponent_Factory(t) { return new (t || WizardComponent)(i0.ɵɵdirectiveInject(i0.Injector), i0.ɵɵdirectiveInject(i0.ElementRef), i0.ɵɵdirectiveInject(i1.AppContextService), i0.ɵɵdirectiveInject(i0.ChangeDetectorRef), i0.ɵɵdirectiveInject(i0.ComponentFactoryResolver), i0.ɵɵdirectiveInject(SME_LAYOUT_PROVIDER, 12)); }; /** @nocollapse */ WizardComponent.ɵcmp = /** @pureOrBreakMyCode */ i0.ɵɵdefineComponent({ type: WizardComponent, selectors: [["sme-wizard"]], viewQuery: function WizardComponent_Query(rf, ctx) { if (rf & 1) { i0.ɵɵviewQuery(_c0, 5); } if (rf & 2) { let _t; i0.ɵɵqueryRefresh(_t = i0.ɵɵloadQuery()) && (ctx.wizardFooter = _t.first); } }, inputs: { model: "model", wizardBuilder: "wizardBuilder", showWizardBanner: "showWizardBanner" }, outputs: { modelChange: "modelChange", exit: "exit", stepSubmitted: "stepSubmitted", stepChanged: "stepChanged", stepInvalidated: "stepInvalidated", finished: "finished", layoutChanged: "layoutChanged", windowBreakpointChanged: "windowBreakpointChanged" }, features: [i0.ɵɵProvidersFeature([ { provide: SME_LAYOUT_PROVIDER, useExisting: forwardRef((() => WizardComponent)) } ]), i0.ɵɵInheritDefinitionFeature], decls: 12, vars: 14, consts: [[1, "sme-wizard", "sme-layout-absolute-phone-up", "sme-arrange-stack-v", "sme-position-inset-none"], ["class", "sme-arrange-stack-h sme-position-flex-none sme-padding-vertical-sm sme-padding-horizontal-lg sme-border-bottom-sm sme-border-bottom-color-base-80", 4, "ngIf"], [3, "text", "smeIconClassName", "subtitle", 4, "ngIf"], [4, "ngIf"], ["role", "tablist", "class", "sme-arrange-stack-h sme-position-flex-none sme-padding-horizontal-lg sme-border-bottom-sm sme-border-bottom-color-base-80 sme-focus-zone sme-arrange-overflow-auto-x", 4, "ngIf"], [1, "sme-arrange-stack-h", "sme-arrange-overflow-auto", "sme-position-flex-auto", "sme-wizard-content-container"], ["class", "sme-arrange-stack-v sme-position-flex-none sme-focus-zone sme-border-right-sm sme-border-right-color-base-80 sme-wizard-left-pane", 4, "ngIf"], [1, "sme-focus-zone", "sme-layout-relative", "sme-arrange-overflow-auto", "sme-wizard-content-pane", 3, "ngClass"], ["id", "dynamic-content-container"], ["container", ""], ["class", "sme-layout-relative sme-position-flex-none sme-border-top-sm sme-border-top-color-base-80 sme-padding-vertical-sm sme-padding-horizontal-lg sme-focus-zone", 4, "ngIf"], [1, "sme-arrange-stack-h", "sme-position-flex-none", "sme-padding-vertical-sm", "sme-padding-horizontal-lg", "sme-border-bottom-sm", "sme-border-bottom-color-base-80"], ["id", "sme-shell-wizard-heading-id"], ["type", "preview", "class", "sme-margin-left-xs", 4, "ngIf"], ["type", "preview", 1, "sme-margin-left-xs"], [3, "text", "smeIconClassName", "subtitle"], ["type", "preview", 4, "ngIf"], ["type", "preview"], [3, "items"], ["role", "tablist", 1, "sme-arrange-stack-h", "sme-position-flex-none", "sme-padding-horizontal-lg", "sme-border-bottom-sm", "sme-border-bottom-color-base-80", "sme-focus-zone", "sme-arrange-overflow-auto-x"], ["role", "tab", "tabindex", "0", "aria-live", "polite", "type", "button", "class", "sme-wizard-stage sme-button-trigger sme-wizard-button-item sme-padding-vertical-sm sme-padding-horizontal-none sme-button-auto-width sme-margin-right-md", 3, "title", "smeTelemetryDataBlob", "smeTelemetryId", "sme-active", "sme-selected", "aria-selected", "sme-wizard-stage-focus", "disabled", "smeTelemetryDisableElement", "ngClass", "click", 4, "ngFor", "ngForOf"], ["role", "tab", "tabindex", "0", "aria-live", "polite", "type", "button", 1, "sme-wizard-stage", "sme-button-trigger", "sme-wizard-button-item", "sme-padding-vertical-sm", "sme-padding-horizontal-none", "sme-button-auto-width", "sme-margin-right-md", 3, "title", "smeTelemetryDataBlob", "smeTelemetryId", "disabled", "smeTelemetryDisableElement", "ngClass", "click"], [1, "sme-wizard-stage-index", "sme-position-flex-none", 3, "ngClass"], [1, "sme-position-flex-auto", "sme-font-body", "sme-arrange-ellipsis", 3, "ngClass"], [1, "sme-arrange-stack-v", "sme-position-flex-none", "sme-focus-zone", "sme-border-right-sm", "sme-border-right-color-base-80", "sme-wizard-left-pane"], ["role", "tablist", "class", "sme-padding-vertical-sm sme-position-flex-auto", 3, "ngClass", 4, "ngIf"], [1, "sme-position-flex-none", "sme-focus-zone"], [1, "split-view-toggle", "sme-button-trigger", "sme-button-auto-width", "sme-position-flex-none", "sme-layout-float-right", 3, "title", "click"], [1, "sme-icon", 3, "ngClass"], ["role", "tablist", 1, "sme-padding-vertical-sm", "sme-position-flex-auto", 3, "ngClass"], ["role", "tab", "type", "button", "class", "sme-arrange-stack-h sme-wizard-step sme-button-trigger sme-wizard-button-item sme-padding-horizontal-none\n sme-position-stretch-h sme-padding-vertical-xs sme-button-auto-width sme-padding-left-lg sme-padding-right-xs", "aria-live", "polite", 3, "ngClass", "disabled", "smeTelemetryDisableElement", "smeTelemetryDataBlob", "smeTelemetryId", "title", "click", 4, "ngFor", "ngForOf"], ["role", "tab", "type", "button", "aria-live", "polite", 1, "sme-arrange-stack-h", "sme-wizard-step", "sme-button-trigger", "sme-wizard-button-item", "sme-padding-horizontal-none", "sme-position-stretch-h", "sme-padding-vertical-xs", "sme-button-auto-width", "sme-padding-left-lg", "sme-padding-right-xs", 3, "ngClass", "disabled", "smeTelemetryDisableElement", "smeTelemetryDataBlob", "smeTelemetryId", "title", "click"], [1, "sme-position-flex-auto", "sme-position-flex-grow-start", "sme-margin-right-xs"], ["class", "sme-position-flex-auto sme-position-flex-grow-start sme-arrange-ellipsis", 4, "ngIf"], [1, "sme-position-flex-auto", "sme-position-flex-grow-start", "sme-arrange-ellipsis"], [3, "alert"], [1, "sme-layout-relative", "sme-position-flex-none", "sme-border-top-sm", "sme-border-top-color-base-80", "sme-padding-vertical-sm", "sme-padding-horizontal-lg", "sme-focus-zone"], ["wizardFooter", ""], [1, "sme-layout-float-left"], ["type", "button", "smeTelemetryId", "wizardPrev", 1, "sme-focus-zone", 3, "smeTelemetryDataBlob", "disabled", "click"], ["type", "button", "smeTelemetryId", "wizardNext", 1, "sme-focus-zone", "sme-button-primary", 3, "smeTelemetryDataBlob", "disabled", "smeTelemetryDisableElement", "click"], ["class", "sme-layout-relative sme-layout-float-right sme-padding-left-xl sme-padding-top-md", 4, "ngIf"], ["type", "button", "smeTelemetryId", "wizardSkip", 1, "sme-focus-zone", 3, "smeTelemetryDataBlob", "click"], ["class", "sme-focus-zone sme-layout-float-right", "type", "button", "smeTelemetryId", "wizardExit", 3, "smeTelemetryDataBlob", "click", 4, "ngIf"], [1, "sme-layout-relative", "sme-layout-float-right", "sme-padding-left-xl", "sme-padding-top-md"], ["size", "small"], ["type", "button", "smeTelemetryId", "wizardExit", 1, "sme-focus-zone", "sme-layout-float-right", 3, "smeTelemetryDataBlob", "click"], ["exitButton", ""]], template: function WizardComponent_Template(rf, ctx) { if (rf & 1) { i0.ɵɵelementStart(0, "div", 0); i0.ɵɵtemplate(1, WizardComponent_div_1_Template, 4, 3, "div", 1); i0.ɵɵtemplate(2, WizardComponent_sme_header_2_Template, 2, 4, "sme-header", 2); i0.ɵɵtemplate(3, WizardComponent_div_3_Template, 2, 1, "div", 3); i0.ɵɵtemplate(4, WizardComponent_div_4_Template, 2, 1, "div", 4); i0.ɵɵelementStart(5, "div", 5); i0.ɵɵtemplate(6, WizardComponent_div_6_Template, 5, 8, "div", 6); i0.ɵɵelementStart(7, "div", 7); i0.ɵɵtemplate(8, WizardComponent_div_8_Template, 2, 1, "div", 3); i0.ɵɵelement(9, "div", 8, 9); i0.ɵɵelementEnd()(); i0.ɵɵtemplate(11, WizardComponent_div_11_Template, 11, 17, "div", 10); i0.ɵɵelementEnd(); } if (rf & 2) { i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx.heading && !ctx.wizardIsFinished && !ctx.headerData); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", !ctx.wizardIsFinished && ctx.headerData && !ctx.heading); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx.showWizardBanner); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", !ctx.wizardIsFinished); i0.ɵɵadvance(1); i0.ɵɵclassProp("sme-wizard-no-left-pane", ctx.hideStepsListSidePane); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", !ctx.wizardIsFinished && !ctx.hideStepsListSidePane); i0.ɵɵadvance(1); i0.ɵɵclassProp("sme-wizard-no-left-pane", ctx.hideStepsListSidePane); i0.ɵɵproperty("ngClass", i0.ɵɵpureFunction1(12, _c7, ctx.currentStepComponent.padContents)); i0.ɵɵadvance(1); i0.ɵɵproperty("ngIf", ctx.currentStepComponent.disabled); i0.ɵɵadvance(3); i0.ɵɵproperty("ngIf", !ctx.wizardIsFinished); } }, dependencies: [i2.NgClass, i2.NgForOf, i2.NgIf, i3.HeaderComponent, i4.LoadingWheelComponent, i5.TelemetryDirective, i6.TooltipDirective, i7.PageAlertBarComponent, i8.PreviewBadgeComponent, i9.BannerComponent], encapsulation: 2 }); __decorate([ Debounce(), __met