OLD | NEW |
| (Empty) |
1 | |
2 | |
3 (function() { | |
4 | |
5 'use strict'; | |
6 | |
7 var SHADOW_WHEN_SCROLLING = 1; | |
8 var SHADOW_ALWAYS = 2; | |
9 | |
10 | |
11 var MODE_CONFIGS = { | |
12 | |
13 outerScroll: { | |
14 scroll: true | |
15 }, | |
16 | |
17 shadowMode: { | |
18 standard: SHADOW_ALWAYS, | |
19 waterfall: SHADOW_WHEN_SCROLLING, | |
20 'waterfall-tall': SHADOW_WHEN_SCROLLING | |
21 }, | |
22 | |
23 tallMode: { | |
24 'waterfall-tall': true | |
25 } | |
26 }; | |
27 | |
28 Polymer({ | |
29 | |
30 is: 'paper-header-panel', | |
31 | |
32 /** | |
33 * Fired when the content has been scrolled. `event.detail.target` return
s | |
34 * the scrollable element which you can use to access scroll info such as | |
35 * `scrollTop`. | |
36 * | |
37 * <paper-header-panel on-content-scroll="{{scrollHandler}}"> | |
38 * ... | |
39 * </paper-header-panel> | |
40 * | |
41 * | |
42 * scrollHandler: function(event) { | |
43 * var scroller = event.detail.target; | |
44 * console.log(scroller.scrollTop); | |
45 * } | |
46 * | |
47 * @event content-scroll | |
48 */ | |
49 | |
50 properties: { | |
51 | |
52 /** | |
53 * Controls header and scrolling behavior. Options are | |
54 * `standard`, `seamed`, `waterfall`, `waterfall-tall`, `scroll` and | |
55 * `cover`. Default is `standard`. | |
56 * | |
57 * `standard`: The header is a step above the panel. The header will con
sume the | |
58 * panel at the point of entry, preventing it from passing through to th
e | |
59 * opposite side. | |
60 * | |
61 * `seamed`: The header is presented as seamed with the panel. | |
62 * | |
63 * `waterfall`: Similar to standard mode, but header is initially presen
ted as | |
64 * seamed with panel, but then separates to form the step. | |
65 * | |
66 * `waterfall-tall`: The header is initially taller (`tall` class is add
ed to | |
67 * the header). As the user scrolls, the header separates (forming an e
dge) | |
68 * while condensing (`tall` class is removed from the header). | |
69 * | |
70 * `scroll`: The header keeps its seam with the panel, and is pushed off
screen. | |
71 * | |
72 * `cover`: The panel covers the whole `paper-header-panel` including th
e | |
73 * header. This allows user to style the panel in such a way that the pa
nel is | |
74 * partially covering the header. | |
75 * | |
76 * <paper-header-panel mode="cover"> | |
77 * <paper-toolbar class="tall"> | |
78 * <core-icon-button icon="menu"></core-icon-button> | |
79 * </paper-toolbar> | |
80 * <div class="content"></div> | |
81 * </paper-header-panel> | |
82 */ | |
83 mode: { | |
84 type: String, | |
85 value: 'standard', | |
86 observer: '_modeChanged', | |
87 reflectToAttribute: true | |
88 }, | |
89 | |
90 /** | |
91 * If true, the drop-shadow is always shown no matter what mode is set t
o. | |
92 */ | |
93 shadow: { | |
94 type: Boolean, | |
95 value: false | |
96 }, | |
97 | |
98 /** | |
99 * The class used in waterfall-tall mode. Change this if the header | |
100 * accepts a different class for toggling height, e.g. "medium-tall" | |
101 */ | |
102 tallClass: { | |
103 type: String, | |
104 value: 'tall' | |
105 }, | |
106 | |
107 /** | |
108 * If true, the scroller is at the top | |
109 */ | |
110 atTop: { | |
111 type: Boolean, | |
112 value: true, | |
113 readOnly: true | |
114 } | |
115 }, | |
116 | |
117 observers: [ | |
118 '_computeDropShadowHidden(atTop, mode, shadow)' | |
119 ], | |
120 | |
121 ready: function() { | |
122 this.scrollHandler = this._scroll.bind(this); | |
123 this._addListener(); | |
124 | |
125 // Run `scroll` logic once to initialze class names, etc. | |
126 this._keepScrollingState(); | |
127 }, | |
128 | |
129 detached: function() { | |
130 this._removeListener(); | |
131 }, | |
132 | |
133 /** | |
134 * Returns the header element | |
135 * | |
136 * @property header | |
137 * @type Object | |
138 */ | |
139 get header() { | |
140 return Polymer.dom(this.$.headerContent).getDistributedNodes()[0]; | |
141 }, | |
142 | |
143 /** | |
144 * Returns the scrollable element. | |
145 * | |
146 * @property scroller | |
147 * @type Object | |
148 */ | |
149 get scroller() { | |
150 return this._getScrollerForMode(this.mode); | |
151 }, | |
152 | |
153 /** | |
154 * Returns true if the scroller has a visible shadow. | |
155 * | |
156 * @property visibleShadow | |
157 * @type Boolean | |
158 */ | |
159 get visibleShadow() { | |
160 return this.header.classList.contains('has-shadow'); | |
161 }, | |
162 | |
163 _computeDropShadowHidden: function(atTop, mode, shadow) { | |
164 | |
165 var shadowMode = MODE_CONFIGS.shadowMode[mode]; | |
166 | |
167 if (this.shadow) { | |
168 this.toggleClass('has-shadow', true, this.header); | |
169 | |
170 } else if (shadowMode === SHADOW_ALWAYS) { | |
171 this.toggleClass('has-shadow', true, this.header); | |
172 | |
173 } else if (shadowMode === SHADOW_WHEN_SCROLLING && !atTop) { | |
174 this.toggleClass('has-shadow', true, this.header); | |
175 | |
176 } else { | |
177 this.toggleClass('has-shadow', false, this.header); | |
178 | |
179 } | |
180 }, | |
181 | |
182 _computeMainContainerClass: function(mode) { | |
183 // TODO: It will be useful to have a utility for classes | |
184 // e.g. Polymer.Utils.classes({ foo: true }); | |
185 | |
186 var classes = {}; | |
187 | |
188 classes['flex'] = mode !== 'cover'; | |
189 | |
190 return Object.keys(classes).filter( | |
191 function(className) { | |
192 return classes[className]; | |
193 }).join(' '); | |
194 }, | |
195 | |
196 _addListener: function() { | |
197 this.scroller.addEventListener('scroll', this.scrollHandler, false); | |
198 }, | |
199 | |
200 _removeListener: function() { | |
201 this.scroller.removeEventListener('scroll', this.scrollHandler); | |
202 }, | |
203 | |
204 _modeChanged: function(newMode, oldMode) { | |
205 var configs = MODE_CONFIGS; | |
206 var header = this.header; | |
207 var animateDuration = 200; | |
208 | |
209 if (header) { | |
210 // in tallMode it may add tallClass to the header; so do the cleanup | |
211 // when mode is changed from tallMode to not tallMode | |
212 if (configs.tallMode[oldMode] && !configs.tallMode[newMode]) { | |
213 header.classList.remove(this.tallClass); | |
214 this.async(function() { | |
215 header.classList.remove('animate'); | |
216 }, null, animateDuration); | |
217 } else { | |
218 header.classList.toggle('animate', configs.tallMode[newMode]); | |
219 } | |
220 } | |
221 this._keepScrollingState(); | |
222 }, | |
223 | |
224 _keepScrollingState: function () { | |
225 var main = this.scroller; | |
226 var header = this.header; | |
227 | |
228 this._setAtTop(main.scrollTop === 0); | |
229 | |
230 if (header && MODE_CONFIGS.tallMode[this.mode]) { | |
231 this.toggleClass(this.tallClass, this.atTop || | |
232 header.classList.contains(this.tallClass) && | |
233 main.scrollHeight < this.offsetHeight, header); | |
234 } | |
235 }, | |
236 | |
237 _scroll: function(e) { | |
238 this._keepScrollingState(); | |
239 this.fire('content-scroll', {target: this.scroller}, this, false); | |
240 }, | |
241 | |
242 _getScrollerForMode: function(mode) { | |
243 return MODE_CONFIGS.outerScroll[mode] ? | |
244 this : this.$.mainContainer; | |
245 } | |
246 | |
247 }); | |
248 | |
249 })(); | |
250 | |
OLD | NEW |