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.

269 lines
8.2 KiB

7 years ago
  1. /*!
  2. * Angular Material Design
  3. * https://github.com/angular/material
  4. * @license MIT
  5. * v1.1.3
  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. * <h2 md-truncate flex>My App's Title</h2>
  40. *
  41. * <md-button>
  42. * Right Bar Button
  43. * </md-button>
  44. * </div>
  45. *
  46. * </md-toolbar>
  47. * <md-content>
  48. * Hello!
  49. * </md-content>
  50. * </div>
  51. * </hljs>
  52. *
  53. * <i><b>Note:</b> The code above shows usage with the `md-truncate` component which provides an
  54. * ellipsis if the title is longer than the width of the Toolbar.</i>
  55. *
  56. * ## CSS & Styles
  57. *
  58. * The `<md-toolbar>` provides a few custom CSS classes that you may use to enhance the
  59. * functionality of your toolbar.
  60. *
  61. * <div>
  62. * <docs-css-api-table>
  63. *
  64. * <docs-css-selector code="md-toolbar .md-toolbar-tools">
  65. * The `md-toolbar-tools` class provides quite a bit of automatic styling for your toolbar
  66. * buttons and text. When applied, it will center the buttons and text vertically for you.
  67. * </docs-css-selector>
  68. *
  69. * </docs-css-api-table>
  70. * </div>
  71. *
  72. * ### Private Classes
  73. *
  74. * Currently, the only private class is the `md-toolbar-transitions` class. All other classes are
  75. * considered public.
  76. *
  77. * @param {boolean=} md-scroll-shrink Whether the header should shrink away as
  78. * the user scrolls down, and reveal itself as the user scrolls up.
  79. *
  80. * _**Note (1):** for scrollShrink to work, the toolbar must be a sibling of a
  81. * `md-content` element, placed before it. See the scroll shrink demo._
  82. *
  83. * _**Note (2):** The `md-scroll-shrink` attribute is only parsed on component
  84. * initialization, it does not watch for scope changes._
  85. *
  86. *
  87. * @param {number=} md-shrink-speed-factor How much to change the speed of the toolbar's
  88. * shrinking by. For example, if 0.25 is given then the toolbar will shrink
  89. * at one fourth the rate at which the user scrolls down. Default 0.5.
  90. *
  91. */
  92. function mdToolbarDirective($$rAF, $mdConstant, $mdUtil, $mdTheming, $animate) {
  93. var translateY = angular.bind(null, $mdUtil.supplant, 'translate3d(0,{0}px,0)');
  94. return {
  95. template: '',
  96. restrict: 'E',
  97. link: function(scope, element, attr) {
  98. element.addClass('_md'); // private md component indicator for styling
  99. $mdTheming(element);
  100. $mdUtil.nextTick(function () {
  101. element.addClass('_md-toolbar-transitions'); // adding toolbar transitions after digest
  102. }, false);
  103. if (angular.isDefined(attr.mdScrollShrink)) {
  104. setupScrollShrink();
  105. }
  106. function setupScrollShrink() {
  107. var toolbarHeight;
  108. var contentElement;
  109. var disableScrollShrink = angular.noop;
  110. // Current "y" position of scroll
  111. // Store the last scroll top position
  112. var y = 0;
  113. var prevScrollTop = 0;
  114. var shrinkSpeedFactor = attr.mdShrinkSpeedFactor || 0.5;
  115. var debouncedContentScroll = $$rAF.throttle(onContentScroll);
  116. var debouncedUpdateHeight = $mdUtil.debounce(updateToolbarHeight, 5 * 1000);
  117. // Wait for $mdContentLoaded event from mdContent directive.
  118. // If the mdContent element is a sibling of our toolbar, hook it up
  119. // to scroll events.
  120. scope.$on('$mdContentLoaded', onMdContentLoad);
  121. // If the toolbar is used inside an ng-if statement, we may miss the
  122. // $mdContentLoaded event, so we attempt to fake it if we have a
  123. // md-content close enough.
  124. attr.$observe('mdScrollShrink', onChangeScrollShrink);
  125. // If the toolbar has ngShow or ngHide we need to update height immediately as it changed
  126. // and not wait for $mdUtil.debounce to happen
  127. if (attr.ngShow) { scope.$watch(attr.ngShow, updateToolbarHeight); }
  128. if (attr.ngHide) { scope.$watch(attr.ngHide, updateToolbarHeight); }
  129. // If the scope is destroyed (which could happen with ng-if), make sure
  130. // to disable scroll shrinking again
  131. scope.$on('$destroy', disableScrollShrink);
  132. /**
  133. *
  134. */
  135. function onChangeScrollShrink(shrinkWithScroll) {
  136. var closestContent = element.parent().find('md-content');
  137. // If we have a content element, fake the call; this might still fail
  138. // if the content element isn't a sibling of the toolbar
  139. if (!contentElement && closestContent.length) {
  140. onMdContentLoad(null, closestContent);
  141. }
  142. // Evaluate the expression
  143. shrinkWithScroll = scope.$eval(shrinkWithScroll);
  144. // Disable only if the attribute's expression evaluates to false
  145. if (shrinkWithScroll === false) {
  146. disableScrollShrink();
  147. } else {
  148. disableScrollShrink = enableScrollShrink();
  149. }
  150. }
  151. /**
  152. *
  153. */
  154. function onMdContentLoad($event, newContentEl) {
  155. // Toolbar and content must be siblings
  156. if (newContentEl && element.parent()[0] === newContentEl.parent()[0]) {
  157. // unhook old content event listener if exists
  158. if (contentElement) {
  159. contentElement.off('scroll', debouncedContentScroll);
  160. }
  161. contentElement = newContentEl;
  162. disableScrollShrink = enableScrollShrink();
  163. }
  164. }
  165. /**
  166. *
  167. */
  168. function onContentScroll(e) {
  169. var scrollTop = e ? e.target.scrollTop : prevScrollTop;
  170. debouncedUpdateHeight();
  171. y = Math.min(
  172. toolbarHeight / shrinkSpeedFactor,
  173. Math.max(0, y + scrollTop - prevScrollTop)
  174. );
  175. element.css($mdConstant.CSS.TRANSFORM, translateY([-y * shrinkSpeedFactor]));
  176. contentElement.css($mdConstant.CSS.TRANSFORM, translateY([(toolbarHeight - y) * shrinkSpeedFactor]));
  177. prevScrollTop = scrollTop;
  178. $mdUtil.nextTick(function() {
  179. var hasWhiteFrame = element.hasClass('md-whiteframe-z1');
  180. if (hasWhiteFrame && !y) {
  181. $animate.removeClass(element, 'md-whiteframe-z1');
  182. } else if (!hasWhiteFrame && y) {
  183. $animate.addClass(element, 'md-whiteframe-z1');
  184. }
  185. });
  186. }
  187. /**
  188. *
  189. */
  190. function enableScrollShrink() {
  191. if (!contentElement) return angular.noop; // no md-content
  192. contentElement.on('scroll', debouncedContentScroll);
  193. contentElement.attr('scroll-shrink', 'true');
  194. $mdUtil.nextTick(updateToolbarHeight, false);
  195. return function disableScrollShrink() {
  196. contentElement.off('scroll', debouncedContentScroll);
  197. contentElement.attr('scroll-shrink', 'false');
  198. updateToolbarHeight();
  199. };
  200. }
  201. /**
  202. *
  203. */
  204. function updateToolbarHeight() {
  205. toolbarHeight = element.prop('offsetHeight');
  206. // Add a negative margin-top the size of the toolbar to the content el.
  207. // The content will start transformed down the toolbarHeight amount,
  208. // so everything looks normal.
  209. //
  210. // As the user scrolls down, the content will be transformed up slowly
  211. // to put the content underneath where the toolbar was.
  212. var margin = (-toolbarHeight * shrinkSpeedFactor) + 'px';
  213. contentElement.css({
  214. "margin-top": margin,
  215. "margin-bottom": margin
  216. });
  217. onContentScroll();
  218. }
  219. }
  220. }
  221. };
  222. }
  223. })(window, window.angular);