toolbar.js 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*!
  2. * Angular Material Design
  3. * https://github.com/angular/material
  4. * @license MIT
  5. * v0.11.4
  6. */
  7. goog.provide('ng.material.components.toolbar');
  8. goog.require('ng.material.components.content');
  9. goog.require('ng.material.core');
  10. /**
  11. * @ngdoc module
  12. * @name material.components.toolbar
  13. */
  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. restrict: 'E',
  74. link: function(scope, element, attr) {
  75. $mdTheming(element);
  76. if (angular.isDefined(attr.mdScrollShrink)) {
  77. setupScrollShrink();
  78. }
  79. function setupScrollShrink() {
  80. var toolbarHeight;
  81. var contentElement;
  82. var disableScrollShrink = angular.noop;
  83. // Current "y" position of scroll
  84. // Store the last scroll top position
  85. var y = 0;
  86. var prevScrollTop = 0;
  87. var shrinkSpeedFactor = attr.mdShrinkSpeedFactor || 0.5;
  88. var debouncedContentScroll = $$rAF.throttle(onContentScroll);
  89. var debouncedUpdateHeight = $mdUtil.debounce(updateToolbarHeight, 5 * 1000);
  90. // Wait for $mdContentLoaded event from mdContent directive.
  91. // If the mdContent element is a sibling of our toolbar, hook it up
  92. // to scroll events.
  93. scope.$on('$mdContentLoaded', onMdContentLoad);
  94. // If the toolbar is used inside an ng-if statement, we may miss the
  95. // $mdContentLoaded event, so we attempt to fake it if we have a
  96. // md-content close enough.
  97. attr.$observe('mdScrollShrink', onChangeScrollShrink);
  98. // If the scope is destroyed (which could happen with ng-if), make sure
  99. // to disable scroll shrinking again
  100. scope.$on('$destroy', disableScrollShrink);
  101. /**
  102. *
  103. */
  104. function onChangeScrollShrink(shrinkWithScroll) {
  105. var closestContent = element.parent().find('md-content');
  106. // If we have a content element, fake the call; this might still fail
  107. // if the content element isn't a sibling of the toolbar
  108. if (!contentElement && closestContent.length) {
  109. onMdContentLoad(null, closestContent);
  110. }
  111. // Evaluate the expression
  112. shrinkWithScroll = scope.$eval(shrinkWithScroll);
  113. // Disable only if the attribute's expression evaluates to false
  114. if (shrinkWithScroll === false) {
  115. disableScrollShrink();
  116. } else {
  117. disableScrollShrink = enableScrollShrink();
  118. }
  119. }
  120. /**
  121. *
  122. */
  123. function onMdContentLoad($event, newContentEl) {
  124. // Toolbar and content must be siblings
  125. if (newContentEl && element.parent()[0] === newContentEl.parent()[0]) {
  126. // unhook old content event listener if exists
  127. if (contentElement) {
  128. contentElement.off('scroll', debouncedContentScroll);
  129. }
  130. contentElement = newContentEl;
  131. disableScrollShrink = enableScrollShrink();
  132. }
  133. }
  134. /**
  135. *
  136. */
  137. function onContentScroll(e) {
  138. var scrollTop = e ? e.target.scrollTop : prevScrollTop;
  139. debouncedUpdateHeight();
  140. y = Math.min(
  141. toolbarHeight / shrinkSpeedFactor,
  142. Math.max(0, y + scrollTop - prevScrollTop)
  143. );
  144. element.css($mdConstant.CSS.TRANSFORM, translateY([-y * shrinkSpeedFactor]));
  145. contentElement.css($mdConstant.CSS.TRANSFORM, translateY([(toolbarHeight - y) * shrinkSpeedFactor]));
  146. prevScrollTop = scrollTop;
  147. $mdUtil.nextTick(function() {
  148. var hasWhiteFrame = element.hasClass('md-whiteframe-z1');
  149. if (hasWhiteFrame && !y) {
  150. $animate.removeClass(element, 'md-whiteframe-z1');
  151. } else if (!hasWhiteFrame && y) {
  152. $animate.addClass(element, 'md-whiteframe-z1');
  153. }
  154. });
  155. }
  156. /**
  157. *
  158. */
  159. function enableScrollShrink() {
  160. if (!contentElement) return angular.noop; // no md-content
  161. contentElement.on('scroll', debouncedContentScroll);
  162. contentElement.attr('scroll-shrink', 'true');
  163. $$rAF(updateToolbarHeight);
  164. return function disableScrollShrink() {
  165. contentElement.off('scroll', debouncedContentScroll);
  166. contentElement.attr('scroll-shrink', 'false');
  167. $$rAF(updateToolbarHeight);
  168. }
  169. }
  170. /**
  171. *
  172. */
  173. function updateToolbarHeight() {
  174. toolbarHeight = element.prop('offsetHeight');
  175. // Add a negative margin-top the size of the toolbar to the content el.
  176. // The content will start transformed down the toolbarHeight amount,
  177. // so everything looks normal.
  178. //
  179. // As the user scrolls down, the content will be transformed up slowly
  180. // to put the content underneath where the toolbar was.
  181. var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
  182. contentElement.css({
  183. "margin-top": margin,
  184. "margin-bottom": margin
  185. });
  186. onContentScroll();
  187. }
  188. }
  189. }
  190. };
  191. }
  192. mdToolbarDirective.$inject = ["$$rAF", "$mdConstant", "$mdUtil", "$mdTheming", "$animate"];
  193. ng.material.components.toolbar = angular.module("material.components.toolbar");