2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
3 This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
5 The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
6 Code distributed by Google as part of the polymer project is also
7 subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
11 `paper-tabs` is a `core-selector` styled to look like tabs. Tabs make it easy to
12 explore and switch between different views or functional aspects of an app, or
13 to browse categorized data sets.
15 Use `selected` property to get or set the selected tab.
19 <paper-tabs selected="0">
20 <paper-tab>TAB 1</paper-tab>
21 <paper-tab>TAB 2</paper-tab>
22 <paper-tab>TAB 3</paper-tab>
25 See <a href="#paper-tab">paper-tab</a> for more information about
28 A common usage for `paper-tabs` is to use it along with `core-pages` to switch
29 between different views.
31 <paper-tabs selected="{{selected}}">
32 <paper-tab>Tab 1</paper-tab>
33 <paper-tab>Tab 2</paper-tab>
34 <paper-tab>Tab 3</paper-tab>
37 <core-pages selected="{{selected}}">
43 `paper-tabs` adapt to mobile/narrow layout when there is a `core-narrow` class set
44 on itself or any of its ancestors.
46 To use links in tabs, add `link` attribute to `paper-tabs` and put an `<a>`
47 element in `paper-tab`.
51 <paper-tabs selected="0" link>
53 <a href="#link1" horizontal center-center layout>TAB ONE</a>
56 <a href="#link2" horizontal center-center layout>TAB TWO</a>
59 <a href="#link3" horizontal center-center layout>TAB THREE</a>
65 To change the sliding bar color:
67 paper-tabs.pink::shadow #selectionBar {
68 background-color: #ff4081;
71 To change the ink ripple color:
73 paper-tabs.pink paper-tab::shadow #ink {
79 @extends core-selector
83 <link rel=
"import" href=
"../core-selector/core-selector.html">
84 <link rel=
"import" href=
"../paper-icon-button/paper-icon-button.html">
85 <link rel=
"import" href=
"../core-resizable/core-resizable.html">
86 <link rel=
"import" href=
"paper-tab.html">
88 <polymer-element name=
"paper-tabs" extends=
"core-selector" attributes=
"noink nobar noslide scrollable hideScrollButton" role=
"tablist" horizontal center layout
>
91 <link rel=
"stylesheet" href=
"paper-tabs.css">
93 <div class=
"scroll-button" hidden?=
"{{!scrollable || hideScrollButton}}">
94 <paper-icon-button icon=
"chevron-left" class=
"{{ {hidden: leftHidden} | tokenList }}" on-down=
"{{holdLeft}}" on-up=
"{{releaseHold}}"></paper-icon-button>
97 <div id=
"tabsContainer" class=
"{{ {scrollable: scrollable} | tokenList }}" flex
on-scroll=
"{{scroll}}" on-trackstart=
"{{trackStart}}">
99 <div id=
"tabsContent" horizontal layout?=
"{{!scrollable}}">
101 <div id=
"selectionBar" hidden?=
"{{nobar}}" on-transitionend=
"{{barTransitionEnd}}"></div>
106 <div class=
"scroll-button" hidden?=
"{{!scrollable || hideScrollButton}}">
107 <paper-icon-button icon=
"chevron-right" class=
"{{ {hidden: rightHidden} | tokenList }}" on-down=
"{{holdRight}}" on-up=
"{{releaseHold}}"></paper-icon-button>
113 Polymer(Polymer
.mixin({
116 * If true, ink ripple effect is disabled.
125 * If true, the bottom bar to indicate the selected tab will not be shown.
134 * If true, the slide effect for the bottom bar is disabled.
143 * If true, tabs are scrollable and the tab width is based on the label width.
145 * @attribute scrollable
152 * If true, dragging on the tabs to scroll is disabled.
154 * @attribute disableDrag
161 * If true, scroll buttons (left/right arrow) will be hidden for scrollable tabs.
163 * @attribute hideScrollButton
167 hideScrollButton
: false,
170 'core-resize': 'resizeHandler'
173 activateEvent
: 'tap',
181 this._trackxHandler
= this.trackx
.bind(this);
182 Polymer
.addEventListener(this.$.tabsContainer
, 'trackx', this._trackxHandler
);
183 this._tabsObserver
= new MutationObserver(this.updateBar
.bind(this));
186 domReady: function() {
187 this.async('resizeHandler');
188 this._tabsObserver
.observe(this, {childList
: true, subtree
: true, characterData
: true});
191 attached: function() {
192 this.resizableAttachedHandler();
195 detached: function() {
196 Polymer
.removeEventListener(this.$.tabsContainer
, 'trackx', this._trackxHandler
);
197 this._tabsObserver
.disconnect();
198 this.resizableDetachedHandler();
201 trackStart: function(e
) {
202 if (!this.scrollable
|| this.disableDrag
) {
206 if (t
&& t
.cancelRipple
) {
209 this._startx
= this.$.tabsContainer
.scrollLeft
;
213 trackx: function(e
) {
214 if (!this.scrollable
|| this.disableDrag
) {
217 this.$.tabsContainer
.scrollLeft
= this._startx
- e
.dx
;
220 resizeHandler: function() {
226 if (!this.scrollable
) {
229 var tc
= this.$.tabsContainer
;
230 var l
= tc
.scrollLeft
;
231 this.leftHidden
= l
=== 0;
232 this.rightHidden
= l
=== (tc
.scrollWidth
- tc
.clientWidth
);
235 holdLeft: function() {
236 this.holdJob
= setInterval(this.scrollToLeft
.bind(this), this.holdDelay
);
239 holdRight: function() {
240 this.holdJob
= setInterval(this.scrollToRight
.bind(this), this.holdDelay
);
243 releaseHold: function() {
244 clearInterval(this.holdJob
);
248 scrollToLeft: function() {
249 this.$.tabsContainer
.scrollLeft
-= this.step
;
252 scrollToRight: function() {
253 this.$.tabsContainer
.scrollLeft
+= this.step
;
257 * Invoke this to update the size and position of the bottom bar. Usually
258 * you only need to call this if the `paper-tabs` is initially hidden and
259 * later becomes visible.
263 updateBar: function() {
264 this.async('selectedItemChanged');
267 selectedItemChanged: function(old
) {
268 var oldIndex
= this.selectedIndex
;
269 this.super(arguments
);
270 var s
= this.$.selectionBar
.style
;
272 if (!this.selectedItem
) {
278 var r
= this.$.tabsContent
.getBoundingClientRect();
282 r
= this.selectedItem
.getBoundingClientRect();
285 this._sOffsetLeft
= this._sl
- this._l
;
287 if (this.noslide
|| old
== null) {
288 this.positionBarForSelected();
292 var oldRect
= old
.getBoundingClientRect();
295 this.$.selectionBar
.classList
.add('expand');
296 if (oldIndex
< this.selectedIndex
) {
297 s
.width
= this.calcPercent(this._sl
+ this._sw
- oldRect
.left
) - m
+ '%';
298 this._transitionCounter
= 1;
300 s
.width
= this.calcPercent(oldRect
.left
+ oldRect
.width
- this._sl
) - m
+ '%';
301 s
.left
= this.calcPercent(this._sOffsetLeft
) + m
+ '%';
302 this._transitionCounter
= 2;
304 if (this.scrollable
) {
305 this.scrollToSelectedIfNeeded();
309 scrollToSelectedIfNeeded: function() {
310 var scrollLeft
= this.$.tabsContainer
.scrollLeft
;
311 // scroll to selected if needed
312 if (this._sOffsetLeft
+ this._sw
< scrollLeft
||
313 this._sOffsetLeft
- scrollLeft
> this.$.tabsContainer
.offsetWidth
) {
314 this.$.tabsContainer
.scrollLeft
= this._sOffsetLeft
;
318 positionBarForSelected: function() {
319 var s
= this.$.selectionBar
.style
;
320 s
.width
= this.calcPercent(this._sw
) + '%';
321 s
.left
= this.calcPercent(this._sOffsetLeft
) + '%';
324 calcPercent: function(w
) {
325 return 100 * w
/ this._w
;
328 barTransitionEnd: function(e
) {
329 this._transitionCounter
--;
330 var cl
= this.$.selectionBar
.classList
;
331 if (cl
.contains('expand') && !this._transitionCounter
) {
334 this.positionBarForSelected();
335 } else if (cl
.contains('contract')) {
336 cl
.remove('contract');
340 }, Polymer
.CoreResizable
));