material-steppers
Version:
Angular Steppers directive for Angular Material
250 lines (249 loc) • 14.4 kB
JavaScript
var StepperCtrl = (function () {
function StepperCtrl($mdComponentRegistry, $attrs, $log) {
this.$mdComponentRegistry = $mdComponentRegistry;
this.$attrs = $attrs;
this.$log = $log;
this.labelStep = 'Step';
this.labelOf = 'of';
/* End of bindings */
this.steps = [];
this.currentStep = 0;
}
StepperCtrl.prototype.$onInit = function () {
if (this.$attrs.mdMobileStepText === '') {
this.mobileStepText = true;
}
if (this.$attrs.mdLinear === '') {
this.linear = true;
}
if (this.$attrs.mdAlternative === '') {
this.alternative = true;
}
};
StepperCtrl.prototype.$postLink = function () {
if (!this.$attrs.id) {
this.$log.warn('You must set an id attribute to your stepper');
}
this.registeredStepper = this.$mdComponentRegistry.register(this, this.$attrs.id);
};
StepperCtrl.prototype.$onDestroy = function () {
this.registeredStepper && this.registeredStepper();
};
/**
* Register component step to this stepper.
*
* @param {StepCtrl} step The step to add.
* @returns number - The step number.
*/
StepperCtrl.prototype.$addStep = function (step) {
return this.steps.push(step) - 1;
};
/**
* Complete the current step and move one to the next.
* Using this method on editable steps (in linear stepper)
* it will search by the next step without "completed" state to move.
* When invoked it dispatch the event onstepcomplete to the step element.
*
* @returns boolean - True if move and false if not move (e.g. On the last step)
*/
StepperCtrl.prototype.next = function () {
if (this.currentStep < this.steps.length) {
this.clearError();
this.currentStep++;
this.clearFeedback();
return true;
}
return false;
};
/**
* Move to the previous step without change the state of current step.
* Using this method in linear stepper it will check if previous step is editable to move.
*
* @returns boolean - True if move and false if not move (e.g. On the first step)
*/
StepperCtrl.prototype.back = function () {
if (this.currentStep > 0) {
this.clearError();
this.currentStep--;
this.clearFeedback();
return true;
}
return false;
};
/**
* Move to the next step without change the state of current step.
* This method works only in optional steps.
*
* @returns boolean - True if move and false if not move (e.g. On non-optional step)
*/
StepperCtrl.prototype.skip = function () {
var step = this.steps[this.currentStep];
if (step.optional) {
this.currentStep++;
this.clearFeedback();
return true;
}
return false;
};
/**
* Defines the current step state to "error" and shows the message parameter on
* title message element.When invoked it dispatch the event onsteperror to the step element.
*
* @param {string} message The error message
*/
StepperCtrl.prototype.error = function (message) {
var step = this.steps[this.currentStep];
step.hasError = true;
step.message = message;
this.clearFeedback();
};
/**
* Defines the current step state to "normal" and removes the message parameter on
* title message element.
*/
StepperCtrl.prototype.clearError = function () {
var step = this.steps[this.currentStep];
step.hasError = false;
};
/**
* Move "active" to specified step id parameter.
* The id used as reference is the integer number shown on the label of each step (e.g. 2).
*
* @param {number} stepNumber (description)
* @returns boolean - True if move and false if not move (e.g. On id not found)
*/
StepperCtrl.prototype.goto = function (stepNumber) {
if (stepNumber < this.steps.length) {
this.currentStep = stepNumber;
this.clearFeedback();
return true;
}
return false;
};
/**
* Shows a feedback message and a loading indicador.
*
* @param {string} [message] The feedbackMessage
*/
StepperCtrl.prototype.showFeedback = function (message) {
this.hasFeedback = true;
this.feedbackMessage = message;
};
/**
* Removes the feedback.
*/
StepperCtrl.prototype.clearFeedback = function () {
this.hasFeedback = false;
};
StepperCtrl.prototype.isCompleted = function (stepNumber) {
return this.linear && stepNumber < this.currentStep;
};
;
StepperCtrl.prototype.isActiveStep = function (step) {
return this.steps.indexOf(step) === this.currentStep;
};
StepperCtrl.$inject = [
'$mdComponentRegistry',
'$attrs',
'$log'
];
return StepperCtrl;
}());
var StepperServiceFactory = ['$mdComponentRegistry',
function ($mdComponentRegistry) {
return function (handle) {
var instance = $mdComponentRegistry.get(handle);
if (!instance) {
$mdComponentRegistry.notFoundError(handle);
}
return instance;
};
}];
var StepCtrl = (function () {
/**
*
*/
function StepCtrl($element, $compile, $scope) {
this.$element = $element;
this.$compile = $compile;
this.$scope = $scope;
}
StepCtrl.prototype.$postLink = function () {
this.stepNumber = this.$stepper.$addStep(this);
};
StepCtrl.prototype.isActive = function () {
var state = this.$stepper.isActiveStep(this);
return state;
};
StepCtrl.$inject = [
'$element',
'$compile',
'$scope'
];
return StepCtrl;
}());
angular.module('mdSteppers', ['ngMaterial'])
.factory('$mdStepper', StepperServiceFactory)
.directive('mdStepper', function () {
return {
transclude: true,
scope: {
linear: '<?mdLinear',
alternative: '<?mdAlternative',
vertical: '<?mdVertical',
mobileStepText: '<?mdMobileStepText',
labelStep: '@?mdLabelStep',
labelOf: '@?mdLabelOf'
},
bindToController: true,
controller: StepperCtrl,
controllerAs: 'stepper',
templateUrl: 'mdSteppers/mdStepper.tpl.html'
};
})
.directive('mdStep', ['$compile', function ($compile) {
return {
require: '^^mdStepper',
transclude: true,
scope: {
label: '',
optional: '@?mdOptional'
},
bindToController: true,
controller: StepCtrl,
controllerAs: '$ctrl',
link: function (scope, iElement, iAttrs, stepperCtrl) {
function addOverlay() {
var hasOverlay = !!iElement.find('.md-step-body-overlay')[0];
if (!hasOverlay) {
var overlay = angular.element("<div class=\"md-step-body-overlay\"></div>\n <div class=\"md-step-body-loading\">\n <md-progress-circular md-mode=\"indeterminate\"></md-progress-circular>\n </div>");
$compile(overlay)(scope);
iElement.find('.md-steppers-scope').append(overlay);
}
}
scope.$ctrl.$stepper = stepperCtrl;
scope.$watch(function () {
return scope.$ctrl.isActive();
}, function (isActive) {
if (isActive) {
iElement.addClass('md-active');
addOverlay();
}
else {
iElement.removeClass('md-active');
}
});
},
templateUrl: 'mdSteppers/mdStep.tpl.html'
};
}])
.config(['$mdIconProvider', function ($mdIconProvider) {
$mdIconProvider.icon('steppers-check', 'mdSteppers/ic_check_24px.svg');
$mdIconProvider.icon('steppers-warning', 'mdSteppers/ic_warning_24px.svg');
}])
.run(["$templateCache", function ($templateCache) {
$templateCache.put("mdSteppers/ic_check_24px.svg", "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z\"/>\r\n</svg>");
$templateCache.put("mdSteppers/ic_warning_24px.svg", "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\">\r\n <path d=\"M0 0h24v24H0z\" fill=\"none\"/>\r\n <path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/>\r\n</svg>");
}]);
angular.module("mdSteppers").run(["$templateCache", function($templateCache) {$templateCache.put("mdSteppers/mdStep.tpl.html","<div class=\"md-stepper\" ng-class=\"{ \'md-active\': $ctrl.isActive() }\">\r\n <md-steppers-header class=\"md-steppers-header md-steppers-vertical\">\r\n <button class=\"md-stepper-indicator\" ng-class=\"{\r\n \'md-active\': $ctrl.stepNumber === $ctrl.$stepper.currentStep,\r\n \'md-completed\': $ctrl.$stepper.isCompleted($ctrl.stepNumber),\r\n \'md-error\': $ctrl.hasError,\r\n \'md-stepper-optional\': $ctrl.optional || $ctrl.hasError\r\n }\" ng-click=\"$ctrl.$stepper.goto($ctrl.stepNumber)\" ng-disabled=\"$ctrl.$stepper.linear || $ctrl.stepNumber === $ctrl.$stepper.currentStep\">\r\n <div class=\"md-stepper-indicator-wrapper\">\r\n <div class=\"md-stepper-number\" ng-hide=\"$ctrl.hasError\">\r\n <span ng-if=\"!$ctrl.$stepper.isCompleted($ctrl.stepNumber)\">{{ ::$ctrl.stepNumber+1 }}</span>\r\n <md-icon md-svg-icon=\"steppers-check\" class=\"md-stepper-icon\" ng-if=\"$ctrl.$stepper.isCompleted($ctrl.stepNumber)\"></md-icon>\r\n </div>\r\n <div class=\"md-stepper-error-indicator\" ng-show=\"$ctrl.hasError\">\r\n <md-icon md-svg-icon=\"steppers-warning\"></md-icon>\r\n </div>\r\n\r\n <div class=\"md-stepper-title\">\r\n <span>{{ $ctrl.label }}</span>\r\n <small ng-if=\"$ctrl.optional && !$ctrl.hasError\">{{ $ctrl.optional }}</small>\r\n <small class=\"md-stepper-error-message\" ng-show=\"$ctrl.hasError\">\r\n {{ $ctrl.message }}\r\n </small>\r\n </div>\r\n </div>\r\n </button>\r\n <div class=\"md-stepper-feedback-message\" ng-show=\"stepper.hasFeedback\">\r\n {{stepper.feedbackMessage}}\r\n </div>\r\n </md-steppers-header>\r\n <md-steppers-scope layout=\"column\" class=\"md-steppers-scope\" ng-if=\"$ctrl.isActive()\" ng-transclude></md-steppers-scope>\r\n</div>");
$templateCache.put("mdSteppers/mdStepper.tpl.html","<div flex class=\"md-steppers\" ng-class=\"{ \r\n \'md-steppers-linear\': stepper.linear, \r\n \'md-steppers-alternative\': stepper.alternative,\r\n \'md-steppers-vertical\': stepper.vertical,\r\n \'md-steppers-mobile-step-text\': stepper.mobileStepText,\r\n \'md-steppers-has-feedback\': stepper.hasFeedback\r\n }\">\r\n <div class=\"md-steppers-header-region\">\r\n <md-steppers-header class=\"md-steppers-header md-steppers-horizontal md-whiteframe-1dp\">\r\n <button class=\"md-stepper-indicator\" ng-repeat=\"(stepNumber, $step) in stepper.steps track by $index\" ng-class=\"{\r\n \'md-active\': stepNumber === stepper.currentStep,\r\n \'md-completed\': stepper.isCompleted(stepNumber),\r\n \'md-error\': $step.hasError,\r\n \'md-stepper-optional\': $step.optional || $step.hasError\r\n }\" ng-click=\"stepper.goto(stepNumber)\" ng-disabled=\"stepper.linear || stepNumber === stepper.currentStep\">\r\n <div class=\"md-stepper-indicator-wrapper\">\r\n <div class=\"md-stepper-number\" ng-hide=\"$step.hasError\">\r\n <span ng-if=\"!stepper.isCompleted(stepNumber)\">{{ ::stepNumber+1 }}</span>\r\n <md-icon md-svg-icon=\"steppers-check\" class=\"md-stepper-icon\" ng-if=\"stepper.isCompleted(stepNumber)\"></md-icon>\r\n </div>\r\n\r\n <div class=\"md-stepper-error-indicator\" ng-show=\"$step.hasError\">\r\n <md-icon md-svg-icon=\"steppers-warning\"></md-icon>\r\n </div>\r\n <div class=\"md-stepper-title\">\r\n <span>{{ $step.label }}</span>\r\n <small ng-if=\"$step.optional && !$step.hasError\">{{ $step.optional }}</small>\r\n <small class=\"md-stepper-error-message\" ng-show=\"$step.hasError\">\r\n {{ $step.message }}\r\n </small>\r\n </div>\r\n </div>\r\n </button>\r\n \r\n </md-steppers-header>\r\n <md-steppers-mobile-header class=\"md-steppers-mobile-header\">\r\n <md-toolbar flex=\"none\" class=\"md-whiteframe-1dp\" style=\"background: #f6f6f6 !important; color: #202020 !important;\">\r\n <div class=\"md-toolbar-tools\">\r\n <h3>\r\n <span>{{stepper.labelStep}} {{stepper.currentStep+1}} {{stepper.labelOf}} {{stepper.steps.length}}</span>\r\n </h3>\r\n </div>\r\n </md-toolbar>\r\n </md-steppers-mobile-header>\r\n <div class=\"md-stepper-feedback-message\" ng-show=\"stepper.hasFeedback\">\r\n {{stepper.feedbackMessage}}\r\n </div>\r\n </div>\r\n <md-steppers-content class=\"md-steppers-content\" ng-transclude></md-steppers-content>\r\n <div class=\"md-steppers-overlay\"></div>\r\n</div>");}]);