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 |