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 `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. | |
13 | |
14 <button on-click="{{toggle}}">toggle collapse</button> | |
15 | |
16 <core-collapse id="collapse"> | |
17 Content goes here... | |
18 </core-collapse> | |
19 | |
20 ... | |
21 | |
22 toggle: function() { | |
23 this.$.collapse.toggle(); | |
24 } | |
25 | |
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. | |
29 | |
30 <style> | |
31 .collapse-content { | |
32 padding: 15px; | |
33 border: 1px solid #dedede; | |
34 } | |
35 </style> | |
36 | |
37 <core-collapse> | |
38 <div class="collapse-content"> | |
39 Content goes here... | |
40 </div> | |
41 </core-collapse> | |
42 | |
43 @group Polymer Core Elements | |
44 @element core-collapse | |
45 --> | |
46 | |
47 <link rel="import" href="../polymer/polymer.html"> | |
48 | |
49 <link rel="stylesheet" href="core-collapse.css" shim-shadowdom> | |
50 | |
51 <polymer-element name="core-collapse" attributes="target horizontal opened durat
ion fixedSize allowOverflow"> | |
52 <template> | |
53 | |
54 <content></content> | |
55 | |
56 </template> | |
57 <script> | |
58 | |
59 Polymer('core-collapse', { | |
60 | |
61 /** | |
62 * Fired when the `core-collapse`'s `opened` property changes. | |
63 * | |
64 * @event core-collapse-open | |
65 */ | |
66 | |
67 /** | |
68 * Fired when the target element has been resized as a result of the opened | |
69 * state changing. | |
70 * | |
71 * @event core-resize | |
72 */ | |
73 | |
74 /** | |
75 * The target element that will be opened when the `core-collapse` is | |
76 * opened. If unspecified, the `core-collapse` itself is the target. | |
77 * | |
78 * @attribute target | |
79 * @type object | |
80 * @default null | |
81 */ | |
82 target: null, | |
83 | |
84 /** | |
85 * If true, the orientation is horizontal; otherwise is vertical. | |
86 * | |
87 * @attribute horizontal | |
88 * @type boolean | |
89 * @default false | |
90 */ | |
91 horizontal: false, | |
92 | |
93 /** | |
94 * Set opened to true to show the collapse element and to false to hide it. | |
95 * | |
96 * @attribute opened | |
97 * @type boolean | |
98 * @default false | |
99 */ | |
100 opened: false, | |
101 | |
102 /** | |
103 * Collapsing/expanding animation duration in second. | |
104 * | |
105 * @attribute duration | |
106 * @type number | |
107 * @default 0.33 | |
108 */ | |
109 duration: 0.33, | |
110 | |
111 /** | |
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. | |
116 * | |
117 * @attribute fixedSize | |
118 * @type boolean | |
119 * @default false | |
120 */ | |
121 fixedSize: false, | |
122 | |
123 /** | |
124 * By default the collapsible element is set to overflow hidden. This helps | |
125 * avoid element bleeding outside the region and provides consistent overflo
w | |
126 * style across opened and closed states. Set this property to true to allow
| |
127 * the collapsible element to overflow when it's opened. | |
128 * | |
129 * @attribute allowOverflow | |
130 * @type boolean | |
131 * @default false | |
132 */ | |
133 allowOverflow: false, | |
134 | |
135 created: function() { | |
136 this.transitionEndListener = this.transitionEnd.bind(this); | |
137 }, | |
138 | |
139 ready: function() { | |
140 this.target = this.target || this; | |
141 }, | |
142 | |
143 domReady: function() { | |
144 this.async(function() { | |
145 this.afterInitialUpdate = true; | |
146 }); | |
147 }, | |
148 | |
149 detached: function() { | |
150 if (this.target) { | |
151 this.removeListeners(this.target); | |
152 } | |
153 }, | |
154 | |
155 targetChanged: function(old) { | |
156 if (old) { | |
157 this.removeListeners(old); | |
158 } | |
159 if (!this.target) { | |
160 return; | |
161 } | |
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); | |
169 this.update(); | |
170 }, | |
171 | |
172 addListeners: function(node) { | |
173 node.addEventListener('transitionend', this.transitionEndListener); | |
174 }, | |
175 | |
176 removeListeners: function(node) { | |
177 node.removeEventListener('transitionend', this.transitionEndListener); | |
178 }, | |
179 | |
180 horizontalChanged: function() { | |
181 this.dimension = this.horizontal ? 'width' : 'height'; | |
182 }, | |
183 | |
184 openedChanged: function() { | |
185 this.update(); | |
186 this.fire('core-collapse-open', this.opened); | |
187 }, | |
188 | |
189 /** | |
190 * Toggle the opened state. | |
191 * | |
192 * @method toggle | |
193 */ | |
194 toggle: function() { | |
195 this.opened = !this.opened; | |
196 }, | |
197 | |
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'); | |
203 } | |
204 }, | |
205 | |
206 transitionEnd: function() { | |
207 if (this.opened && !this.fixedSize) { | |
208 this.updateSize('auto', null); | |
209 } | |
210 this.setTransitionDuration(null); | |
211 this.toggleOpenedStyle(this.opened); | |
212 this.toggleClosedClass(!this.opened); | |
213 this.asyncFire('core-resize', null, this.target); | |
214 }, | |
215 | |
216 toggleClosedClass: function(closed) { | |
217 this.hasClosedClass = closed; | |
218 this.target.classList.toggle('core-collapse-closed', closed); | |
219 }, | |
220 | |
221 toggleOpenedStyle: function(opened) { | |
222 this.target.style.overflow = this.allowOverflow && opened ? '' : 'hidden'; | |
223 }, | |
224 | |
225 updateSize: function(size, duration, forceEnd) { | |
226 this.setTransitionDuration(duration); | |
227 this.calcSize(); | |
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(); | |
234 } | |
235 }, | |
236 | |
237 update: function() { | |
238 if (!this.target) { | |
239 return; | |
240 } | |
241 if (!this.isTargetReady) { | |
242 this.targetChanged(); | |
243 } | |
244 this.horizontalChanged(); | |
245 this[this.opened ? 'show' : 'hide'](); | |
246 }, | |
247 | |
248 calcSize: function() { | |
249 return this.target.getBoundingClientRect()[this.dimension] + 'px'; | |
250 }, | |
251 | |
252 getComputedSize: function() { | |
253 return getComputedStyle(this.target)[this.dimension]; | |
254 }, | |
255 | |
256 show: function() { | |
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(); | |
262 return; | |
263 } | |
264 if (!this.fixedSize) { | |
265 this.updateSize('auto', null); | |
266 var s = this.calcSize(); | |
267 if (s == '0px') { | |
268 this.transitionEnd(); | |
269 return; | |
270 } | |
271 this.updateSize(0, null); | |
272 } | |
273 this.async(function() { | |
274 this.updateSize(this.size || s, this.duration, true); | |
275 }); | |
276 }, | |
277 | |
278 hide: function() { | |
279 this.toggleOpenedStyle(false); | |
280 // don't need to do anything if it's already hidden | |
281 if (this.hasClosedClass && !this.fixedSize) { | |
282 return; | |
283 } | |
284 if (this.fixedSize) { | |
285 // save the size before hiding it | |
286 this.size = this.getComputedSize(); | |
287 } else { | |
288 this.updateSize(this.calcSize(), null); | |
289 } | |
290 this.async(function() { | |
291 this.updateSize(0, this.duration); | |
292 }); | |
293 } | |
294 | |
295 }); | |
296 | |
297 </script> | |
298 </polymer-element> | |
OLD | NEW |