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.6 KiB

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