@openui5/sap.m
Version:
OpenUI5 UI Library sap.m
1,008 lines (887 loc) • 33 kB
JavaScript
/*!
* UI development toolkit for HTML5 (OpenUI5)
* (c) Copyright 2009-2022 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
sap.ui.define([
"./library",
"sap/ui/core/Control",
"sap/ui/core/delegate/ScrollEnablement",
"./WizardProgressNavigator",
"sap/ui/Device",
"./WizardRenderer",
"sap/ui/dom/containsOrEquals",
"sap/base/Log",
"sap/ui/thirdparty/jquery",
// jQuery Plugin "firstFocusableDomRef"
"sap/ui/dom/jquery/Focusable"
], function(
library,
Control,
ScrollEnablement,
WizardProgressNavigator,
Device,
WizardRenderer,
containsOrEquals,
Log,
jQuery
) {
"use strict";
/**
* Constructor for a new Wizard.
*
* @param {string} [sId] ID for the new control, generated automatically if no ID is given
* @param {object} [mSettings] Initial settings for the new control
*
* @class
* Enables users to accomplish a single goal which consists of multiple dependable sub-tasks.
* <h3>Overview</h3>
* The sap.m.Wizard helps users complete a complex and unfamiliar task by dividing it into sections and guiding the user through it.
* The wizard has two main areas - a navigation area at the top showing the step sequence and a content area below it.
* <h3>Structure</h3>
* <h4>Navigation Area</h4>
* The top most area of the wizard is occupied by the navigation area. It shows the sequence of {@link sap.m.WizardStep wizard steps}.
* <ul>
* <li>The minimum number of steps is 3 and the maximum is 8 and are stored in the <code>steps</code> aggregation.</li>
* <li>Steps can be branching depending on choices the user made in their input - this is set by the <code>enableBranching</code> property. </li>
* <li>Steps can have different visual representations - numbers or icons. You can add labels for better readability </li>
* </ul>
* <h4>Content</h4>
* The content occupies the main part of the page. It can hold any type of input controls. The content is kept in {@link sap.m.WizardStep wizard steps}.
* <h4>Next Step Button</h4>
* The next step button is displayed below the content. It can be hidden by setting <code>showNextButton</code> to <code>false</code> and displayed, for example,
* only after the user has filled all mandatory fields.
* <h3>Usage</h3>
* <h4>When to use:</h4>
* When the user has to accomplish a long or unfamiliar task.
* <h4>When not to use:</h4>
* When the user has to accomplish a routine task that is clear and familiar.
* When the task has only two steps or less.
* <h3>Responsive Behavior</h3>
* On mobile devices the steps in the StepNavigator are grouped together and overlap. Tapping on them will show a popover to select the step to navigate to.
* @extends sap.ui.core.Control
* @author SAP SE
* @version 1.60.39
*
* @constructor
* @public
* @since 1.30
* @alias sap.m.Wizard
* @see {@link fiori:https://experience.sap.com/fiori-design-web/wizard/ Wizard}
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var Wizard = Control.extend("sap.m.Wizard", /** @lends sap.m.Wizard.prototype */ {
metadata: {
library: "sap.m",
designtime: "sap/m/designtime/Wizard.designtime",
properties: {
/**
* Determines the width of the Wizard.
*/
width : {type : "sap.ui.core.CSSSize", group : "Appearance", defaultValue : "auto"},
/**
* Determines the height of the Wizard.
*/
height : {type : "sap.ui.core.CSSSize", group : "Appearance", defaultValue : "100%"},
/**
* Controls the visibility of the next button. The developers can choose to control the flow of the
* steps either through the API (with <code>nextStep</code> and <code>previousStep</code> methods) or let the user click
* the next button, and control it with <code>validateStep</code> or <code>invalidateStep</code> methods.
*/
showNextButton : {type : "boolean", group : "Behavior", defaultValue : true},
/**
* Changes the text of the finish button for the last step.
* This property can be used only if <code>showNextButton</code> is set to true.
* By default the text of the button is "Review".
*/
finishButtonText: {type: "string", group: "Appearance", defaultValue: "Review"},
/**
* Enables the branching functionality of the Wizard.
* Branching gives the developer the ability to define multiple routes a user
* is able to take based on the input in the current step.
* It is up to the developer to programatically check for what is the input in the
* current step and set a concrete next step amongs the available subsequent steps.
* Note: If this property is set to false, <code>next</code> and <code>subSequentSteps</code>
* associations of the WizardStep control are ignored.
* @since 1.32
*/
enableBranching : {type: "boolean", group: "Behavior", defaultValue : false}
},
defaultAggregation: "steps",
aggregations: {
/**
* The wizard steps to be included in the content of the control.
*/
steps: {type: "sap.m.WizardStep", multiple: true, singularName: "step"},
/**
* The progress navigator for the wizard.
* @since 1.32
*/
_progressNavigator: {type: "sap.ui.core.Control", multiple: false, visibility: "hidden"},
/**
* The next button for the wizard.
*/
_nextButton: {type: "sap.m.Button", multiple: false, visibility: "hidden"}
},
associations: {
/**
* This association controls the current activated step of the wizard (meaning the last step)
* For example if we have A->B->C->D steps, we are on step A and we setCurrentStep(C) A,B and C are going to be activated. D will still remain unvisited.
* The parameter needs to be a Wizard step that is part of the current Wizard
* @since 1.50
*/
currentStep: {type: "sap.m.WizardStep", multiple: false}
},
events: {
/**
* The StepActivated event is fired every time a new step is activated.
*/
stepActivate: {
parameters: {
/**
* The index of the activated step as a parameter. One-based.
*/
index: {type: "int"}
}
},
/**
* The complete event is fired when the user clicks the finish button of the Wizard.
* The finish button is only available on the last step of the Wizard.
*/
complete: {
parameters: {}
}
}
}
});
Wizard.CONSTANTS = {
MINIMUM_STEPS: 3,
MAXIMUM_STEPS: 8,
ANIMATION_TIME: 300,
SCROLL_OFFSET: 16
};
/************************************** LIFE CYCLE METHODS ***************************************/
Wizard.prototype.init = function () {
this._stepCount = 0;
this._stepPath = [];
this._scrollLocked = false;
this._scroller = this._initScrollEnablement();
this._resourceBundle = sap.ui.getCore().getLibraryResourceBundle("sap.m");
this._initProgressNavigator();
};
Wizard.prototype.onBeforeRendering = function () {
if (!this._isMinStepCountReached() || this._isMaxStepCountExceeded()) {
Log.error("The Wizard is supposed to handle from 3 to 8 steps.");
}
this._saveInitialValidatedState();
var step = this._getStartingStep();
if (step && this._stepPath.indexOf(step) < 0) {
this._activateStep(step);
this._updateProgressNavigator();
}
};
Wizard.prototype.onAfterRendering = function () {
if (!this.getCurrentStep()) {
this.setAssociation("currentStep", this.getSteps()[0], true);
}
var step = sap.ui.getCore().byId(this.getCurrentStep());
this._activateAllPreceedingSteps(step);
this._attachScrollHandler();
};
/**
* Destroy all content on wizard destroy
*/
Wizard.prototype.exit = function () {
var contentDomRef = this.getDomRef("step-container");
if (contentDomRef) {
contentDomRef.onscroll = null;
}
this._scroller.destroy();
this._scroller = null;
this._stepPath = null;
this._stepCount = null;
this._scrollLocked = null;
this._resourceBundle = null;
};
/**************************************** PUBLIC METHODS ***************************************/
/**
* Validates the given step.
* @param {sap.m.WizardStep} step The step to be validated.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.validateStep = function (step) {
if (!this._containsStep(step)) {
Log.error("The wizard does not contain this step");
return this;
}
step.setProperty("validated", true, true); //Surpress rerendering
this._updateNextButtonState();
return this;
};
/**
* Invalidates the given step.
* @param {sap.m.WizardStep} step The step to be invalidated.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.invalidateStep = function (step) {
if (!this._containsStep(step)) {
Log.error("The wizard does not contain this step");
return this;
}
step.setProperty("validated", false, true); //Surpress rerendering
this._updateNextButtonState();
return this;
};
/**
* Validates the current step, and moves one step further.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.nextStep = function () {
var currentStepIndex = this._getProgressNavigator().getProgress() - 1;
var currentStep = this._stepPath[currentStepIndex];
this.validateStep(currentStep);
this._handleNextButtonPress();
return this;
};
/**
* Discards the current step and goes one step back.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.previousStep = function () {
var previousStepIndex = this._getProgressNavigator().getProgress() - 2;
if (previousStepIndex >= 0) {
this.discardProgress(this._stepPath[previousStepIndex]);
}
return this;
};
/**
* Returns the number of the last activated step in the Wizard.
* @returns {number} The last activated step.
* @public
*/
Wizard.prototype.getProgress = function () {
return this._getProgressNavigator().getProgress();
};
/**
* Returns the last activated step in the Wizard.
* @returns {sap.m.WizardStep} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.getProgressStep = function () {
return this._stepPath[this.getProgress() - 1];
};
/**
* Goes to the given step. The step must already be activated and visible. You can't use this method on steps
* that haven't been reached yet.
* @param {sap.m.WizardStep} step The step to go to.
* @param {boolean} focusFirstStepElement Defines whether the focus should be changed to the first element.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.goToStep = function (step, focusFirstStepElement) {
if (!this.getVisible() || this._stepPath.indexOf(step) < 0) {
return this;
}
var that = this,
scrollProps = {
scrollTop: this._getStepScrollOffset(step)
},
animProps = {
queue: false,
duration: Wizard.CONSTANTS.ANIMATION_TIME,
start: function () {
that._scrollLocked = true;
},
complete: function () {
that._scrollLocked = false;
var progressNavigator = that._getProgressNavigator();
if (!progressNavigator) {
return;
}
progressNavigator._updateCurrentStep(that._stepPath.indexOf(step) + 1, undefined, true);
if (focusFirstStepElement || focusFirstStepElement === undefined) {
that._focusFirstStepElement(step);
}
}
};
jQuery(this.getDomRef("step-container")).animate(scrollProps, animProps);
return this;
};
/**
* Discards all progress done from the given step(incl.) to the end of the wizard.
* The verified state of the steps is returned to the initial provided.
* @param {sap.m.WizardStep} step The step after which the progress is discarded.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.discardProgress = function (step, preserveNextStep) {
var progressAchieved = this.getProgress(),
steps = this._stepPath,
index = this._stepPath.indexOf(step),
lastStep = this._stepPath[index],
progressNavigatorIndex = index + 1;
if (progressNavigatorIndex > progressAchieved || progressNavigatorIndex <= 0) {
Log.warning("The given step is either not yet reached, or is not present in the wizard control.");
return this;
}
this._getProgressNavigator().discardProgress(progressNavigatorIndex, true);
this._updateNextButtonState();
this._restoreInitialValidatedState(progressNavigatorIndex);
lastStep._markAsLast();
for (var j = 0; j < progressNavigatorIndex - 1; j++) {
var oButton = steps[j].getAggregation("_nextButton");
oButton.setEnabled(false);
oButton.removeStyleClass("sapMWizardNextButtonVisible");
}
for (var i = progressNavigatorIndex; i < steps.length; i++) {
steps[i]._deactivate();
if (steps[i].getSubsequentSteps().length > 1) {
steps[i].setNextStep(null);
}
}
if (step.getSubsequentSteps().length > 1 && !preserveNextStep) {
step.setNextStep(null);
}
steps.splice(progressNavigatorIndex);
this._updateProgressNavigator();
this.setAssociation("currentStep", step);
this.getShowNextButton() && lastStep._oNextButton.setVisible(true);
return this;
};
/**************************************** PROXY METHODS ***************************************/
/**
* Sets association currentStep to the given step.
*
* @param {sap.m.WizardStep | String} stepId The step of the wizard that will be currently activated (meaning the last step)
* @returns {sap.m.Wizard} Reference to the control instance for chaining.
* @public
*/
Wizard.prototype.setCurrentStep = function (stepId) {
this.setAssociation("currentStep", stepId, true);
var step = (typeof stepId === "string") ? sap.ui.getCore().byId(stepId) : stepId;
if (step && this._isStepReachable(step)) {
this._activateAllPreceedingSteps(step);
}
return this;
};
/**
* Sets the visibility of the next button.
* @param {boolean} value True to show the button or false to hide it.
* @returns {sap.m.Wizard} Reference to the control instance for chaining.
* @public
*/
Wizard.prototype.setShowNextButton = function (value) {
this.setProperty("showNextButton", value, true);
this.getSteps().forEach(function(oStep){
oStep.getAggregation("_nextButton").setVisible(value);
});
return this;
};
/**
* Sets the text for the finish button. By default it is "Review".
* @param {string} value The text of the finish button.
* @returns {sap.m.Wizard} Reference to the control instance for chaining.
* @public
*/
Wizard.prototype.setFinishButtonText = function (value) {
this.setProperty("finishButtonText", value, true);
this._updateNextButtonState();
return this;
};
/**
* Returns the finish button text which will be rendered.
* @returns {string} The text which will be rendered in the finish button.
* @public
*/
Wizard.prototype.getFinishButtonText = function () {
if (this.getProperty("finishButtonText") === "Review") {
return this._resourceBundle.getText("WIZARD_FINISH");
} else {
return this.getProperty("finishButtonText");
}
};
/**
* Adds a new step to the Wizard.
* @param {sap.m.WizardStep} wizardStep New WizardStep to add to the Wizard
* @returns {sap.m.Wizard} Pointer to the control instance for chaining
* @public
*/
Wizard.prototype.addStep = function (wizardStep) {
if (this._isMaxStepCountExceeded()) {
Log.error("The Wizard is supposed to handle up to 8 steps.");
return this;
}
wizardStep._attachNextButtonHandler(this._handleNextButtonPress.bind(this));
this._incrementStepCount();
return this.addAggregation("steps", wizardStep);
};
/**
* Dynamic step insertion is not yet supported.
* @param {sap.m.WizardStep} wizardStep The step to be inserted
* @param {index} index The index at which to insert
* @experimental
* @private
*/
Wizard.prototype.insertStep = function (wizardStep, index) {
throw new Error("Dynamic step insertion is not yet supported.");
};
/**
* Dynamic step removal is not yet supported.
* @param {sap.m.WizardStep} wizardStep The step to be removed
* @experimental
* @private
*/
Wizard.prototype.removeStep = function (wizardStep) {
throw new Error("Dynamic step removal is not yet supported.");
};
/**
* Removes all steps from the Wizard.
* @returns {sap.m.WizardStep[]} Pointer to the Steps that were removed.
* @public
*/
Wizard.prototype.removeAllSteps = function () {
this._resetStepCount();
this.getSteps().forEach(function(oStep){
oStep._detachNextButtonHandler();
});
return this.removeAllAggregation("steps");
};
/**
* Destroys all aggregated steps in the Wizard.
* @returns {sap.m.Wizard} Pointer to the control instance for chaining.
* @public
*/
Wizard.prototype.destroySteps = function () {
this._resetStepCount();
this._getProgressNavigator().setStepCount(this._getStepCount());
return this.destroyAggregation("steps");
};
/**************************************** PRIVATE METHODS ***************************************/
Wizard.prototype._activateAllPreceedingSteps = function (step) {
if (this._stepPath.indexOf(step) >= 0) {
this.discardProgress(step, true);
return;
}
while (this.getProgressStep() !== step) {
this.nextStep();
}
};
/**
* Checks if in branching mode and the nextStep association of the currentStep is not set.
*
* @param step
* @param progress
* @returns {boolean} Whether the check passed
* @private
*/
Wizard.prototype._isNextStepDetermined = function (step, progress) {
if (!this.getEnableBranching()) {
return true;
}
step = step || sap.ui.getCore().byId(this.getCurrentStep());
return this._getNextStep(step, progress) !== null;
};
/**
* Searches for the given step, starting from the firstStep, checking the nextStep in the path.
* @param {sap.m.WizardStep} step The step to be reached
* @returns {boolean} Whether the step is reachable
*/
Wizard.prototype._isStepReachable = function (step) {
if (this.getEnableBranching()) {
var stepIterator = this._getStartingStep();
while (stepIterator !== step) {
stepIterator = stepIterator._getNextStepReference();
if (stepIterator == null) {
return false;
}
}
return true;
} else {
return this.getSteps().indexOf(step) >= 0;
}
};
Wizard.prototype._initScrollEnablement = function () {
return new ScrollEnablement(this, null, {
scrollContainerId: this.getId() + "-step-container",
horizontal: false,
vertical: true
});
};
/**
* Creates the internal WizardProgressNavigator aggregation of the Wizard
* @returns {void}
* @private
*/
Wizard.prototype._initProgressNavigator = function () {
var that = this,
progressNavigator = new WizardProgressNavigator(this.getId() + "-progressNavigator", {
stepChanged: this._handleStepChanged.bind(this)
});
progressNavigator._setOnEnter(function (event, stepIndex) {
var step = that._stepPath[stepIndex];
setTimeout(function () {
this._focusFirstStepElement(step);
}.bind(that), Wizard.CONSTANTS.ANIMATION_TIME);
});
this.setAggregation("_progressNavigator", progressNavigator);
};
/**
* Handler for the next button press
* @private
*/
Wizard.prototype._handleNextButtonPress = function () {
var progressNavigator = this._getProgressNavigator(),
lastStepInPath = this._stepPath[this._stepPath.length - 1],
progressAchieved = progressNavigator.getProgress(),
stepCount = progressNavigator.getStepCount(),
isStepFinal = this.getEnableBranching() ? lastStepInPath._isLeaf() : progressAchieved === stepCount;
if (isStepFinal) {
this.fireComplete();
} else {
var progressStep = this.getProgressStep();
// hide the next button only if it not the review button
// otherwise when completing the wizard and going back to edit it
// the review button won't be shown
this._getNextButton().setVisible(false);
progressStep._complete();
if (!this._isNextStepDetermined(progressStep, progressAchieved)) {
throw new Error("The wizard is in branching mode, and the nextStep association is not set.");
}
if (progressAchieved === stepCount) {
progressNavigator.setStepCount(stepCount + 1);
progressNavigator.rerender();
}
progressNavigator.incrementProgress();
this._handleStepActivated(progressNavigator.getProgress());
this._handleStepChanged(progressNavigator.getProgress());
this.setAssociation("currentStep", this._stepPath[this._stepPath.length - 1], true);
}
this._updateNextButtonState();
};
/**
* Gets the distance between the step heading, and the top of the container
* @param {sap.m.WizardStep} step The step whose distance is going to be calculcated
* @returns {number} The measured distance
* @private
*/
Wizard.prototype._getStepScrollOffset = function (step) {
var stepTop = step.$().position().top,
scrollerTop = this._scroller.getScrollTop(),
progressStep = this._stepPath[this.getProgress() - 1],
additionalOffset = 0;
/**
* Additional Offset is added in case of new step activation.
* Because the rendering from step.addContent(button) happens with delay,
* we can't properly detect the offset of the step, that's why
* additionalOffset is added like this.
*/
if (!Device.system.phone &&
!containsOrEquals(progressStep.getDomRef(), this._getNextButton().getDomRef())) {
additionalOffset = this._getNextButton().$().outerHeight();
}
return (scrollerTop + stepTop) - (Wizard.CONSTANTS.SCROLL_OFFSET + additionalOffset);
};
/**
* Focuses the first focusable element of a given step
* @param {sap.m.WizardStep} step The step to be focused
* @private
*/
Wizard.prototype._focusFirstStepElement = function (step) {
var $step = step.$();
if ($step.firstFocusableDomRef()) {
$step.firstFocusableDomRef().focus();
}
};
/**
* Handler for the stepChanged event. The event comes from the WizardProgressNavigator
* @param {jQuery.Event} event The event object
* @private
*/
Wizard.prototype._handleStepChanged = function (event) {
var previousStepIndex = ((typeof event === "number") ? event : event.getParameter("current")) - 2;
var previousStep = this._stepPath[previousStepIndex];
var subsequentStep = this._getNextStep(previousStep, previousStepIndex);
var focusFirstElement = Device.system.desktop ? true : false;
this.goToStep(subsequentStep, focusFirstElement);
};
/**
* Handler for the stepActivated event. The event comes from the WizardProgressNavigator
* @param {number} index The step index
* @private
*/
Wizard.prototype._handleStepActivated = function (index) {
var previousStepIndex = index - 2,
previousStep = this._stepPath[previousStepIndex];
var nextStep = this._getNextStep(previousStep, previousStepIndex);
this._activateStep(nextStep);
this._updateProgressNavigator();
this.fireStepActivate({index: index});
};
/**
* Checks whether the maximum step count is reached
* @returns {boolean} True if the max step count is reached
* @private
*/
Wizard.prototype._isMaxStepCountExceeded = function () {
if (this.getEnableBranching()) {
return false;
}
var stepCount = this._getStepCount();
return stepCount >= Wizard.CONSTANTS.MAXIMUM_STEPS;
};
/**
* Checks whether the minimum step count is reached
* @returns {boolean} True if the min step count is reached
* @private
*/
Wizard.prototype._isMinStepCountReached = function () {
var stepCount = this._getStepCount();
return stepCount >= Wizard.CONSTANTS.MINIMUM_STEPS;
};
/**
* Returns the number of steps in the wizard
* @returns {number} the number of steps
* @private
*/
Wizard.prototype._getStepCount = function () {
return this._stepCount;
};
/**
* Increases the internal step count, and the step count in the progress navigator
* @private
*/
Wizard.prototype._incrementStepCount = function () {
this._stepCount += 1;
this._getProgressNavigator().setStepCount(this._getStepCount());
};
/**
* Decreases the internal step count, and the step count in the progress navigator
* @private
*/
Wizard.prototype._decrementStepCount = function () {
this._stepCount -= 1;
this._getProgressNavigator().setStepCount(this._getStepCount());
};
/**
* Sets the internal step count to 0, and the step count of the progress navigator to 0
* @private
*/
Wizard.prototype._resetStepCount = function () {
this._stepCount = 0;
this._getProgressNavigator().setStepCount(this._getStepCount());
};
/**
* Returns the progress navigator element of the wizard
* @returns {sap.m.WizardProgressNavigator} The progress navigator
* @private
*/
Wizard.prototype._getProgressNavigator = function () {
return this.getAggregation("_progressNavigator");
};
/**
* Saves the initial valdiated state of the steps
* @private
*/
Wizard.prototype._saveInitialValidatedState = function () {
if (this._initialValidatedState) {
return;
}
this._initialValidatedState = this.getSteps().map(function (step) {
return step.getValidated();
});
};
/**
* Restores the initial validated state of the steps, starting from the given index
* @param {number} index The index to start the restoring from
* @private
*/
Wizard.prototype._restoreInitialValidatedState = function (index) {
var steps = this._stepPath,
aggregationSteps = this.getSteps();
for (var i = index; i < steps.length; i++) {
var step = steps[i];
var stepIndexInAggregation = aggregationSteps.indexOf(step);
var initialState = this._initialValidatedState[stepIndexInAggregation];
step.setValidated(initialState);
}
};
/**
* Returns a reference to the subsequent step of the provided step
* @param {sap.m.WizardStep} step The parent step
* @param {number} progress The current progress of the Wizard, used in non branching mode.
* @returns {sap.m.WizardStep} The subsequent step
* @private
*/
Wizard.prototype._getNextStep = function (step, progress) {
if (!this.getEnableBranching()) {
return this.getSteps()[progress + 1];
}
if (progress < 0) {
return this._getStartingStep();
}
var nextStep = step._getNextStepReference();
if (nextStep === null) {
throw new Error("The wizard is in branching mode, and no next step is defined for " +
"the current step, please set one.");
}
if (!this._containsStep(nextStep)) {
throw new Error("The next step that you have defined is not part of the wizard steps aggregation." +
"Please add it to the wizard control.");
}
var subsequentSteps = step.getSubsequentSteps();
if (subsequentSteps.length > 0 && !step._containsSubsequentStep(nextStep.getId())) {
throw new Error("The next step that you have defined is not contained inside the subsequentSteps" +
" association of the current step.");
}
return nextStep;
};
/**
* Updates the next button state, changing the enablement, and changing the text,
* depending on the validation of the progress step
* @private
*/
Wizard.prototype._updateNextButtonState = function () {
if (!this._getNextButton()) {
return;
}
var isStepFinal,
stepCount = this._getStepCount(),
nextButton = this._getNextButton(),
progressAchieved = this.getProgress(),
isStepValidated = this._stepPath[progressAchieved - 1].getValidated();
if (this.getEnableBranching()) {
isStepFinal = this._stepPath[progressAchieved - 1]._isLeaf();
} else {
isStepFinal = progressAchieved === stepCount;
}
nextButton.setEnabled(isStepValidated);
if (isStepFinal) {
nextButton.setText(this.getFinishButtonText());
} else {
nextButton.setText(this._resourceBundle.getText("WIZARD_STEP" ) + " " + (progressAchieved + 1));
}
};
/**
* Returns a reference to the next button
* @returns {sap.m.Button} The button reference
* @private
*/
Wizard.prototype._getNextButton = function () {
var step = this._stepPath[this._stepPath.length - 1];
if (step) {
return step.getAggregation("_nextButton");
} else {
return null;
}
};
/**
* This method updates the visual style of the navigator.
* If the wizard is in branching mode, the progress navigator has different visualization, compared
* to normal mode.
* @private
*/
Wizard.prototype._updateProgressNavigator = function () {
var progressNavigator = this._getProgressNavigator(),
currentStep = this._getStartingStep(),
allSteps = this.getSteps(),
stepTitles = [currentStep.getTitle()],
stepIcons = [currentStep.getIcon()],
stepOptionalIndication = [currentStep.getOptional()],
stepCount = 1;
if (this.getEnableBranching()) {
// Find branched, or leaf step
while (!currentStep._isLeaf() && currentStep._getNextStepReference() !== null) {
stepCount++;
currentStep = currentStep._getNextStepReference();
stepTitles.push(currentStep.getTitle());
stepOptionalIndication.push(currentStep.getOptional());
stepIcons.push(currentStep.getIcon());
}
progressNavigator.setVaryingStepCount(currentStep._isBranched());
progressNavigator.setStepCount(stepCount);
} else {
stepTitles = allSteps.map(function (step) { return step.getTitle(); });
stepOptionalIndication = allSteps.map(function (step) { return step.getOptional(); });
stepIcons = allSteps.map(function (step) { return step.getIcon(); });
}
progressNavigator.setStepTitles(stepTitles);
progressNavigator._stepOptionalIndication = stepOptionalIndication;
progressNavigator.setStepIcons(stepIcons);
};
/**
* Returns the entry point for the wizard.
* @returns {sap.m.WizardStep} Reference to the starting step
* @private
*/
Wizard.prototype._getStartingStep = function () {
return this.getSteps()[0];
};
/**
* Attaches the wizard scroll handler directly to the steps container element
* @private
*/
Wizard.prototype._attachScrollHandler = function () {
var contentDOM = this.getDomRef("step-container");
contentDOM.onscroll = this._scrollHandler.bind(this);
};
/**
* Handles the scroll event of the steps container element
* @param {jQuery.Event} event The event object
* @private
*/
Wizard.prototype._scrollHandler = function (event) {
if (this._scrollLocked) {
return;
}
var scrollTop = event.target.scrollTop,
progressNavigator = this._getProgressNavigator(),
currentStepDOM = this._stepPath[progressNavigator.getCurrentStep() - 1].getDomRef();
if (!currentStepDOM) {
return;
}
var stepHeight = currentStepDOM.clientHeight,
stepOffset = currentStepDOM.offsetTop,
stepChangeThreshold = 100;
if (scrollTop + stepChangeThreshold >= stepOffset + stepHeight && progressNavigator._isActiveStep(progressNavigator._currentStep + 1)) {
progressNavigator.nextStep();
}
var aSteps = this.getSteps();
// change the navigator current step
for (var index = 0; index < aSteps.length; index++) {
if (scrollTop + stepChangeThreshold <= stepOffset) {
progressNavigator.previousStep();
// update the currentStep reference
currentStepDOM = this._stepPath[progressNavigator.getCurrentStep() - 1].getDomRef();
if (!currentStepDOM) {
break;
}
stepOffset = currentStepDOM.offsetTop;
}
}
};
Wizard.prototype._containsStep = function (step) {
return this.getSteps().some(function (ourStep) { return ourStep === step; });
};
Wizard.prototype._checkCircularReference = function (step) {
if (this._stepPath.indexOf(step) >= 0) {
throw new Error("The step that you are trying to activate has already been visited. You are creating " +
"a loop inside the wizard.");
}
};
/**
* Activates the current step, adding it to the stepPath, and checks if the current step hasn't already
* been visited. If visited - an Error is thrown.
* @param {sap.m.WizardStep} step The step to be activated
* @private
*/
Wizard.prototype._activateStep = function (step) {
this._checkCircularReference(step);
this._stepPath.push(step);
step._activate();
};
return Wizard;
});