fabToolbar.js 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. /*!
  2. * Angular Material Design
  3. * https://github.com/angular/material
  4. * @license MIT
  5. * v0.11.4
  6. */
  7. (function( window, angular, undefined ){
  8. "use strict";
  9. (function() {
  10. 'use strict';
  11. /**
  12. * @ngdoc module
  13. * @name material.components.fabToolbar
  14. */
  15. angular
  16. // Declare our module
  17. .module('material.components.fabToolbar', [
  18. 'material.core',
  19. 'material.components.fabShared',
  20. 'material.components.fabTrigger',
  21. 'material.components.fabActions'
  22. ])
  23. // Register our directive
  24. .directive('mdFabToolbar', MdFabToolbarDirective)
  25. // Register our custom animations
  26. .animation('.md-fab-toolbar', MdFabToolbarAnimation)
  27. // Register a service for the animation so that we can easily inject it into unit tests
  28. .service('mdFabToolbarAnimation', MdFabToolbarAnimation);
  29. /**
  30. * @ngdoc directive
  31. * @name mdFabToolbar
  32. * @module material.components.fabToolbar
  33. *
  34. * @restrict E
  35. *
  36. * @description
  37. *
  38. * The `<md-fab-toolbar>` directive is used present a toolbar of elements (usually `<md-button>`s)
  39. * for quick access to common actions when a floating action button is activated (via click or
  40. * keyboard navigation).
  41. *
  42. * You may also easily position the trigger by applying one one of the following classes to the
  43. * `<md-fab-toolbar>` element:
  44. * - `md-fab-top-left`
  45. * - `md-fab-top-right`
  46. * - `md-fab-bottom-left`
  47. * - `md-fab-bottom-right`
  48. *
  49. * These CSS classes use `position: absolute`, so you need to ensure that the container element
  50. * also uses `position: absolute` or `position: relative` in order for them to work.
  51. *
  52. * @usage
  53. *
  54. * <hljs lang="html">
  55. * <md-fab-toolbar md-direction='left'>
  56. * <md-fab-trigger>
  57. * <md-button aria-label="Add..."><md-icon icon="/img/icons/plus.svg"></md-icon></md-button>
  58. * </md-fab-trigger>
  59. *
  60. * <md-fab-actions>
  61. * <md-button aria-label="Add User">
  62. * <md-icon icon="/img/icons/user.svg"></md-icon>
  63. * </md-button>
  64. *
  65. * <md-button aria-label="Add Group">
  66. * <md-icon icon="/img/icons/group.svg"></md-icon>
  67. * </md-button>
  68. * </md-fab-actions>
  69. * </md-fab-toolbar>
  70. * </hljs>
  71. *
  72. * @param {string} md-direction From which direction you would like the toolbar items to appear
  73. * relative to the trigger element. Supports `left` and `right` directions.
  74. * @param {expression=} md-open Programmatically control whether or not the toolbar is visible.
  75. */
  76. function MdFabToolbarDirective() {
  77. return {
  78. restrict: 'E',
  79. transclude: true,
  80. template: '<div class="md-fab-toolbar-wrapper">' +
  81. ' <div class="md-fab-toolbar-content" ng-transclude></div>' +
  82. '</div>',
  83. scope: {
  84. direction: '@?mdDirection',
  85. isOpen: '=?mdOpen'
  86. },
  87. bindToController: true,
  88. controller: 'FabController',
  89. controllerAs: 'vm',
  90. link: link
  91. };
  92. function link(scope, element, attributes) {
  93. // Add the base class for animations
  94. element.addClass('md-fab-toolbar');
  95. // Prepend the background element to the trigger's button
  96. element.find('md-fab-trigger').find('button')
  97. .prepend('<div class="md-fab-toolbar-background"></div>');
  98. }
  99. }
  100. function MdFabToolbarAnimation() {
  101. function runAnimation(element, className, done) {
  102. // If no className was specified, don't do anything
  103. if (!className) {
  104. return;
  105. }
  106. var el = element[0];
  107. var ctrl = element.controller('mdFabToolbar');
  108. // Grab the relevant child elements
  109. var backgroundElement = el.querySelector('.md-fab-toolbar-background');
  110. var triggerElement = el.querySelector('md-fab-trigger button');
  111. var toolbarElement = el.querySelector('md-toolbar');
  112. var iconElement = el.querySelector('md-fab-trigger button md-icon');
  113. var actions = element.find('md-fab-actions').children();
  114. // If we have both elements, use them to position the new background
  115. if (triggerElement && backgroundElement) {
  116. // Get our variables
  117. var color = window.getComputedStyle(triggerElement).getPropertyValue('background-color');
  118. var width = el.offsetWidth;
  119. var height = el.offsetHeight;
  120. // Make it twice as big as it should be since we scale from the center
  121. var scale = 2 * (width / triggerElement.offsetWidth);
  122. // Set some basic styles no matter what animation we're doing
  123. backgroundElement.style.backgroundColor = color;
  124. backgroundElement.style.borderRadius = width + 'px';
  125. // If we're open
  126. if (ctrl.isOpen) {
  127. // Turn on toolbar pointer events when closed
  128. toolbarElement.style.pointerEvents = 'initial';
  129. backgroundElement.style.width = triggerElement.offsetWidth + 'px';
  130. backgroundElement.style.height = triggerElement.offsetHeight + 'px';
  131. backgroundElement.style.transform = 'scale(' + scale + ')';
  132. // Set the next close animation to have the proper delays
  133. backgroundElement.style.transitionDelay = '0ms';
  134. iconElement && (iconElement.style.transitionDelay = '.3s');
  135. // Apply a transition delay to actions
  136. angular.forEach(actions, function(action, index) {
  137. action.style.transitionDelay = (actions.length - index) * 25 + 'ms';
  138. });
  139. } else {
  140. // Turn off toolbar pointer events when closed
  141. toolbarElement.style.pointerEvents = 'none';
  142. // Scale it back down to the trigger's size
  143. backgroundElement.style.transform = 'scale(1)';
  144. // Reset the position
  145. backgroundElement.style.top = '0';
  146. if (element.hasClass('md-right')) {
  147. backgroundElement.style.left = '0';
  148. backgroundElement.style.right = null;
  149. }
  150. if (element.hasClass('md-left')) {
  151. backgroundElement.style.right = '0';
  152. backgroundElement.style.left = null;
  153. }
  154. // Set the next open animation to have the proper delays
  155. backgroundElement.style.transitionDelay = '200ms';
  156. iconElement && (iconElement.style.transitionDelay = '0ms');
  157. // Apply a transition delay to actions
  158. angular.forEach(actions, function(action, index) {
  159. action.style.transitionDelay = 200 + (index * 25) + 'ms';
  160. });
  161. }
  162. }
  163. }
  164. return {
  165. addClass: function(element, className, done) {
  166. runAnimation(element, className, done);
  167. done();
  168. },
  169. removeClass: function(element, className, done) {
  170. runAnimation(element, className, done);
  171. done();
  172. }
  173. }
  174. }
  175. })();
  176. })(window, window.angular);