toolbar.js 6.9 KB

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