1 | /*!
|
---|
2 | * AngularJS Material Design
|
---|
3 | * https://github.com/angular/material
|
---|
4 | * @license MIT
|
---|
5 | * v1.2.3
|
---|
6 | */
|
---|
7 | (function( window, angular, undefined ){
|
---|
8 | "use strict";
|
---|
9 |
|
---|
10 | (function() {
|
---|
11 | 'use strict';
|
---|
12 |
|
---|
13 | MdFabController['$inject'] = ["$scope", "$element", "$animate", "$mdUtil", "$mdConstant", "$timeout"];
|
---|
14 | angular.module('material.components.fabShared', ['material.core'])
|
---|
15 | .controller('MdFabController', MdFabController);
|
---|
16 |
|
---|
17 | function MdFabController($scope, $element, $animate, $mdUtil, $mdConstant, $timeout) {
|
---|
18 | var ctrl = this;
|
---|
19 | var initialAnimationAttempts = 0;
|
---|
20 |
|
---|
21 | // NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops
|
---|
22 |
|
---|
23 | ctrl.open = function() {
|
---|
24 | $scope.$evalAsync("ctrl.isOpen = true");
|
---|
25 | };
|
---|
26 |
|
---|
27 | ctrl.close = function() {
|
---|
28 | // Async eval to avoid conflicts with existing digest loops
|
---|
29 | $scope.$evalAsync("ctrl.isOpen = false");
|
---|
30 |
|
---|
31 | // Focus the trigger when the element closes so users can still tab to the next item
|
---|
32 | $element.find('md-fab-trigger')[0].focus();
|
---|
33 | };
|
---|
34 |
|
---|
35 | // Toggle the open/close state when the trigger is clicked
|
---|
36 | ctrl.toggle = function() {
|
---|
37 | $scope.$evalAsync("ctrl.isOpen = !ctrl.isOpen");
|
---|
38 | };
|
---|
39 |
|
---|
40 | /*
|
---|
41 | * AngularJS Lifecycle hook for newer AngularJS versions.
|
---|
42 | * Bindings are not guaranteed to have been assigned in the controller, but they are in the
|
---|
43 | * $onInit hook.
|
---|
44 | */
|
---|
45 | ctrl.$onInit = function() {
|
---|
46 | setupDefaults();
|
---|
47 | setupListeners();
|
---|
48 | setupWatchers();
|
---|
49 |
|
---|
50 | fireInitialAnimations();
|
---|
51 | };
|
---|
52 |
|
---|
53 | // For AngularJS 1.4 and older, where there are no lifecycle hooks but bindings are pre-assigned,
|
---|
54 | // manually call the $onInit hook.
|
---|
55 | if (angular.version.major === 1 && angular.version.minor <= 4) {
|
---|
56 | this.$onInit();
|
---|
57 | }
|
---|
58 |
|
---|
59 | function setupDefaults() {
|
---|
60 | // Set the default direction to 'down' if none is specified
|
---|
61 | ctrl.direction = ctrl.direction || 'down';
|
---|
62 |
|
---|
63 | // Set the default to be closed
|
---|
64 | ctrl.isOpen = ctrl.isOpen || false;
|
---|
65 |
|
---|
66 | // Start the keyboard interaction at the first action
|
---|
67 | resetActionIndex();
|
---|
68 |
|
---|
69 | // Add an animations waiting class so we know not to run
|
---|
70 | $element.addClass('md-animations-waiting');
|
---|
71 | }
|
---|
72 |
|
---|
73 | function setupListeners() {
|
---|
74 | var eventTypes = [
|
---|
75 | 'click', 'focusin', 'focusout'
|
---|
76 | ];
|
---|
77 |
|
---|
78 | // Add our listeners
|
---|
79 | angular.forEach(eventTypes, function(eventType) {
|
---|
80 | $element.on(eventType, parseEvents);
|
---|
81 | });
|
---|
82 |
|
---|
83 | // Remove our listeners when destroyed
|
---|
84 | $scope.$on('$destroy', function() {
|
---|
85 | angular.forEach(eventTypes, function(eventType) {
|
---|
86 | $element.off(eventType, parseEvents);
|
---|
87 | });
|
---|
88 |
|
---|
89 | // remove any attached keyboard handlers in case element is removed while
|
---|
90 | // speed dial is open
|
---|
91 | disableKeyboard();
|
---|
92 | });
|
---|
93 | }
|
---|
94 |
|
---|
95 | var closeTimeout;
|
---|
96 |
|
---|
97 | /**
|
---|
98 | * @param {MouseEvent} event
|
---|
99 | */
|
---|
100 | function parseEvents(event) {
|
---|
101 | // If the event is a click, just handle it
|
---|
102 | if (event.type == 'click') {
|
---|
103 | handleItemClick(event);
|
---|
104 | }
|
---|
105 |
|
---|
106 | // If we focusout, set a timeout to close the element
|
---|
107 | if (event.type == 'focusout' && !closeTimeout) {
|
---|
108 | closeTimeout = $timeout(function() {
|
---|
109 | ctrl.close();
|
---|
110 | }, 100, false);
|
---|
111 | }
|
---|
112 |
|
---|
113 | // If we see a focusin and there is a timeout about to run, cancel it so we stay open
|
---|
114 | if (event.type == 'focusin' && closeTimeout) {
|
---|
115 | $timeout.cancel(closeTimeout);
|
---|
116 | closeTimeout = null;
|
---|
117 | }
|
---|
118 | }
|
---|
119 |
|
---|
120 | function resetActionIndex() {
|
---|
121 | ctrl.currentActionIndex = -1;
|
---|
122 | }
|
---|
123 |
|
---|
124 | function setupWatchers() {
|
---|
125 | // Watch for changes to the direction and update classes/attributes
|
---|
126 | $scope.$watch('ctrl.direction', function(newDir, oldDir) {
|
---|
127 | // Add the appropriate classes so we can target the direction in the CSS
|
---|
128 | $animate.removeClass($element, 'md-' + oldDir);
|
---|
129 | $animate.addClass($element, 'md-' + newDir);
|
---|
130 |
|
---|
131 | // Reset the action index since it may have changed
|
---|
132 | resetActionIndex();
|
---|
133 | });
|
---|
134 |
|
---|
135 | var trigger, actions;
|
---|
136 |
|
---|
137 | // Watch for changes to md-open
|
---|
138 | $scope.$watch('ctrl.isOpen', function(isOpen) {
|
---|
139 | // Reset the action index since it may have changed
|
---|
140 | resetActionIndex();
|
---|
141 |
|
---|
142 | // We can't get the trigger/actions outside of the watch because the component hasn't been
|
---|
143 | // linked yet, so we wait until the first watch fires to cache them.
|
---|
144 | if (!trigger || !actions) {
|
---|
145 | trigger = getTriggerElement();
|
---|
146 | actions = getActionsElement();
|
---|
147 | }
|
---|
148 |
|
---|
149 | if (isOpen) {
|
---|
150 | enableKeyboard();
|
---|
151 | } else {
|
---|
152 | disableKeyboard();
|
---|
153 | }
|
---|
154 |
|
---|
155 | var toAdd = isOpen ? 'md-is-open' : '';
|
---|
156 | var toRemove = isOpen ? '' : 'md-is-open';
|
---|
157 |
|
---|
158 | // Set the proper ARIA attributes
|
---|
159 | trigger.attr('aria-haspopup', true);
|
---|
160 | trigger.attr('aria-expanded', isOpen);
|
---|
161 | actions.attr('aria-hidden', !isOpen);
|
---|
162 |
|
---|
163 | // Animate the CSS classes
|
---|
164 | $animate.setClass($element, toAdd, toRemove);
|
---|
165 | });
|
---|
166 | }
|
---|
167 |
|
---|
168 | function fireInitialAnimations() {
|
---|
169 | // If the element is actually visible on the screen
|
---|
170 | if ($element[0].scrollHeight > 0) {
|
---|
171 | // Fire our animation
|
---|
172 | $animate.addClass($element, '_md-animations-ready').then(function() {
|
---|
173 | // Remove the waiting class
|
---|
174 | $element.removeClass('md-animations-waiting');
|
---|
175 | });
|
---|
176 | }
|
---|
177 |
|
---|
178 | // Otherwise, try for up to 1 second before giving up
|
---|
179 | else if (initialAnimationAttempts < 10) {
|
---|
180 | $timeout(fireInitialAnimations, 100);
|
---|
181 |
|
---|
182 | // Increment our counter
|
---|
183 | initialAnimationAttempts = initialAnimationAttempts + 1;
|
---|
184 | }
|
---|
185 | }
|
---|
186 |
|
---|
187 | function enableKeyboard() {
|
---|
188 | $element.on('keydown', keyPressed);
|
---|
189 |
|
---|
190 | // On the next tick, setup a check for outside clicks; we do this on the next tick to avoid
|
---|
191 | // clicks/touches that result in the isOpen attribute changing (e.g. a bound radio button)
|
---|
192 | $mdUtil.nextTick(function() {
|
---|
193 | angular.element(document).on('click touchend', checkForOutsideClick);
|
---|
194 | });
|
---|
195 | }
|
---|
196 |
|
---|
197 | function disableKeyboard() {
|
---|
198 | $element.off('keydown', keyPressed);
|
---|
199 | angular.element(document).off('click touchend', checkForOutsideClick);
|
---|
200 | }
|
---|
201 |
|
---|
202 | function checkForOutsideClick(event) {
|
---|
203 | if (event.target) {
|
---|
204 | var closestTrigger = $mdUtil.getClosest(event.target, 'md-fab-trigger');
|
---|
205 | var closestActions = $mdUtil.getClosest(event.target, 'md-fab-actions');
|
---|
206 |
|
---|
207 | if (!closestTrigger && !closestActions) {
|
---|
208 | ctrl.close();
|
---|
209 | }
|
---|
210 | }
|
---|
211 | }
|
---|
212 |
|
---|
213 | /**
|
---|
214 | * @param {KeyboardEvent} event
|
---|
215 | * @returns {boolean}
|
---|
216 | */
|
---|
217 | function keyPressed(event) {
|
---|
218 | switch (event.which) {
|
---|
219 | case $mdConstant.KEY_CODE.ESCAPE: ctrl.close(); event.preventDefault(); return false;
|
---|
220 | case $mdConstant.KEY_CODE.LEFT_ARROW: doKeyLeft(event); return false;
|
---|
221 | case $mdConstant.KEY_CODE.UP_ARROW: doKeyUp(event); return false;
|
---|
222 | case $mdConstant.KEY_CODE.RIGHT_ARROW: doKeyRight(event); return false;
|
---|
223 | case $mdConstant.KEY_CODE.DOWN_ARROW: doKeyDown(event); return false;
|
---|
224 | case $mdConstant.KEY_CODE.TAB: doShift(event); return false;
|
---|
225 | }
|
---|
226 | }
|
---|
227 |
|
---|
228 | function doActionPrev(event) {
|
---|
229 | focusAction(event, -1);
|
---|
230 | }
|
---|
231 |
|
---|
232 | function doActionNext(event) {
|
---|
233 | focusAction(event, 1);
|
---|
234 | }
|
---|
235 |
|
---|
236 | function focusAction(event, direction) {
|
---|
237 | var actions = getActionsElement()[0].querySelectorAll('.md-fab-action-item');
|
---|
238 | var previousActionIndex = ctrl.currentActionIndex;
|
---|
239 |
|
---|
240 | // Increment/decrement the counter with restrictions
|
---|
241 | ctrl.currentActionIndex = ctrl.currentActionIndex + direction;
|
---|
242 | ctrl.currentActionIndex = Math.min(actions.length - 1, ctrl.currentActionIndex);
|
---|
243 | ctrl.currentActionIndex = Math.max(0, ctrl.currentActionIndex);
|
---|
244 |
|
---|
245 | // Let Tab and Shift+Tab escape if we're trying to move past the start/end.
|
---|
246 | if (event.which !== $mdConstant.KEY_CODE.TAB ||
|
---|
247 | previousActionIndex !== ctrl.currentActionIndex) {
|
---|
248 | // Focus the element
|
---|
249 | var focusElement = angular.element(actions[ctrl.currentActionIndex]).children()[0];
|
---|
250 | focusElement.focus();
|
---|
251 |
|
---|
252 | // Make sure the event doesn't bubble and cause something else
|
---|
253 | event.preventDefault();
|
---|
254 | event.stopImmediatePropagation();
|
---|
255 | }
|
---|
256 | }
|
---|
257 |
|
---|
258 | function doKeyLeft(event) {
|
---|
259 | if (ctrl.direction === 'left') {
|
---|
260 | doActionNext(event);
|
---|
261 | } else {
|
---|
262 | doActionPrev(event);
|
---|
263 | }
|
---|
264 | }
|
---|
265 |
|
---|
266 | function doKeyUp(event) {
|
---|
267 | if (ctrl.direction === 'down') {
|
---|
268 | doActionPrev(event);
|
---|
269 | } else {
|
---|
270 | doActionNext(event);
|
---|
271 | }
|
---|
272 | }
|
---|
273 |
|
---|
274 | function doKeyRight(event) {
|
---|
275 | if (ctrl.direction === 'left') {
|
---|
276 | doActionPrev(event);
|
---|
277 | } else {
|
---|
278 | doActionNext(event);
|
---|
279 | }
|
---|
280 | }
|
---|
281 |
|
---|
282 | function doKeyDown(event) {
|
---|
283 | if (ctrl.direction === 'up') {
|
---|
284 | doActionPrev(event);
|
---|
285 | } else {
|
---|
286 | doActionNext(event);
|
---|
287 | }
|
---|
288 | }
|
---|
289 |
|
---|
290 | function doShift(event) {
|
---|
291 | if (event.shiftKey) {
|
---|
292 | doActionPrev(event);
|
---|
293 | } else {
|
---|
294 | doActionNext(event);
|
---|
295 | }
|
---|
296 | }
|
---|
297 |
|
---|
298 | /**
|
---|
299 | * @param {Node} element
|
---|
300 | * @returns {Node|null}
|
---|
301 | */
|
---|
302 | function getClosestButton(element) {
|
---|
303 | return $mdUtil.getClosest(element, 'button') || $mdUtil.getClosest(element, 'md-button');
|
---|
304 | }
|
---|
305 |
|
---|
306 | /**
|
---|
307 | * @param {Node} element
|
---|
308 | * @returns {Node|null}
|
---|
309 | */
|
---|
310 | function getClosestTrigger(element) {
|
---|
311 | return $mdUtil.getClosest(element, 'md-fab-trigger');
|
---|
312 | }
|
---|
313 |
|
---|
314 | /**
|
---|
315 | * @param {Node} element
|
---|
316 | * @returns {Node|null}
|
---|
317 | */
|
---|
318 | function getClosestAction(element) {
|
---|
319 | return $mdUtil.getClosest(element, 'md-fab-actions');
|
---|
320 | }
|
---|
321 |
|
---|
322 | /**
|
---|
323 | * @param {MouseEvent|FocusEvent} event
|
---|
324 | */
|
---|
325 | function handleItemClick(event) {
|
---|
326 | var closestButton = event.target ? getClosestButton(event.target) : null;
|
---|
327 |
|
---|
328 | // Check that the button in the trigger is not disabled
|
---|
329 | if (closestButton && !closestButton.disabled) {
|
---|
330 | if (getClosestTrigger(event.target)) {
|
---|
331 | ctrl.toggle();
|
---|
332 | }
|
---|
333 | }
|
---|
334 |
|
---|
335 | if (getClosestAction(event.target)) {
|
---|
336 | ctrl.close();
|
---|
337 | }
|
---|
338 | }
|
---|
339 |
|
---|
340 | function getTriggerElement() {
|
---|
341 | return $element.find('md-fab-trigger');
|
---|
342 | }
|
---|
343 |
|
---|
344 | function getActionsElement() {
|
---|
345 | return $element.find('md-fab-actions');
|
---|
346 | }
|
---|
347 | }
|
---|
348 | })();
|
---|
349 |
|
---|
350 | (function() {
|
---|
351 | 'use strict';
|
---|
352 |
|
---|
353 | /**
|
---|
354 | * The duration of the CSS animation in milliseconds.
|
---|
355 | *
|
---|
356 | * @type {number}
|
---|
357 | */
|
---|
358 | MdFabSpeedDialFlingAnimation['$inject'] = ["$timeout"];
|
---|
359 | MdFabSpeedDialScaleAnimation['$inject'] = ["$timeout"];
|
---|
360 | var cssAnimationDuration = 300;
|
---|
361 |
|
---|
362 | /**
|
---|
363 | * @ngdoc module
|
---|
364 | * @name material.components.fabSpeedDial
|
---|
365 | */
|
---|
366 | angular
|
---|
367 | // Declare our module
|
---|
368 | .module('material.components.fabSpeedDial', [
|
---|
369 | 'material.core',
|
---|
370 | 'material.components.fabShared',
|
---|
371 | 'material.components.fabActions'
|
---|
372 | ])
|
---|
373 |
|
---|
374 | // Register our directive
|
---|
375 | .directive('mdFabSpeedDial', MdFabSpeedDialDirective)
|
---|
376 |
|
---|
377 | // Register our custom animations
|
---|
378 | .animation('.md-fling', MdFabSpeedDialFlingAnimation)
|
---|
379 | .animation('.md-scale', MdFabSpeedDialScaleAnimation)
|
---|
380 |
|
---|
381 | // Register a service for each animation so that we can easily inject them into unit tests
|
---|
382 | .service('mdFabSpeedDialFlingAnimation', MdFabSpeedDialFlingAnimation)
|
---|
383 | .service('mdFabSpeedDialScaleAnimation', MdFabSpeedDialScaleAnimation);
|
---|
384 |
|
---|
385 | /**
|
---|
386 | * @ngdoc directive
|
---|
387 | * @name mdFabSpeedDial
|
---|
388 | * @module material.components.fabSpeedDial
|
---|
389 | *
|
---|
390 | * @restrict E
|
---|
391 | *
|
---|
392 | * @description
|
---|
393 | * The `<md-fab-speed-dial>` directive is used to present a series of popup elements (usually
|
---|
394 | * `<md-button>`s) for quick access to common actions.
|
---|
395 | *
|
---|
396 | * There are currently two animations available by applying one of the following classes to
|
---|
397 | * the component:
|
---|
398 | *
|
---|
399 | * - `md-fling` - The speed dial items appear from underneath the trigger and move into their
|
---|
400 | * appropriate positions.
|
---|
401 | * - `md-scale` - The speed dial items appear in their proper places by scaling from 0% to 100%.
|
---|
402 | *
|
---|
403 | * You may also easily position the trigger by applying one one of the following classes to the
|
---|
404 | * `<md-fab-speed-dial>` element:
|
---|
405 | * - `md-fab-top-left`
|
---|
406 | * - `md-fab-top-right`
|
---|
407 | * - `md-fab-bottom-left`
|
---|
408 | * - `md-fab-bottom-right`
|
---|
409 | *
|
---|
410 | * These CSS classes use `position: absolute`, so you need to ensure that the container element
|
---|
411 | * also uses `position: absolute` or `position: relative` in order for them to work.
|
---|
412 | *
|
---|
413 | * Additionally, you may use the standard `ng-mouseenter` and `ng-mouseleave` directives to
|
---|
414 | * open or close the speed dial. However, if you wish to allow users to hover over the empty
|
---|
415 | * space where the actions will appear, you must also add the `md-hover-full` class to the speed
|
---|
416 | * dial element. Without this, the hover effect will only occur on top of the trigger.
|
---|
417 | *
|
---|
418 | * See the demos for more information.
|
---|
419 | *
|
---|
420 | * ## Troubleshooting
|
---|
421 | *
|
---|
422 | * If your speed dial shows the closing animation upon launch, you may need to use `ng-cloak` on
|
---|
423 | * the parent container to ensure that it is only visible once ready. We have plans to remove this
|
---|
424 | * necessity in the future.
|
---|
425 | *
|
---|
426 | * @usage
|
---|
427 | * <hljs lang="html">
|
---|
428 | * <md-fab-speed-dial md-direction="up" class="md-fling">
|
---|
429 | * <md-fab-trigger>
|
---|
430 | * <md-button aria-label="Add..."><md-icon md-svg-src="/img/icons/plus.svg"></md-icon></md-button>
|
---|
431 | * </md-fab-trigger>
|
---|
432 | *
|
---|
433 | * <md-fab-actions>
|
---|
434 | * <md-button aria-label="Add User">
|
---|
435 | * <md-icon md-svg-src="/img/icons/user.svg"></md-icon>
|
---|
436 | * </md-button>
|
---|
437 | *
|
---|
438 | * <md-button aria-label="Add Group">
|
---|
439 | * <md-icon md-svg-src="/img/icons/group.svg"></md-icon>
|
---|
440 | * </md-button>
|
---|
441 | * </md-fab-actions>
|
---|
442 | * </md-fab-speed-dial>
|
---|
443 | * </hljs>
|
---|
444 | *
|
---|
445 | * @param {string} md-direction From which direction you would like the speed dial to appear
|
---|
446 | * relative to the trigger element.
|
---|
447 | * @param {expression=} md-open Programmatically control whether or not the speed-dial is visible.
|
---|
448 | */
|
---|
449 | function MdFabSpeedDialDirective() {
|
---|
450 | return {
|
---|
451 | restrict: 'E',
|
---|
452 |
|
---|
453 | scope: {
|
---|
454 | direction: '@?mdDirection',
|
---|
455 | isOpen: '=?mdOpen'
|
---|
456 | },
|
---|
457 |
|
---|
458 | bindToController: true,
|
---|
459 | controller: 'MdFabController',
|
---|
460 | controllerAs: 'ctrl',
|
---|
461 |
|
---|
462 | link: FabSpeedDialLink
|
---|
463 | };
|
---|
464 |
|
---|
465 | function FabSpeedDialLink(scope, element) {
|
---|
466 | // Prepend an element to hold our CSS variables so we can use them in the animations below
|
---|
467 | element.prepend('<div class="_md-css-variables"></div>');
|
---|
468 | }
|
---|
469 | }
|
---|
470 |
|
---|
471 | function MdFabSpeedDialFlingAnimation($timeout) {
|
---|
472 | function delayDone(done) { $timeout(done, cssAnimationDuration, false); }
|
---|
473 |
|
---|
474 | function runAnimation(element) {
|
---|
475 | // Don't run if we are still waiting and we are not ready
|
---|
476 | if (element.hasClass('md-animations-waiting') && !element.hasClass('_md-animations-ready')) {
|
---|
477 | return;
|
---|
478 | }
|
---|
479 |
|
---|
480 | var el = element[0];
|
---|
481 | var ctrl = element.controller('mdFabSpeedDial');
|
---|
482 | var items = el.querySelectorAll('.md-fab-action-item');
|
---|
483 |
|
---|
484 | // Grab our trigger element
|
---|
485 | var triggerElement = el.querySelector('md-fab-trigger');
|
---|
486 |
|
---|
487 | // Grab our element which stores CSS variables
|
---|
488 | var variablesElement = el.querySelector('._md-css-variables');
|
---|
489 |
|
---|
490 | // Setup JS variables based on our CSS variables
|
---|
491 | var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex);
|
---|
492 |
|
---|
493 | // Always reset the items to their natural position/state
|
---|
494 | angular.forEach(items, function(item, index) {
|
---|
495 | var styles = item.style;
|
---|
496 |
|
---|
497 | styles.transform = styles.webkitTransform = '';
|
---|
498 | styles.transitionDelay = '';
|
---|
499 | styles.opacity = ctrl.isOpen ? 1 : 0;
|
---|
500 |
|
---|
501 | // Make the items closest to the trigger have the highest z-index
|
---|
502 | styles.zIndex = (items.length - index) + startZIndex;
|
---|
503 | });
|
---|
504 |
|
---|
505 | // Set the trigger to be above all of the actions so they disappear behind it.
|
---|
506 | triggerElement.style.zIndex = startZIndex + items.length + 1;
|
---|
507 |
|
---|
508 | // If the control is closed, hide the items behind the trigger
|
---|
509 | if (!ctrl.isOpen) {
|
---|
510 | angular.forEach(items, function(item, index) {
|
---|
511 | var newPosition, axis;
|
---|
512 | var styles = item.style;
|
---|
513 |
|
---|
514 | // Make sure to account for differences in the dimensions of the trigger verses the items
|
---|
515 | // so that we can properly center everything; this helps hide the item's shadows behind
|
---|
516 | // the trigger.
|
---|
517 | var triggerItemHeightOffset = (triggerElement.clientHeight - item.clientHeight) / 2;
|
---|
518 | var triggerItemWidthOffset = (triggerElement.clientWidth - item.clientWidth) / 2;
|
---|
519 |
|
---|
520 | switch (ctrl.direction) {
|
---|
521 | case 'up':
|
---|
522 | newPosition = (item.scrollHeight * (index + 1) + triggerItemHeightOffset);
|
---|
523 | axis = 'Y';
|
---|
524 | break;
|
---|
525 | case 'down':
|
---|
526 | newPosition = -(item.scrollHeight * (index + 1) + triggerItemHeightOffset);
|
---|
527 | axis = 'Y';
|
---|
528 | break;
|
---|
529 | case 'left':
|
---|
530 | newPosition = (item.scrollWidth * (index + 1) + triggerItemWidthOffset);
|
---|
531 | axis = 'X';
|
---|
532 | break;
|
---|
533 | case 'right':
|
---|
534 | newPosition = -(item.scrollWidth * (index + 1) + triggerItemWidthOffset);
|
---|
535 | axis = 'X';
|
---|
536 | break;
|
---|
537 | }
|
---|
538 |
|
---|
539 | var newTranslate = 'translate' + axis + '(' + newPosition + 'px)';
|
---|
540 |
|
---|
541 | styles.transform = styles.webkitTransform = newTranslate;
|
---|
542 | });
|
---|
543 | }
|
---|
544 | }
|
---|
545 |
|
---|
546 | return {
|
---|
547 | addClass: function(element, className, done) {
|
---|
548 | if (element.hasClass('md-fling')) {
|
---|
549 | runAnimation(element);
|
---|
550 | delayDone(done);
|
---|
551 | } else {
|
---|
552 | done();
|
---|
553 | }
|
---|
554 | },
|
---|
555 | removeClass: function(element, className, done) {
|
---|
556 | runAnimation(element);
|
---|
557 | delayDone(done);
|
---|
558 | }
|
---|
559 | };
|
---|
560 | }
|
---|
561 |
|
---|
562 | function MdFabSpeedDialScaleAnimation($timeout) {
|
---|
563 | function delayDone(done) { $timeout(done, cssAnimationDuration, false); }
|
---|
564 |
|
---|
565 | var delay = 65;
|
---|
566 |
|
---|
567 | function runAnimation(element) {
|
---|
568 | var el = element[0];
|
---|
569 | var ctrl = element.controller('mdFabSpeedDial');
|
---|
570 | var items = el.querySelectorAll('.md-fab-action-item');
|
---|
571 |
|
---|
572 | // Grab our element which stores CSS variables
|
---|
573 | var variablesElement = el.querySelector('._md-css-variables');
|
---|
574 |
|
---|
575 | // Setup JS variables based on our CSS variables
|
---|
576 | var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex);
|
---|
577 |
|
---|
578 | // Always reset the items to their natural position/state
|
---|
579 | angular.forEach(items, function(item, index) {
|
---|
580 | var styles = item.style,
|
---|
581 | offsetDelay = index * delay;
|
---|
582 |
|
---|
583 | styles.opacity = ctrl.isOpen ? 1 : 0;
|
---|
584 | styles.transform = styles.webkitTransform = ctrl.isOpen ? 'scale(1)' : 'scale(0)';
|
---|
585 | styles.transitionDelay = (ctrl.isOpen ? offsetDelay : (items.length - offsetDelay)) + 'ms';
|
---|
586 |
|
---|
587 | // Make the items closest to the trigger have the highest z-index
|
---|
588 | styles.zIndex = (items.length - index) + startZIndex;
|
---|
589 | });
|
---|
590 | }
|
---|
591 |
|
---|
592 | return {
|
---|
593 | addClass: function(element, className, done) {
|
---|
594 | runAnimation(element);
|
---|
595 | delayDone(done);
|
---|
596 | },
|
---|
597 |
|
---|
598 | removeClass: function(element, className, done) {
|
---|
599 | runAnimation(element);
|
---|
600 | delayDone(done);
|
---|
601 | }
|
---|
602 | };
|
---|
603 | }
|
---|
604 | })();
|
---|
605 |
|
---|
606 | })(window, window.angular); |
---|