OLD | NEW |
| (Empty) |
1 <!-- | |
2 @license | |
3 Copyright (c) 2015 The Polymer Project Authors. All rights reserved. | |
4 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | |
5 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
6 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | |
7 Code distributed by Google as part of the polymer project is also | |
8 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | |
9 --> | |
10 | |
11 <link rel="import" href="../polymer/polymer.html"> | |
12 <link rel="import" href="../iron-flex-layout/iron-flex-layout.html"> | |
13 <link rel="import" href="../iron-flex-layout/classes/iron-flex-layout.html"> | |
14 <link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html
"> | |
15 <link rel="import" href="../iron-menu-behavior/iron-menubar-behavior.html"> | |
16 <link rel="import" href="../iron-icon/iron-icon.html"> | |
17 <link rel="import" href="../paper-icon-button/paper-icon-button.html"> | |
18 <link rel="import" href="../paper-styles/color.html"> | |
19 <link rel="import" href="paper-tabs-icons.html"> | |
20 <link rel="import" href="paper-tab.html"> | |
21 | |
22 <!-- | |
23 `paper-tabs` makes it easy to explore and switch between different views or func
tional aspects of | |
24 an app, or to browse categorized data sets. | |
25 | |
26 Use `selected` property to get or set the selected tab. | |
27 | |
28 Example: | |
29 | |
30 <paper-tabs selected="0"> | |
31 <paper-tab>TAB 1</paper-tab> | |
32 <paper-tab>TAB 2</paper-tab> | |
33 <paper-tab>TAB 3</paper-tab> | |
34 </paper-tabs> | |
35 | |
36 See <a href="#paper-tab">paper-tab</a> for more information about | |
37 `paper-tab`. | |
38 | |
39 A common usage for `paper-tabs` is to use it along with `iron-pages` to switch | |
40 between different views. | |
41 | |
42 <paper-tabs selected="{{selected}}"> | |
43 <paper-tab>Tab 1</paper-tab> | |
44 <paper-tab>Tab 2</paper-tab> | |
45 <paper-tab>Tab 3</paper-tab> | |
46 </paper-tabs> | |
47 | |
48 <iron-pages selected="{{selected}}"> | |
49 <div>Page 1</div> | |
50 <div>Page 2</div> | |
51 <div>Page 3</div> | |
52 </iron-pages> | |
53 | |
54 | |
55 To use links in tabs, add `link` attribute to `paper-tab` and put an `<a>` | |
56 element in `paper-tab`. | |
57 | |
58 Example: | |
59 | |
60 <paper-tabs selected="0"> | |
61 <paper-tab link> | |
62 <a href="#link1" class="horizontal center-center layout">TAB ONE</a> | |
63 </paper-tab> | |
64 <paper-tab link> | |
65 <a href="#link2" class="horizontal center-center layout">TAB TWO</a> | |
66 </paper-tab> | |
67 <paper-tab link> | |
68 <a href="#link3" class="horizontal center-center layout">TAB THREE</a> | |
69 </paper-tab> | |
70 </paper-tabs> | |
71 | |
72 ### Styling | |
73 | |
74 The following custom properties and mixins are available for styling: | |
75 | |
76 Custom property | Description | Default | |
77 ----------------|-------------|---------- | |
78 `--paper-tabs-selection-bar-color` | Color for the selection bar | `--paper-yell
ow-a100` | |
79 `--paper-tabs` | Mixin applied to the tabs | `{}` | |
80 | |
81 @hero hero.svg | |
82 @demo demo/index.html | |
83 --> | |
84 | |
85 <dom-module id="paper-tabs"> | |
86 | |
87 <style> | |
88 | |
89 :host { | |
90 @apply(--layout); | |
91 @apply(--layout-center); | |
92 | |
93 height: 48px; | |
94 font-size: 14px; | |
95 font-weight: 500; | |
96 overflow: hidden; | |
97 -webkit-user-select: none; | |
98 -moz-user-select: none; | |
99 -ms-user-select: none; | |
100 user-select: none; | |
101 -webkit-tap-highlight-color: rgba(0,0,0,0); | |
102 | |
103 @apply(--paper-tabs); | |
104 } | |
105 | |
106 #tabsContainer { | |
107 position: relative; | |
108 height: 100%; | |
109 white-space: nowrap; | |
110 overflow: hidden; | |
111 } | |
112 | |
113 #tabsContent { | |
114 height: 100%; | |
115 } | |
116 | |
117 #tabsContent.scrollable { | |
118 position: absolute; | |
119 white-space: nowrap; | |
120 } | |
121 | |
122 .hidden { | |
123 display: none; | |
124 } | |
125 | |
126 .not-visible { | |
127 opacity: 0; | |
128 } | |
129 | |
130 paper-icon-button { | |
131 width: 24px; | |
132 padding: 16px; | |
133 } | |
134 | |
135 #selectionBar { | |
136 position: absolute; | |
137 height: 2px; | |
138 bottom: 0; | |
139 left: 0; | |
140 right: 0; | |
141 background-color: var(--paper-tabs-selection-bar-color, --paper-yellow-a10
0); | |
142 -webkit-transform-origin: left center; | |
143 transform-origin: left center; | |
144 -webkit-transform: scale(0); | |
145 transform: scale(0); | |
146 transition: -webkit-transform; | |
147 transition: transform; | |
148 | |
149 @apply(--paper-tabs-selection-bar); | |
150 } | |
151 | |
152 #selectionBar.align-bottom { | |
153 top: 0; | |
154 bottom: auto; | |
155 } | |
156 | |
157 #selectionBar.expand { | |
158 transition-duration: 0.15s; | |
159 transition-timing-function: cubic-bezier(0.4, 0.0, 1, 1); | |
160 } | |
161 | |
162 #selectionBar.contract { | |
163 transition-duration: 0.18s; | |
164 transition-timing-function: cubic-bezier(0.0, 0.0, 0.2, 1); | |
165 } | |
166 | |
167 #tabsContent > ::content > *:not(#selectionBar) { | |
168 height: 100%; | |
169 } | |
170 | |
171 </style> | |
172 | |
173 <template> | |
174 | |
175 <paper-icon-button icon="paper-tabs:chevron-left" class$="[[_computeScrollBu
ttonClass(_leftHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButtonU
p" on-down="_onLeftScrollButtonDown"></paper-icon-button> | |
176 | |
177 <div id="tabsContainer" class="flex" on-scroll="_scroll"> | |
178 | |
179 <div id="tabsContent" class$="[[_computeTabsContentClass(scrollable)]]"> | |
180 | |
181 <content select="*"></content> | |
182 | |
183 <div id="selectionBar" class$="[[_computeSelectionBarClass(noBar, alignB
ottom)]]" | |
184 on-transitionend="_onBarTransitionEnd"></div> | |
185 | |
186 </div> | |
187 | |
188 </div> | |
189 | |
190 <paper-icon-button icon="paper-tabs:chevron-right" class$="[[_computeScrollB
uttonClass(_rightHidden, scrollable, hideScrollButtons)]]" on-up="_onScrollButto
nUp" on-down="_onRightScrollButtonDown"></paper-icon-button> | |
191 | |
192 </template> | |
193 | |
194 </dom-module> | |
195 | |
196 <script> | |
197 | |
198 Polymer({ | |
199 | |
200 is: 'paper-tabs', | |
201 | |
202 behaviors: [ | |
203 Polymer.IronResizableBehavior, | |
204 Polymer.IronMenubarBehavior | |
205 ], | |
206 | |
207 properties: { | |
208 | |
209 /** | |
210 * If true, ink ripple effect is disabled. | |
211 */ | |
212 noink: { | |
213 type: Boolean, | |
214 value: false | |
215 }, | |
216 | |
217 /** | |
218 * If true, the bottom bar to indicate the selected tab will not be shown. | |
219 */ | |
220 noBar: { | |
221 type: Boolean, | |
222 value: false | |
223 }, | |
224 | |
225 /** | |
226 * If true, the slide effect for the bottom bar is disabled. | |
227 */ | |
228 noSlide: { | |
229 type: Boolean, | |
230 value: false | |
231 }, | |
232 | |
233 /** | |
234 * If true, tabs are scrollable and the tab width is based on the label wi
dth. | |
235 */ | |
236 scrollable: { | |
237 type: Boolean, | |
238 value: false | |
239 }, | |
240 | |
241 /** | |
242 * If true, dragging on the tabs to scroll is disabled. | |
243 */ | |
244 disableDrag: { | |
245 type: Boolean, | |
246 value: false | |
247 }, | |
248 | |
249 /** | |
250 * If true, scroll buttons (left/right arrow) will be hidden for scrollabl
e tabs. | |
251 */ | |
252 hideScrollButtons: { | |
253 type: Boolean, | |
254 value: false | |
255 }, | |
256 | |
257 /** | |
258 * If true, the tabs are aligned to bottom (the selection bar appears at t
he top). | |
259 */ | |
260 alignBottom: { | |
261 type: Boolean, | |
262 value: false | |
263 }, | |
264 | |
265 /** | |
266 * Gets or sets the selected element. The default is to use the index of t
he item. | |
267 */ | |
268 selected: { | |
269 type: String, | |
270 notify: true | |
271 }, | |
272 | |
273 selectable: { | |
274 type: String, | |
275 value: 'paper-tab' | |
276 }, | |
277 | |
278 _step: { | |
279 type: Number, | |
280 value: 10 | |
281 }, | |
282 | |
283 _holdDelay: { | |
284 type: Number, | |
285 value: 1 | |
286 }, | |
287 | |
288 _leftHidden: { | |
289 type: Boolean, | |
290 value: false | |
291 }, | |
292 | |
293 _rightHidden: { | |
294 type: Boolean, | |
295 value: false | |
296 }, | |
297 | |
298 _previousTab: { | |
299 type: Object | |
300 } | |
301 }, | |
302 | |
303 hostAttributes: { | |
304 role: 'tablist' | |
305 }, | |
306 | |
307 listeners: { | |
308 'iron-resize': '_onResize', | |
309 'iron-select': '_onIronSelect', | |
310 'iron-deselect': '_onIronDeselect' | |
311 }, | |
312 | |
313 _computeScrollButtonClass: function(hideThisButton, scrollable, hideScrollBu
ttons) { | |
314 if (!scrollable || hideScrollButtons) { | |
315 return 'hidden'; | |
316 } | |
317 | |
318 if (hideThisButton) { | |
319 return 'not-visible'; | |
320 } | |
321 | |
322 return ''; | |
323 }, | |
324 | |
325 _computeTabsContentClass: function(scrollable) { | |
326 return scrollable ? 'scrollable' : 'horizontal layout'; | |
327 }, | |
328 | |
329 _computeSelectionBarClass: function(noBar, alignBottom) { | |
330 if (noBar) { | |
331 return 'hidden'; | |
332 } else if (alignBottom) { | |
333 return 'align-bottom'; | |
334 } | |
335 }, | |
336 | |
337 // TODO(cdata): Add `track` response back in when gesture lands. | |
338 | |
339 _onResize: function() { | |
340 this.debounce('_onResize', function() { | |
341 this._scroll(); | |
342 this._tabChanged(this.selectedItem); | |
343 }, 10); | |
344 }, | |
345 | |
346 _onIronSelect: function(event) { | |
347 this._tabChanged(event.detail.item, this._previousTab); | |
348 this._previousTab = event.detail.item; | |
349 this.cancelDebouncer('tab-changed'); | |
350 }, | |
351 | |
352 _onIronDeselect: function(event) { | |
353 this.debounce('tab-changed', function() { | |
354 this._tabChanged(null, this._previousTab); | |
355 // See polymer/polymer#1305 | |
356 }, 1); | |
357 }, | |
358 | |
359 get _tabContainerScrollSize () { | |
360 return Math.max( | |
361 0, | |
362 this.$.tabsContainer.scrollWidth - | |
363 this.$.tabsContainer.offsetWidth | |
364 ); | |
365 }, | |
366 | |
367 _scroll: function() { | |
368 var scrollLeft; | |
369 | |
370 if (!this.scrollable) { | |
371 return; | |
372 } | |
373 | |
374 scrollLeft = this.$.tabsContainer.scrollLeft; | |
375 | |
376 this._leftHidden = scrollLeft === 0; | |
377 this._rightHidden = scrollLeft === this._tabContainerScrollSize; | |
378 }, | |
379 | |
380 _onLeftScrollButtonDown: function() { | |
381 this._holdJob = setInterval(this._scrollToLeft.bind(this), this._holdDelay
); | |
382 }, | |
383 | |
384 _onRightScrollButtonDown: function() { | |
385 this._holdJob = setInterval(this._scrollToRight.bind(this), this._holdDela
y); | |
386 }, | |
387 | |
388 _onScrollButtonUp: function() { | |
389 clearInterval(this._holdJob); | |
390 this._holdJob = null; | |
391 }, | |
392 | |
393 _scrollToLeft: function() { | |
394 this.$.tabsContainer.scrollLeft -= this._step; | |
395 }, | |
396 | |
397 _scrollToRight: function() { | |
398 this.$.tabsContainer.scrollLeft += this._step; | |
399 }, | |
400 | |
401 _tabChanged: function(tab, old) { | |
402 if (!tab) { | |
403 this._positionBar(0, 0); | |
404 return; | |
405 } | |
406 | |
407 var r = this.$.tabsContent.getBoundingClientRect(); | |
408 var w = r.width; | |
409 var tabRect = tab.getBoundingClientRect(); | |
410 var tabOffsetLeft = tabRect.left - r.left; | |
411 | |
412 this._pos = { | |
413 width: this._calcPercent(tabRect.width, w), | |
414 left: this._calcPercent(tabOffsetLeft, w) | |
415 }; | |
416 | |
417 if (this.noSlide || old == null) { | |
418 // position bar directly without animation | |
419 this._positionBar(this._pos.width, this._pos.left); | |
420 return; | |
421 } | |
422 | |
423 var oldRect = old.getBoundingClientRect(); | |
424 var oldIndex = this.items.indexOf(old); | |
425 var index = this.items.indexOf(tab); | |
426 var m = 5; | |
427 | |
428 // bar animation: expand | |
429 this.$.selectionBar.classList.add('expand'); | |
430 | |
431 if (oldIndex < index) { | |
432 this._positionBar(this._calcPercent(tabRect.left + tabRect.width - oldRe
ct.left, w) - m, | |
433 this._left); | |
434 } else { | |
435 this._positionBar(this._calcPercent(oldRect.left + oldRect.width - tabRe
ct.left, w) - m, | |
436 this._calcPercent(tabOffsetLeft, w) + m); | |
437 } | |
438 | |
439 if (this.scrollable) { | |
440 this._scrollToSelectedIfNeeded(tabRect.width, tabOffsetLeft); | |
441 } | |
442 }, | |
443 | |
444 _scrollToSelectedIfNeeded: function(tabWidth, tabOffsetLeft) { | |
445 var l = tabOffsetLeft - this.$.tabsContainer.scrollLeft; | |
446 if (l < 0) { | |
447 this.$.tabsContainer.scrollLeft += l; | |
448 } else { | |
449 l += (tabWidth - this.$.tabsContainer.offsetWidth); | |
450 if (l > 0) { | |
451 this.$.tabsContainer.scrollLeft += l; | |
452 } | |
453 } | |
454 }, | |
455 | |
456 _calcPercent: function(w, w0) { | |
457 return 100 * w / w0; | |
458 }, | |
459 | |
460 _positionBar: function(width, left) { | |
461 width = width || 0; | |
462 left = left || 0; | |
463 | |
464 this._width = width; | |
465 this._left = left; | |
466 this.transform( | |
467 'translate3d(' + left + '%, 0, 0) scaleX(' + (width / 100) + ')', | |
468 this.$.selectionBar); | |
469 }, | |
470 | |
471 _onBarTransitionEnd: function(e) { | |
472 var cl = this.$.selectionBar.classList; | |
473 // bar animation: expand -> contract | |
474 if (cl.contains('expand')) { | |
475 cl.remove('expand'); | |
476 cl.add('contract'); | |
477 this._positionBar(this._pos.width, this._pos.left); | |
478 // bar animation done | |
479 } else if (cl.contains('contract')) { | |
480 cl.remove('contract'); | |
481 } | |
482 } | |
483 | |
484 }); | |
485 | |
486 </script> | |
OLD | NEW |