UNPKG

angular-material-npfixed

Version:

The Angular Material project is an implementation of Material Design in Angular.js. This project provides a set of reusable, well-tested, and accessible Material Design UI components. Angular Material is supported internally at Google by the Angular.js, M

254 lines (217 loc) 8.81 kB
(function() { 'use strict'; /** * The duration of the CSS animation in milliseconds. * * @type {number} */ var cssAnimationDuration = 300; /** * @ngdoc module * @name material.components.fabSpeedDial */ angular // Declare our module .module('material.components.fabSpeedDial', [ 'material.core', 'material.components.fabShared', 'material.components.fabActions' ]) // Register our directive .directive('mdFabSpeedDial', MdFabSpeedDialDirective) // Register our custom animations .animation('.md-fling', MdFabSpeedDialFlingAnimation) .animation('.md-scale', MdFabSpeedDialScaleAnimation) // Register a service for each animation so that we can easily inject them into unit tests .service('mdFabSpeedDialFlingAnimation', MdFabSpeedDialFlingAnimation) .service('mdFabSpeedDialScaleAnimation', MdFabSpeedDialScaleAnimation); /** * @ngdoc directive * @name mdFabSpeedDial * @module material.components.fabSpeedDial * * @restrict E * * @description * The `<md-fab-speed-dial>` directive is used to present a series of popup elements (usually * `<md-button>`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%. * * You may also easily position the trigger by applying one one of the following classes to the * `<md-fab-speed-dial>` element: * - `md-fab-top-left` * - `md-fab-top-right` * - `md-fab-bottom-left` * - `md-fab-bottom-right` * * These CSS classes use `position: absolute`, so you need to ensure that the container element * also uses `position: absolute` or `position: relative` in order for them to work. * * Additionally, you may use the standard `ng-mouseenter` and `ng-mouseleave` directives to * open or close the speed dial. However, if you wish to allow users to hover over the empty * space where the actions will appear, you must also add the `md-hover-full` class to the speed * dial element. Without this, the hover effect will only occur on top of the trigger. * * See the demos for more information. * * ## Troubleshooting * * If your speed dial shows the closing animation upon launch, you may need to use `ng-cloak` on * the parent container to ensure that it is only visible once ready. We have plans to remove this * necessity in the future. * * @usage * <hljs lang="html"> * <md-fab-speed-dial md-direction="up" class="md-fling"> * <md-fab-trigger> * <md-button aria-label="Add..."><md-icon md-svg-src="/img/icons/plus.svg"></md-icon></md-button> * </md-fab-trigger> * * <md-fab-actions> * <md-button aria-label="Add User"> * <md-icon md-svg-src="/img/icons/user.svg"></md-icon> * </md-button> * * <md-button aria-label="Add Group"> * <md-icon md-svg-src="/img/icons/group.svg"></md-icon> * </md-button> * </md-fab-actions> * </md-fab-speed-dial> * </hljs> * * @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() { return { restrict: 'E', scope: { direction: '@?mdDirection', isOpen: '=?mdOpen' }, bindToController: true, controller: 'MdFabController', 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('<div class="_md-css-variables"></div>'); } } function MdFabSpeedDialFlingAnimation($timeout) { function delayDone(done) { $timeout(done, cssAnimationDuration, false); } function runAnimation(element) { // Don't run if we are still waiting and we are not ready if (element.hasClass('md-animations-waiting') && !element.hasClass('_md-animations-ready')) { return; } var el = element[0]; var ctrl = element.controller('mdFabSpeedDial'); var items = el.querySelectorAll('.md-fab-action-item'); // Grab our trigger element var triggerElement = el.querySelector('md-fab-trigger'); // Grab our element which stores CSS variables var variablesElement = el.querySelector('._md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex); // Always reset the items to their natural position/state angular.forEach(items, function(item, index) { var styles = item.style; styles.transform = styles.webkitTransform = ''; styles.transitionDelay = ''; styles.opacity = 1; // Make the items closest to the trigger have the highest z-index styles.zIndex = (items.length - index) + startZIndex; }); // Set the trigger to be above all of the actions so they disappear behind it. triggerElement.style.zIndex = startZIndex + items.length + 1; // If the control is closed, hide the items behind the trigger if (!ctrl.isOpen) { angular.forEach(items, function(item, index) { var newPosition, axis; var styles = item.style; // Make sure to account for differences in the dimensions of the trigger verses the items // so that we can properly center everything; this helps hide the item's shadows behind // the trigger. var triggerItemHeightOffset = (triggerElement.clientHeight - item.clientHeight) / 2; var triggerItemWidthOffset = (triggerElement.clientWidth - item.clientWidth) / 2; switch (ctrl.direction) { case 'up': newPosition = (item.scrollHeight * (index + 1) + triggerItemHeightOffset); axis = 'Y'; break; case 'down': newPosition = -(item.scrollHeight * (index + 1) + triggerItemHeightOffset); axis = 'Y'; break; case 'left': newPosition = (item.scrollWidth * (index + 1) + triggerItemWidthOffset); axis = 'X'; break; case 'right': newPosition = -(item.scrollWidth * (index + 1) + triggerItemWidthOffset); axis = 'X'; break; } var newTranslate = 'translate' + axis + '(' + newPosition + 'px)'; styles.transform = styles.webkitTransform = newTranslate; }); } } return { addClass: function(element, className, done) { if (element.hasClass('md-fling')) { runAnimation(element); delayDone(done); } else { done(); } }, removeClass: function(element, className, done) { runAnimation(element); delayDone(done); } }; } function MdFabSpeedDialScaleAnimation($timeout) { function delayDone(done) { $timeout(done, cssAnimationDuration, false); } var delay = 65; 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 = parseInt(window.getComputedStyle(variablesElement).zIndex); // 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 = styles.webkitTransform = ctrl.isOpen ? 'scale(1)' : 'scale(0)'; styles.transitionDelay = (ctrl.isOpen ? offsetDelay : (items.length - offsetDelay)) + 'ms'; // Make the items closest to the trigger have the highest z-index styles.zIndex = (items.length - index) + startZIndex; }); } return { addClass: function(element, className, done) { runAnimation(element); delayDone(done); }, removeClass: function(element, className, done) { runAnimation(element); delayDone(done); } }; } })();