/*! * Angular Material Design * https://github.com/angular/material * @license MIT * v0.10.0 */ (function( window, angular, undefined ){ "use strict"; (function() { 'use strict'; angular .module('material.components.fabSpeedDial', [ 'material.core', 'material.components.fabTrigger', 'material.components.fabActions' ]) .directive('mdFabSpeedDial', MdFabSpeedDialDirective) .animation('.md-fling', MdFabSpeedDialFlingAnimation) .animation('.md-scale', MdFabSpeedDialScaleAnimation); /** * @ngdoc directive * @name mdFabSpeedDial * @module material.components.fabSpeedDial * * @restrict E * * @description * The `` directive is used to present a series of popup elements (usually * ``s) for quick access to common actions. * * There are currently two animations available by applying one of the following classes to * the component: * * - `md-fling` - The speed dial items appear from underneath the trigger and move into their * appropriate positions. * - `md-scale` - The speed dial items appear in their proper places by scaling from 0% to 100%. * * @usage * * * * * * * * * * * * * * * * * * * @param {string=} md-direction From which direction you would like the speed dial to appear * relative to the trigger element. * @param {expression=} md-open Programmatically control whether or not the speed-dial is visible. */ function MdFabSpeedDialDirective() { FabSpeedDialController.$inject = ["$scope", "$element", "$animate"]; return { restrict: 'E', scope: { direction: '@?mdDirection', isOpen: '=?mdOpen' }, bindToController: true, controller: FabSpeedDialController, controllerAs: 'vm', link: FabSpeedDialLink }; function FabSpeedDialLink(scope, element) { // Prepend an element to hold our CSS variables so we can use them in the animations below element.prepend('
'); } function FabSpeedDialController($scope, $element, $animate) { var vm = this; // Define our open/close functions // Note: Used by fabTrigger and fabActions directives vm.open = function() { $scope.$apply('vm.isOpen = true'); }; vm.close = function() { $scope.$apply('vm.isOpen = false'); }; setupDefaults(); setupListeners(); setupWatchers(); // Set our default variables function setupDefaults() { // Set the default direction to 'down' if none is specified vm.direction = vm.direction || 'down'; // Set the default to be closed vm.isOpen = vm.isOpen || false; } // Setup our event listeners function setupListeners() { $element.on('mouseenter', vm.open); $element.on('mouseleave', vm.close); } // Setup our watchers function setupWatchers() { // Watch for changes to the direction and update classes/attributes $scope.$watch('vm.direction', function(newDir, oldDir) { // Add the appropriate classes so we can target the direction in the CSS $animate.removeClass($element, 'md-' + oldDir); $animate.addClass($element, 'md-' + newDir); }); // Watch for changes to md-open $scope.$watch('vm.isOpen', function(isOpen) { var toAdd = isOpen ? 'md-is-open' : ''; var toRemove = isOpen ? '' : 'md-is-open'; $animate.setClass($element, toAdd, toRemove); }); } } } function MdFabSpeedDialFlingAnimation() { function runAnimation(element) { var el = element[0]; var ctrl = element.controller('mdFabSpeedDial'); var items = el.querySelectorAll('.md-fab-action-item'); // Grab our element which stores CSS variables var variablesElement = el.querySelector('.md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = variablesElement.style.zIndex; // Always reset the items to their natural position/state angular.forEach(items, function(item, index) { var styles = item.style; styles.transform = ''; styles.transitionDelay = ''; styles.opacity = 1; // Make the items closest to the trigger have the highest z-index item.style.zIndex = (items.length - index) + startZIndex; }); // If the control is closed, hide the items behind the trigger if (!ctrl.isOpen) { angular.forEach(items, function(item, index) { var newPosition, axis; switch (ctrl.direction) { case 'up': newPosition = item.scrollHeight * (index + 1); axis = 'Y'; break; case 'down': newPosition = -item.scrollHeight * (index + 1); axis = 'Y'; break; case 'left': newPosition = item.scrollWidth * (index + 1); axis = 'X'; break; case 'right': newPosition = -item.scrollWidth * (index + 1); axis = 'X'; break; } item.style.transform = 'translate' + axis + '(' + newPosition + 'px)'; }); } } return { addClass: function(element, className, done) { if (element.hasClass('md-fling')) { runAnimation(element); } }, removeClass: function(element, className, done) { runAnimation(element); } } } function MdFabSpeedDialScaleAnimation() { var delay = 65; function runAnimation(element) { var el = element[0]; var ctrl = element.controller('mdFabSpeedDial'); var items = el.querySelectorAll('.md-fab-action-item'); // Always reset the items to their natural position/state angular.forEach(items, function(item, index) { var styles = item.style, offsetDelay = index * delay; styles.opacity = ctrl.isOpen ? 1 : 0; styles.transform = ctrl.isOpen ? 'scale(1)' : 'scale(0)'; styles.transitionDelay = (ctrl.isOpen ? offsetDelay : (items.length - offsetDelay)) + 'ms'; }); } return { addClass: function(element, className, done) { runAnimation(element); }, removeClass: function(element, className, done) { runAnimation(element); } } } })(); })(window, window.angular);