Index: third_party/polymer/components/core-scroll-header-panel/core-scroll-header-panel.html |
diff --git a/third_party/polymer/components/core-scroll-header-panel/core-scroll-header-panel.html b/third_party/polymer/components/core-scroll-header-panel/core-scroll-header-panel.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ae0f9e1eeb55b7eda27842e734f71970941b854d |
--- /dev/null |
+++ b/third_party/polymer/components/core-scroll-header-panel/core-scroll-header-panel.html |
@@ -0,0 +1,302 @@ |
+<!-- |
+Copyright (c) 2014 The Polymer Project Authors. All rights reserved. |
+This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt |
+The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt |
+The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt |
+Code distributed by Google as part of the polymer project is also |
+subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt |
+--> |
+ |
+<!-- |
+`core-scroll-header-panel` contains a header section and a content section. The |
+header is initially on the top part of the view but it scrolls away with the |
+rest of the scrollable content. Upon scrolling slightly up at any point, the |
+header scrolls back into view. This saves screen space and allows users to |
+access important controls by easily moving them back to the view. |
+ |
+__Important:__ The `core-scroll-header-panel` will not display if its parent does not have a height. |
+ |
+Using [layout attributes](http://www.polymer-project.org/docs/polymer/layout-attrs.html), you can easily make the `core-scroll-header-panel` fill the screen |
+ |
+ <body fullbleed layout vertical> |
+ <core-scroll-header-panel flex> |
+ <core-toolbar> |
+ <div>Hello World!</div> |
+ </core-toolbar> |
+ </core-scroll-header-panel> |
+ </body> |
+ |
+or, if you would prefer to do it in CSS, just give `html`, `body`, and `core-scroll-header-panel` a height of 100%: |
+ |
+ html, body { |
+ height: 100%; |
+ margin: 0; |
+ } |
+ core-scroll-header-panel { |
+ height: 100%; |
+ } |
+ |
+`core-scroll-header-panel` works well with `core-toolbar` but can use any element |
+that represents a header by adding a `core-header` class to it. Use the attribute |
+or class `content` to delineate the content section. |
+ |
+ <core-scroll-header-panel> |
+ <core-toolbar>Header</core-toolbar> |
+ <div content>Content goes here...</div> |
+ </core-scroll-header-panel> |
+ |
+@group Polymer Core Elements |
+@element core-scroll-header-panel |
+@homepage github.io |
+--> |
+ |
+<link rel="import" href="../polymer/polymer.html"> |
+ |
+<polymer-element name="core-scroll-header-panel"> |
+<template> |
+ |
+ <link rel="stylesheet" href="core-scroll-header-panel.css"> |
+ |
+ <div id="mainContainer" on-scroll="{{scroll}}"> |
+ |
+ <content id="mainContent" select="[content], .content"></content> |
+ |
+ </div> |
+ |
+ <div id="headerContainer"> |
+ |
+ <div class="bg-container"> |
+ <div id="condensedHeaderBg"></div> |
+ <div id="headerBg"></div> |
+ </div> |
+ |
+ <content id="headerContent" select="core-toolbar, .core-header"></content> |
+ |
+ </div> |
+ |
+</template> |
+<script> |
+ |
+ Polymer('core-scroll-header-panel', { |
+ |
+ /** |
+ * Fired when the content has been scrolled. |
+ * |
+ * @event scroll |
+ */ |
+ |
+ /** |
+ * Fired when the header is transformed. |
+ * |
+ * @event core-header-transform |
+ */ |
+ |
+ publish: { |
+ /** |
+ * If true, the header's height will condense to `_condensedHeaderHeight` |
+ * as the user scrolls down from the top of the content area. |
+ * |
+ * @attribute condenses |
+ * @type boolean |
+ * @default false |
+ */ |
+ condenses: false, |
+ |
+ /** |
+ * If true, no cross-fade transition from one background to another. |
+ * |
+ * @attribute noDissolve |
+ * @type boolean |
+ * @default false |
+ */ |
+ noDissolve: false, |
+ |
+ /** |
+ * If true, the header doesn't slide back in when scrolling back up. |
+ * |
+ * @attribute noReveal |
+ * @type boolean |
+ * @default false |
+ */ |
+ noReveal: false, |
+ |
+ /** |
+ * If true, the header is fixed to the top and never moves away. |
+ * |
+ * @attribute fixed |
+ * @type boolean |
+ * @default false |
+ */ |
+ fixed: false, |
+ |
+ /** |
+ * If true, the condensed header is always shown and does not move away. |
+ * |
+ * @attribute keepCondensedHeader |
+ * @type boolean |
+ * @default false |
+ */ |
+ keepCondensedHeader: false, |
+ |
+ /** |
+ * The height of the header when it is at its full size. |
+ * |
+ * By default, the height will be measured when it is ready. If the height |
+ * changes later the user needs to either set this value to reflect the |
+ * new height or invoke `measureHeaderHeight()`. |
+ * |
+ * @attribute headerHeight |
+ * @type number |
+ */ |
+ headerHeight: 0, |
+ |
+ /** |
+ * The height of the header when it is condensed. |
+ * |
+ * By default, `_condensedHeaderHeight` is 1/3 of `headerHeight` unless |
+ * this is specified. |
+ * |
+ * @attribute condensedHeaderHeight |
+ * @type number |
+ */ |
+ condensedHeaderHeight: 0 |
+ }, |
+ |
+ prevScrollTop: 0, |
+ |
+ headerMargin: 0, |
+ |
+ y: 0, |
+ |
+ observe: { |
+ 'headerMargin fixed': 'setup' |
+ }, |
+ |
+ domReady: function() { |
+ this.async('measureHeaderHeight'); |
+ }, |
+ |
+ get header() { |
+ return this.$.headerContent.getDistributedNodes()[0]; |
+ }, |
+ |
+ get scroller() { |
+ return this.$.mainContainer; |
+ }, |
+ |
+ measureHeaderHeight: function() { |
+ var header = this.header; |
+ if (this.header) { |
+ this.headerHeight = header.offsetHeight; |
+ } |
+ }, |
+ |
+ headerHeightChanged: function() { |
+ if (!this.condensedHeaderHeight) { |
+ // assume _condensedHeaderHeight is 1/3 of the headerHeight |
+ this._condensedHeaderHeight = this.headerHeight * 1 / 3; |
+ } |
+ this.condensedHeaderHeightChanged(); |
+ }, |
+ |
+ condensedHeaderHeightChanged: function() { |
+ if (this.condensedHeaderHeight) { |
+ this._condensedHeaderHeight = this.condensedHeaderHeight; |
+ } |
+ if (this.headerHeight) { |
+ this.headerMargin = this.headerHeight - this._condensedHeaderHeight; |
+ } |
+ }, |
+ |
+ condensesChanged: function() { |
+ if (this.condenses) { |
+ this.scroll(); |
+ } else { |
+ // reset transform/opacity set on the header |
+ this.condenseHeader(null); |
+ } |
+ }, |
+ |
+ setup: function() { |
+ var s = this.scroller.style; |
+ s.paddingTop = this.fixed ? '' : this.headerHeight + 'px'; |
+ s.top = this.fixed ? this.headerHeight + 'px' : ''; |
+ if (this.fixed) { |
+ this.transformHeader(null); |
+ } else { |
+ this.scroll(); |
+ } |
+ }, |
+ |
+ transformHeader: function(y) { |
+ var s = this.$.headerContainer.style; |
+ this.translateY(s, -y); |
+ |
+ if (this.condenses) { |
+ this.condenseHeader(y); |
+ } |
+ |
+ this.fire('core-header-transform', {y: y, height: this.headerHeight, |
+ condensedHeight: this._condensedHeaderHeight}); |
+ }, |
+ |
+ condenseHeader: function(y) { |
+ var reset = y == null; |
+ // adjust top bar in core-header so the top bar stays at the top |
+ if (this.header.$ && this.header.$.topBar) { |
+ this.translateY(this.header.$.topBar.style, |
+ reset ? null : Math.min(y, this.headerMargin)); |
+ } |
+ // transition header bg |
+ var hbg = this.$.headerBg.style; |
+ if (!this.noDissolve) { |
+ hbg.opacity = reset ? '' : (this.headerMargin - y) / this.headerMargin; |
+ } |
+ // adjust header bg so it stays at the center |
+ this.translateY(hbg, reset ? null : y / 2); |
+ // transition condensed header bg |
+ var chbg = this.$.condensedHeaderBg.style; |
+ if (!this.noDissolve) { |
+ chbg = this.$.condensedHeaderBg.style; |
+ chbg.opacity = reset ? '' : y / this.headerMargin; |
+ // adjust condensed header bg so it stays at the center |
+ this.translateY(chbg, reset ? null : y / 2); |
+ } |
+ }, |
+ |
+ translateY: function(s, y) { |
+ s.transform = s.webkitTransform = y == null ? '' : |
+ 'translate3d(0, ' + y + 'px, 0)'; |
+ }, |
+ |
+ scroll: function(event) { |
+ if (!this.header) { |
+ return; |
+ } |
+ |
+ var sTop = this.scroller.scrollTop; |
+ |
+ var y = Math.min(this.keepCondensedHeader ? |
+ this.headerMargin : this.headerHeight, Math.max(0, |
+ (this.noReveal ? sTop : this.y + sTop - this.prevScrollTop))); |
+ |
+ if (this.condenses && this.prevScrollTop >= sTop && sTop > this.headerMargin) { |
+ y = Math.max(y, this.headerMargin); |
+ } |
+ |
+ if (!event || !this.fixed && y !== this.y) { |
+ requestAnimationFrame(this.transformHeader.bind(this, y)); |
+ } |
+ |
+ this.prevScrollTop = sTop; |
+ this.y = y; |
+ |
+ if (event) { |
+ this.fire('scroll', {target: this.scroller}, this, false); |
+ } |
+ } |
+ |
+ }); |
+ |
+</script> |
+</polymer-element> |