You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

247 lines
7.5 KiB

  1. /*!
  2. * Angular Material Design
  3. * https://github.com/angular/material
  4. * @license MIT
  5. * v1.1.1
  6. */
  7. (function( window, angular, undefined ){
  8. "use strict";
  9. /**
  10. * @ngdoc module
  11. * @name material.components.toolbar
  12. */
  13. mdToolbarDirective.$inject = ["$$rAF", "$mdConstant", "$mdUtil", "$mdTheming", "$animate"];
  14. angular.module('material.components.toolbar', [
  15. 'material.core',
  16. 'material.components.content'
  17. ])
  18. .directive('mdToolbar', mdToolbarDirective);
  19. /**
  20. * @ngdoc directive
  21. * @name mdToolbar
  22. * @module material.components.toolbar
  23. * @restrict E
  24. * @description
  25. * `md-toolbar` is used to place a toolbar in your app.
  26. *
  27. * Toolbars are usually used above a content area to display the title of the
  28. * current page, and show relevant action buttons for that page.
  29. *
  30. * You can change the height of the toolbar by adding either the
  31. * `md-medium-tall` or `md-tall` class to the toolbar.
  32. *
  33. * @usage
  34. * <hljs lang="html">
  35. * <div layout="column" layout-fill>
  36. * <md-toolbar>
  37. *
  38. * <div class="md-toolbar-tools">
  39. * <span>My App's Title</span>
  40. *
  41. * <!-- fill up the space between left and right area -->
  42. * <span flex></span>
  43. *
  44. * <md-button>
  45. * Right Bar Button
  46. * </md-button>
  47. * </div>
  48. *
  49. * </md-toolbar>
  50. * <md-content>
  51. * Hello!
  52. * </md-content>
  53. * </div>
  54. * </hljs>
  55. *
  56. * @param {boolean=} md-scroll-shrink Whether the header should shrink away as
  57. * the user scrolls down, and reveal itself as the user scrolls up.
  58. *
  59. * _**Note (1):** for scrollShrink to work, the toolbar must be a sibling of a
  60. * `md-content` element, placed before it. See the scroll shrink demo._
  61. *
  62. * _**Note (2):** The `md-scroll-shrink` attribute is only parsed on component
  63. * initialization, it does not watch for scope changes._
  64. *
  65. *
  66. * @param {number=} md-shrink-speed-factor How much to change the speed of the toolbar's
  67. * shrinking by. For example, if 0.25 is given then the toolbar will shrink
  68. * at one fourth the rate at which the user scrolls down. Default 0.5.
  69. */
  70. function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
  71. var translateY = angular.bind(null, $mdUtil.supplant, 'translate3d(0,{0}px,0)');
  72. return {
  73. template: '',
  74. restrict: 'E',
  75. link: function(scope, element, attr) {
  76. element.addClass('_md'); // private md component indicator for styling
  77. $mdTheming(element);
  78. $mdUtil.nextTick(function () {
  79. element.addClass('_md-toolbar-transitions'); // adding toolbar transitions after digest
  80. }, false);
  81. if (angular.isDefined(attr.mdScrollShrink)) {
  82. setupScrollShrink();
  83. }
  84. function setupScrollShrink() {
  85. var toolbarHeight;
  86. var contentElement;
  87. var disableScrollShrink = angular.noop;
  88. // Current "y" position of scroll
  89. // Store the last scroll top position
  90. var y = 0;
  91. var prevScrollTop = 0;
  92. var shrinkSpeedFactor = attr.mdShrinkSpeedFactor || 0.5;
  93. var debouncedContentScroll = $$rAF.throttle(onContentScroll);
  94. var debouncedUpdateHeight = $mdUtil.debounce(updateToolbarHeight, 5 * 1000);
  95. // Wait for $mdContentLoaded event from mdContent directive.
  96. // If the mdContent element is a sibling of our toolbar, hook it up
  97. // to scroll events.
  98. scope.$on('$mdContentLoaded', onMdContentLoad);
  99. // If the toolbar is used inside an ng-if statement, we may miss the
  100. // $mdContentLoaded event, so we attempt to fake it if we have a
  101. // md-content close enough.
  102. attr.$observe('mdScrollShrink', onChangeScrollShrink);
  103. // If the toolbar has ngShow or ngHide we need to update height immediately as it changed
  104. // and not wait for $mdUtil.debounce to happen
  105. if (attr.ngShow) { scope.$watch(attr.ngShow, updateToolbarHeight); }
  106. if (attr.ngHide) { scope.$watch(attr.ngHide, updateToolbarHeight); }
  107. // If the scope is destroyed (which could happen with ng-if), make sure
  108. // to disable scroll shrinking again
  109. scope.$on('$destroy', disableScrollShrink);
  110. /**
  111. *
  112. */
  113. function onChangeScrollShrink(shrinkWithScroll) {
  114. var closestContent = element.parent().find('md-content');
  115. // If we have a content element, fake the call; this might still fail
  116. // if the content element isn't a sibling of the toolbar
  117. if (!contentElement && closestContent.length) {
  118. onMdContentLoad(null, closestContent);
  119. }
  120. // Evaluate the expression
  121. shrinkWithScroll = scope.$eval(shrinkWithScroll);
  122. // Disable only if the attribute's expression evaluates to false
  123. if (shrinkWithScroll === false) {
  124. disableScrollShrink();
  125. } else {
  126. disableScrollShrink = enableScrollShrink();
  127. }
  128. }
  129. /**
  130. *
  131. */
  132. function onMdContentLoad($event, newContentEl) {
  133. // Toolbar and content must be siblings
  134. if (newContentEl && element.parent()[0] === newContentEl.parent()[0]) {
  135. // unhook old content event listener if exists
  136. if (contentElement) {
  137. contentElement.off('scroll', debouncedContentScroll);
  138. }
  139. contentElement = newContentEl;
  140. disableScrollShrink = enableScrollShrink();
  141. }
  142. }
  143. /**
  144. *
  145. */
  146. function onContentScroll(e) {
  147. var scrollTop = e ? e.target.scrollTop : prevScrollTop;
  148. debouncedUpdateHeight();
  149. y = Math.min(
  150. toolbarHeight / shrinkSpeedFactor,
  151. Math.max(0, y + scrollTop - prevScrollTop)
  152. );
  153. element.css($mdConstant.CSS.TRANSFORM, translateY([-y * shrinkSpeedFactor]));
  154. contentElement.css($mdConstant.CSS.TRANSFORM, translateY([(toolbarHeight - y) * shrinkSpeedFactor]));
  155. prevScrollTop = scrollTop;
  156. $mdUtil.nextTick(function() {
  157. var hasWhiteFrame = element.hasClass('md-whiteframe-z1');
  158. if (hasWhiteFrame && !y) {
  159. $animate.removeClass(element, 'md-whiteframe-z1');
  160. } else if (!hasWhiteFrame && y) {
  161. $animate.addClass(element, 'md-whiteframe-z1');
  162. }
  163. });
  164. }
  165. /**
  166. *
  167. */
  168. function enableScrollShrink() {
  169. if (!contentElement) return angular.noop; // no md-content
  170. contentElement.on('scroll', debouncedContentScroll);
  171. contentElement.attr('scroll-shrink', 'true');
  172. $mdUtil.nextTick(updateToolbarHeight, false);
  173. return function disableScrollShrink() {
  174. contentElement.off('scroll', debouncedContentScroll);
  175. contentElement.attr('scroll-shrink', 'false');
  176. updateToolbarHeight();
  177. };
  178. }
  179. /**
  180. *
  181. */
  182. function updateToolbarHeight() {
  183. toolbarHeight = element.prop('offsetHeight');
  184. // Add a negative margin-top the size of the toolbar to the content el.
  185. // The content will start transformed down the toolbarHeight amount,
  186. // so everything looks normal.
  187. //
  188. // As the user scrolls down, the content will be transformed up slowly
  189. // to put the content underneath where the toolbar was.
  190. var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
  191. contentElement.css({
  192. "margin-top": margin,
  193. "margin-bottom": margin
  194. });
  195. onContentScroll();
  196. }
  197. }
  198. }
  199. };
  200. }
  201. })(window, window.angular);