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 `core-collapse` creates a collapsible block of content. By default, the content
12 will be collapsed. Use `opened` or `toggle()` to show/hide the content.
14 <button on-click="{{toggle}}">toggle collapse</button>
16 <core-collapse id="collapse">
23 this.$.collapse.toggle();
26 `core-collapse` adjusts the height/width of the collapsible element to show/hide
27 the content. So avoid putting padding/margin/border on the collapsible directly,
28 and instead put a div inside and style that.
33 border: 1px solid #dedede;
38 <div class="collapse-content">
43 @group Polymer Core Elements
44 @element core-collapse
47 <link rel=
"import" href=
"../polymer/polymer.html">
49 <link rel=
"stylesheet" href=
"core-collapse.css" shim-shadowdom
>
51 <polymer-element name=
"core-collapse" attributes=
"target horizontal opened duration fixedSize allowOverflow">
59 Polymer('core-collapse', {
62 * Fired when the `core-collapse`'s `opened` property changes.
64 * @event core-collapse-open
68 * Fired when the target element has been resized as a result of the opened
75 * The target element that will be opened when the `core-collapse` is
76 * opened. If unspecified, the `core-collapse` itself is the target.
85 * If true, the orientation is horizontal; otherwise is vertical.
87 * @attribute horizontal
94 * Set opened to true to show the collapse element and to false to hide it.
103 * Collapsing/expanding animation duration in second.
105 * @attribute duration
112 * If true, the size of the target element is fixed and is set
113 * on the element. Otherwise it will try to
114 * use auto to determine the natural size to use
115 * for collapsing/expanding.
117 * @attribute fixedSize
124 * By default the collapsible element is set to overflow hidden. This helps
125 * avoid element bleeding outside the region and provides consistent overflow
126 * style across opened and closed states. Set this property to true to allow
127 * the collapsible element to overflow when it's opened.
129 * @attribute allowOverflow
133 allowOverflow
: false,
135 created: function() {
136 this.transitionEndListener
= this.transitionEnd
.bind(this);
140 this.target
= this.target
|| this;
143 domReady: function() {
144 this.async(function() {
145 this.afterInitialUpdate
= true;
149 detached: function() {
151 this.removeListeners(this.target
);
155 targetChanged: function(old
) {
157 this.removeListeners(old
);
162 this.isTargetReady
= !!this.target
;
163 this.classList
.toggle('core-collapse-closed', this.target
!== this);
164 this.toggleOpenedStyle(false);
165 this.horizontalChanged();
166 this.addListeners(this.target
);
167 // set core-collapse-closed class initially to hide the target
168 this.toggleClosedClass(true);
172 addListeners: function(node
) {
173 node
.addEventListener('transitionend', this.transitionEndListener
);
176 removeListeners: function(node
) {
177 node
.removeEventListener('transitionend', this.transitionEndListener
);
180 horizontalChanged: function() {
181 this.dimension
= this.horizontal
? 'width' : 'height';
184 openedChanged: function() {
186 this.fire('core-collapse-open', this.opened
);
190 * Toggle the opened state.
195 this.opened
= !this.opened
;
198 setTransitionDuration: function(duration
) {
199 var s
= this.target
.style
;
200 s
.transition
= duration
? (this.dimension
+ ' ' + duration
+ 's') : null;
201 if (duration
=== 0) {
202 this.async('transitionEnd');
206 transitionEnd: function() {
207 if (this.opened
&& !this.fixedSize
) {
208 this.updateSize('auto', null);
210 this.setTransitionDuration(null);
211 this.toggleOpenedStyle(this.opened
);
212 this.toggleClosedClass(!this.opened
);
213 this.asyncFire('core-resize', null, this.target
);
216 toggleClosedClass: function(closed
) {
217 this.hasClosedClass
= closed
;
218 this.target
.classList
.toggle('core-collapse-closed', closed
);
221 toggleOpenedStyle: function(opened
) {
222 this.target
.style
.overflow
= this.allowOverflow
&& opened
? '' : 'hidden';
225 updateSize: function(size
, duration
, forceEnd
) {
226 this.setTransitionDuration(duration
);
228 var s
= this.target
.style
;
229 var nochange
= s
[this.dimension
] === size
;
230 s
[this.dimension
] = size
;
231 // transitonEnd will not be called if the size has not changed
232 if (forceEnd
&& nochange
) {
233 this.transitionEnd();
241 if (!this.isTargetReady
) {
242 this.targetChanged();
244 this.horizontalChanged();
245 this[this.opened
? 'show' : 'hide']();
248 calcSize: function() {
249 return this.target
.getBoundingClientRect()[this.dimension
] + 'px';
252 getComputedSize: function() {
253 return getComputedStyle(this.target
)[this.dimension
];
257 this.toggleClosedClass(false);
258 // for initial update, skip the expanding animation to optimize
259 // performance e.g. skip calcSize
260 if (!this.afterInitialUpdate
) {
261 this.transitionEnd();
264 if (!this.fixedSize
) {
265 this.updateSize('auto', null);
266 var s
= this.calcSize();
268 this.transitionEnd();
271 this.updateSize(0, null);
273 this.async(function() {
274 this.updateSize(this.size
|| s
, this.duration
, true);
279 this.toggleOpenedStyle(false);
280 // don't need to do anything if it's already hidden
281 if (this.hasClosedClass
&& !this.fixedSize
) {
284 if (this.fixedSize
) {
285 // save the size before hiding it
286 this.size
= this.getComputedSize();
288 this.updateSize(this.calcSize(), null);
290 this.async(function() {
291 this.updateSize(0, this.duration
);