OLD | NEW |
| (Empty) |
1 <!-- | |
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.g
ithub.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/CONTRI
BUTORS.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/PATEN
TS.txt | |
8 --> | |
9 | |
10 <!-- | |
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. | |
14 | |
15 Use `selected` property to get or set the selected tab. | |
16 | |
17 Example: | |
18 | |
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> | |
23 </paper-tabs> | |
24 | |
25 See <a href="#paper-tab">paper-tab</a> for more information about | |
26 `paper-tab`. | |
27 | |
28 A common usage for `paper-tabs` is to use it along with `core-pages` to switch | |
29 between different views. | |
30 | |
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> | |
35 </paper-tabs> | |
36 | |
37 <core-pages selected="{{selected}}"> | |
38 <div>Page 1</div> | |
39 <div>Page 2</div> | |
40 <div>Page 3</div> | |
41 </core-pages> | |
42 | |
43 `paper-tabs` adapt to mobile/narrow layout when there is a `core-narrow` class s
et | |
44 on itself or any of its ancestors. | |
45 | |
46 To use links in tabs, add `link` attribute to `paper-tabs` and put an `<a>` | |
47 element in `paper-tab`. | |
48 | |
49 Example: | |
50 | |
51 <paper-tabs selected="0" link> | |
52 <paper-tab> | |
53 <a href="#link1" horizontal center-center layout>TAB ONE</a> | |
54 </paper-tab> | |
55 <paper-tab> | |
56 <a href="#link2" horizontal center-center layout>TAB TWO</a> | |
57 </paper-tab> | |
58 <paper-tab> | |
59 <a href="#link3" horizontal center-center layout>TAB THREE</a> | |
60 </paper-tab> | |
61 </paper-tabs> | |
62 | |
63 Styling tabs: | |
64 | |
65 To change the sliding bar color: | |
66 | |
67 paper-tabs.pink::shadow #selectionBar { | |
68 background-color: #ff4081; | |
69 } | |
70 | |
71 To change the ink ripple color: | |
72 | |
73 paper-tabs.pink paper-tab::shadow #ink { | |
74 color: #ff4081; | |
75 } | |
76 | |
77 @group Paper Elements | |
78 @element paper-tabs | |
79 @extends core-selector | |
80 @homepage github.io | |
81 --> | |
82 | |
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"> | |
87 | |
88 <polymer-element name="paper-tabs" extends="core-selector" attributes="noink nob
ar noslide scrollable hideScrollButton" role="tablist" horizontal center layout> | |
89 <template> | |
90 | |
91 <link rel="stylesheet" href="paper-tabs.css"> | |
92 | |
93 <div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}"> | |
94 <paper-icon-button icon="chevron-left" class="{{ {hidden: leftHidden} | toke
nList }}" on-down="{{holdLeft}}" on-up="{{releaseHold}}"></paper-icon-button> | |
95 </div> | |
96 | |
97 <div id="tabsContainer" class="{{ {scrollable: scrollable} | tokenList }}" fle
x on-scroll="{{scroll}}" on-trackstart="{{trackStart}}"> | |
98 | |
99 <div id="tabsContent" horizontal layout?="{{!scrollable}}"> | |
100 <shadow></shadow> | |
101 <div id="selectionBar" hidden?="{{nobar}}" on-transitionend="{{barTransiti
onEnd}}"></div> | |
102 </div> | |
103 | |
104 </div> | |
105 | |
106 <div class="scroll-button" hidden?="{{!scrollable || hideScrollButton}}"> | |
107 <paper-icon-button icon="chevron-right" class="{{ {hidden: rightHidden} | to
kenList }}" on-down="{{holdRight}}" on-up="{{releaseHold}}"></paper-icon-button> | |
108 </div> | |
109 | |
110 </template> | |
111 <script> | |
112 | |
113 Polymer(Polymer.mixin({ | |
114 | |
115 /** | |
116 * If true, ink ripple effect is disabled. | |
117 * | |
118 * @attribute noink | |
119 * @type boolean | |
120 * @default false | |
121 */ | |
122 noink: false, | |
123 | |
124 /** | |
125 * If true, the bottom bar to indicate the selected tab will not be shown. | |
126 * | |
127 * @attribute nobar | |
128 * @type boolean | |
129 * @default false | |
130 */ | |
131 nobar: false, | |
132 | |
133 /** | |
134 * If true, the slide effect for the bottom bar is disabled. | |
135 * | |
136 * @attribute noslide | |
137 * @type boolean | |
138 * @default false | |
139 */ | |
140 noslide: false, | |
141 | |
142 /** | |
143 * If true, tabs are scrollable and the tab width is based on the label widt
h. | |
144 * | |
145 * @attribute scrollable | |
146 * @type boolean | |
147 * @default false | |
148 */ | |
149 scrollable: false, | |
150 | |
151 /** | |
152 * If true, dragging on the tabs to scroll is disabled. | |
153 * | |
154 * @attribute disableDrag | |
155 * @type boolean | |
156 * @default false | |
157 */ | |
158 disableDrag: false, | |
159 | |
160 /** | |
161 * If true, scroll buttons (left/right arrow) will be hidden for scrollable
tabs. | |
162 * | |
163 * @attribute hideScrollButton | |
164 * @type boolean | |
165 * @default false | |
166 */ | |
167 hideScrollButton: false, | |
168 | |
169 eventDelegates: { | |
170 'core-resize': 'resizeHandler' | |
171 }, | |
172 | |
173 activateEvent: 'tap', | |
174 | |
175 step: 10, | |
176 | |
177 holdDelay: 10, | |
178 | |
179 ready: function() { | |
180 this.super(); | |
181 this._trackxHandler = this.trackx.bind(this); | |
182 Polymer.addEventListener(this.$.tabsContainer, 'trackx', this._trackxHandl
er); | |
183 this._tabsObserver = new MutationObserver(this.updateBar.bind(this)); | |
184 }, | |
185 | |
186 domReady: function() { | |
187 this.async('resizeHandler'); | |
188 this._tabsObserver.observe(this, {childList: true, subtree: true, characte
rData: true}); | |
189 }, | |
190 | |
191 attached: function() { | |
192 this.resizableAttachedHandler(); | |
193 }, | |
194 | |
195 detached: function() { | |
196 Polymer.removeEventListener(this.$.tabsContainer, 'trackx', this._trackxHa
ndler); | |
197 this._tabsObserver.disconnect(); | |
198 this.resizableDetachedHandler(); | |
199 }, | |
200 | |
201 trackStart: function(e) { | |
202 if (!this.scrollable || this.disableDrag) { | |
203 return; | |
204 } | |
205 var t = e.target; | |
206 if (t && t.cancelRipple) { | |
207 t.cancelRipple(); | |
208 } | |
209 this._startx = this.$.tabsContainer.scrollLeft; | |
210 e.preventTap(); | |
211 }, | |
212 | |
213 trackx: function(e) { | |
214 if (!this.scrollable || this.disableDrag) { | |
215 return; | |
216 } | |
217 this.$.tabsContainer.scrollLeft = this._startx - e.dx; | |
218 }, | |
219 | |
220 resizeHandler: function() { | |
221 this.scroll(); | |
222 this.updateBar(); | |
223 }, | |
224 | |
225 scroll: function() { | |
226 if (!this.scrollable) { | |
227 return; | |
228 } | |
229 var tc = this.$.tabsContainer; | |
230 var l = tc.scrollLeft; | |
231 this.leftHidden = l === 0; | |
232 this.rightHidden = l === (tc.scrollWidth - tc.clientWidth); | |
233 }, | |
234 | |
235 holdLeft: function() { | |
236 this.holdJob = setInterval(this.scrollToLeft.bind(this), this.holdDelay); | |
237 }, | |
238 | |
239 holdRight: function() { | |
240 this.holdJob = setInterval(this.scrollToRight.bind(this), this.holdDelay); | |
241 }, | |
242 | |
243 releaseHold: function() { | |
244 clearInterval(this.holdJob); | |
245 this.holdJob = null; | |
246 }, | |
247 | |
248 scrollToLeft: function() { | |
249 this.$.tabsContainer.scrollLeft -= this.step; | |
250 }, | |
251 | |
252 scrollToRight: function() { | |
253 this.$.tabsContainer.scrollLeft += this.step; | |
254 }, | |
255 | |
256 /** | |
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. | |
260 * | |
261 * @method updateBar | |
262 */ | |
263 updateBar: function() { | |
264 this.async('selectedItemChanged'); | |
265 }, | |
266 | |
267 selectedItemChanged: function(old) { | |
268 var oldIndex = this.selectedIndex; | |
269 this.super(arguments); | |
270 var s = this.$.selectionBar.style; | |
271 | |
272 if (!this.selectedItem) { | |
273 s.width = 0; | |
274 s.left = 0; | |
275 return; | |
276 } | |
277 | |
278 var r = this.$.tabsContent.getBoundingClientRect(); | |
279 this._w = r.width; | |
280 this._l = r.left; | |
281 | |
282 r = this.selectedItem.getBoundingClientRect(); | |
283 this._sw = r.width; | |
284 this._sl = r.left; | |
285 this._sOffsetLeft = this._sl - this._l; | |
286 | |
287 if (this.noslide || old == null) { | |
288 this.positionBarForSelected(); | |
289 return; | |
290 } | |
291 | |
292 var oldRect = old.getBoundingClientRect(); | |
293 | |
294 var m = 5; | |
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; | |
299 } else { | |
300 s.width = this.calcPercent(oldRect.left + oldRect.width - this._sl) - m
+ '%'; | |
301 s.left = this.calcPercent(this._sOffsetLeft) + m + '%'; | |
302 this._transitionCounter = 2; | |
303 } | |
304 if (this.scrollable) { | |
305 this.scrollToSelectedIfNeeded(); | |
306 } | |
307 }, | |
308 | |
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; | |
315 } | |
316 }, | |
317 | |
318 positionBarForSelected: function() { | |
319 var s = this.$.selectionBar.style; | |
320 s.width = this.calcPercent(this._sw) + '%'; | |
321 s.left = this.calcPercent(this._sOffsetLeft) + '%'; | |
322 }, | |
323 | |
324 calcPercent: function(w) { | |
325 return 100 * w / this._w; | |
326 }, | |
327 | |
328 barTransitionEnd: function(e) { | |
329 this._transitionCounter--; | |
330 var cl = this.$.selectionBar.classList; | |
331 if (cl.contains('expand') && !this._transitionCounter) { | |
332 cl.remove('expand'); | |
333 cl.add('contract'); | |
334 this.positionBarForSelected(); | |
335 } else if (cl.contains('contract')) { | |
336 cl.remove('contract'); | |
337 } | |
338 } | |
339 | |
340 }, Polymer.CoreResizable)); | |
341 | |
342 </script> | |
343 </polymer-element> | |
OLD | NEW |