1<!-- 2@license 3Copyright (c) 2015 The Polymer Project Authors. All rights reserved. 4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt 5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt 6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt 7Code distributed by Google as part of the polymer project is also 8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt 9--> 10 11<link rel="import" href="../polymer/polymer.html"> 12<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html"> 13 14<!-- 15`iron-collapse` creates a collapsible block of content. By default, the content 16will be collapsed. Use `opened` or `toggle()` to show/hide the content. 17 18 <button on-click="toggle">toggle collapse</button> 19 20 <iron-collapse id="collapse"> 21 <div>Content goes here...</div> 22 </iron-collapse> 23 24 ... 25 26 toggle: function() { 27 this.$.collapse.toggle(); 28 } 29 30`iron-collapse` adjusts the max-height/max-width of the collapsible element to show/hide 31the content. So avoid putting padding/margin/border on the collapsible directly, 32and instead put a div inside and style that. 33 34 <style> 35 .collapse-content { 36 padding: 15px; 37 border: 1px solid #dedede; 38 } 39 </style> 40 41 <iron-collapse> 42 <div class="collapse-content"> 43 <div>Content goes here...</div> 44 </div> 45 </iron-collapse> 46 47### Styling 48 49The following custom properties and mixins are available for styling: 50 51Custom property | Description | Default 52----------------|-------------|---------- 53`--iron-collapse-transition-duration` | Animation transition duration | `300ms` 54 55@group Iron Elements 56@hero hero.svg 57@demo demo/index.html 58@element iron-collapse 59--> 60 61<dom-module id="iron-collapse"> 62 63 <template> 64 65 <style> 66 :host { 67 display: block; 68 transition-duration: var(--iron-collapse-transition-duration, 300ms); 69 overflow: visible; 70 } 71 72 :host(.iron-collapse-closed) { 73 display: none; 74 } 75 76 :host(:not(.iron-collapse-opened)) { 77 overflow: hidden; 78 } 79 </style> 80 81 <content></content> 82 83 </template> 84 85</dom-module> 86 87<script> 88 89 Polymer({ 90 91 is: 'iron-collapse', 92 93 behaviors: [ 94 Polymer.IronResizableBehavior 95 ], 96 97 properties: { 98 99 /** 100 * If true, the orientation is horizontal; otherwise is vertical. 101 * 102 * @attribute horizontal 103 */ 104 horizontal: { 105 type: Boolean, 106 value: false, 107 observer: '_horizontalChanged' 108 }, 109 110 /** 111 * Set opened to true to show the collapse element and to false to hide it. 112 * 113 * @attribute opened 114 */ 115 opened: { 116 type: Boolean, 117 value: false, 118 notify: true, 119 observer: '_openedChanged' 120 }, 121 122 /** 123 * When true, the element is transitioning its opened state. When false, 124 * the element has finished opening/closing. 125 * 126 * @attribute transitioning 127 */ 128 transitioning: { 129 type: Boolean, 130 notify: true, 131 readOnly: true 132 }, 133 134 /** 135 * Set noAnimation to true to disable animations. 136 * 137 * @attribute noAnimation 138 */ 139 noAnimation: { 140 type: Boolean 141 }, 142 143 /** 144 * Stores the desired size of the collapse body. 145 * @private 146 */ 147 _desiredSize: { 148 type: String, 149 value: '' 150 } 151 }, 152 153 get dimension() { 154 return this.horizontal ? 'width' : 'height'; 155 }, 156 157 /** 158 * `maxWidth` or `maxHeight`. 159 * @private 160 */ 161 get _dimensionMax() { 162 return this.horizontal ? 'maxWidth' : 'maxHeight'; 163 }, 164 165 /** 166 * `max-width` or `max-height`. 167 * @private 168 */ 169 get _dimensionMaxCss() { 170 return this.horizontal ? 'max-width' : 'max-height'; 171 }, 172 173 hostAttributes: { 174 role: 'group', 175 'aria-hidden': 'true', 176 'aria-expanded': 'false' 177 }, 178 179 listeners: { 180 transitionend: '_onTransitionEnd' 181 }, 182 183 /** 184 * Toggle the opened state. 185 * 186 * @method toggle 187 */ 188 toggle: function() { 189 this.opened = !this.opened; 190 }, 191 192 show: function() { 193 this.opened = true; 194 }, 195 196 hide: function() { 197 this.opened = false; 198 }, 199 200 /** 201 * Updates the size of the element. 202 * @param {string} size The new value for `maxWidth`/`maxHeight` as css property value, usually `auto` or `0px`. 203 * @param {boolean=} animated if `true` updates the size with an animation, otherwise without. 204 */ 205 updateSize: function(size, animated) { 206 // Consider 'auto' as '', to take full size. 207 size = size === 'auto' ? '' : size; 208 209 var willAnimate = animated && !this.noAnimation && 210 this.isAttached && this._desiredSize !== size; 211 212 this._desiredSize = size; 213 214 this._updateTransition(false); 215 // If we can animate, must do some prep work. 216 if (willAnimate) { 217 // Animation will start at the current size. 218 var startSize = this._calcSize(); 219 // For `auto` we must calculate what is the final size for the animation. 220 // After the transition is done, _transitionEnd will set the size back to `auto`. 221 if (size === '') { 222 this.style[this._dimensionMax] = ''; 223 size = this._calcSize(); 224 } 225 // Go to startSize without animation. 226 this.style[this._dimensionMax] = startSize; 227 // Force layout to ensure transition will go. Set scrollTop to itself 228 // so that compilers won't remove it. 229 this.scrollTop = this.scrollTop; 230 // Enable animation. 231 this._updateTransition(true); 232 // If final size is the same as startSize it will not animate. 233 willAnimate = (size !== startSize); 234 } 235 // Set the final size. 236 this.style[this._dimensionMax] = size; 237 // If it won't animate, call transitionEnd to set correct classes. 238 if (!willAnimate) { 239 this._transitionEnd(); 240 } 241 }, 242 243 /** 244 * enableTransition() is deprecated, but left over so it doesn't break existing code. 245 * Please use `noAnimation` property instead. 246 * 247 * @method enableTransition 248 * @deprecated since version 1.0.4 249 */ 250 enableTransition: function(enabled) { 251 Polymer.Base._warn('`enableTransition()` is deprecated, use `noAnimation` instead.'); 252 this.noAnimation = !enabled; 253 }, 254 255 _updateTransition: function(enabled) { 256 this.style.transitionDuration = (enabled && !this.noAnimation) ? '' : '0s'; 257 }, 258 259 _horizontalChanged: function() { 260 this.style.transitionProperty = this._dimensionMaxCss; 261 var otherDimension = this._dimensionMax === 'maxWidth' ? 'maxHeight' : 'maxWidth'; 262 this.style[otherDimension] = ''; 263 this.updateSize(this.opened ? 'auto' : '0px', false); 264 }, 265 266 _openedChanged: function() { 267 this.setAttribute('aria-expanded', this.opened); 268 this.setAttribute('aria-hidden', !this.opened); 269 270 this._setTransitioning(true); 271 this.toggleClass('iron-collapse-closed', false); 272 this.toggleClass('iron-collapse-opened', false); 273 this.updateSize(this.opened ? 'auto' : '0px', true); 274 275 // Focus the current collapse. 276 if (this.opened) { 277 this.focus(); 278 } 279 }, 280 281 _transitionEnd: function() { 282 this.style[this._dimensionMax] = this._desiredSize; 283 this.toggleClass('iron-collapse-closed', !this.opened); 284 this.toggleClass('iron-collapse-opened', this.opened); 285 this._updateTransition(false); 286 this.notifyResize(); 287 this._setTransitioning(false); 288 }, 289 290 _onTransitionEnd: function(event) { 291 if (Polymer.dom(event).rootTarget === this) { 292 this._transitionEnd(); 293 } 294 }, 295 296 _calcSize: function() { 297 return this.getBoundingClientRect()[this.dimension] + 'px'; 298 } 299 300 }); 301 302</script> 303