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` to show/hide the content. | |
13 | |
14 <button on-click="{{toggle}}">toggle collapse</button> | |
15 | |
16 <core-collapse id="collapse"> | |
17 ... | |
18 </core-collapse> | |
19 | |
20 ... | |
21 | |
22 toggle: function() { | |
23 this.$.collapse.toggle(); | |
24 } | |
25 | |
26 @group Polymer Core Elements | |
27 @element core-collapse | |
28 --> | |
29 | |
30 <link rel="import" href="../polymer/polymer.html"> | |
31 | |
32 <link rel="stylesheet" href="core-collapse.css" shim-shadowdom> | |
33 | |
34 <polymer-element name="core-collapse" attributes="target horizontal opened durat
ion fixedSize"> | |
35 <template> | |
36 | |
37 <content></content> | |
38 | |
39 </template> | |
40 <script> | |
41 | |
42 Polymer('core-collapse', { | |
43 | |
44 /** | |
45 * Fired when the `core-collapse`'s `opened` property changes. | |
46 * | |
47 * @event core-collapse-open | |
48 */ | |
49 | |
50 /** | |
51 * Fired when the target element has been resized as a result of the opened | |
52 * state changing. | |
53 * | |
54 * @event core-resize | |
55 */ | |
56 | |
57 /** | |
58 * The target element. | |
59 * | |
60 * @attribute target | |
61 * @type object | |
62 * @default null | |
63 */ | |
64 target: null, | |
65 | |
66 /** | |
67 * If true, the orientation is horizontal; otherwise is vertical. | |
68 * | |
69 * @attribute horizontal | |
70 * @type boolean | |
71 * @default false | |
72 */ | |
73 horizontal: false, | |
74 | |
75 /** | |
76 * Set opened to true to show the collapse element and to false to hide it. | |
77 * | |
78 * @attribute opened | |
79 * @type boolean | |
80 * @default false | |
81 */ | |
82 opened: false, | |
83 | |
84 /** | |
85 * Collapsing/expanding animation duration in second. | |
86 * | |
87 * @attribute duration | |
88 * @type number | |
89 * @default 0.33 | |
90 */ | |
91 duration: 0.33, | |
92 | |
93 /** | |
94 * If true, the size of the target element is fixed and is set | |
95 * on the element. Otherwise it will try to | |
96 * use auto to determine the natural size to use | |
97 * for collapsing/expanding. | |
98 * | |
99 * @attribute fixedSize | |
100 * @type boolean | |
101 * @default false | |
102 */ | |
103 fixedSize: false, | |
104 | |
105 created: function() { | |
106 this.transitionEndListener = this.transitionEnd.bind(this); | |
107 }, | |
108 | |
109 ready: function() { | |
110 this.target = this.target || this; | |
111 }, | |
112 | |
113 domReady: function() { | |
114 this.async(function() { | |
115 this.afterInitialUpdate = true; | |
116 }); | |
117 }, | |
118 | |
119 detached: function() { | |
120 if (this.target) { | |
121 this.removeListeners(this.target); | |
122 } | |
123 }, | |
124 | |
125 targetChanged: function(old) { | |
126 if (old) { | |
127 this.removeListeners(old); | |
128 } | |
129 if (!this.target) { | |
130 return; | |
131 } | |
132 this.isTargetReady = !!this.target; | |
133 this.classList.toggle('core-collapse-closed', this.target !== this); | |
134 this.target.style.overflow = 'hidden'; | |
135 this.horizontalChanged(); | |
136 this.addListeners(this.target); | |
137 // set core-collapse-closed class initially to hide the target | |
138 this.toggleClosedClass(true); | |
139 this.update(); | |
140 }, | |
141 | |
142 addListeners: function(node) { | |
143 node.addEventListener('transitionend', this.transitionEndListener); | |
144 }, | |
145 | |
146 removeListeners: function(node) { | |
147 node.removeEventListener('transitionend', this.transitionEndListener); | |
148 }, | |
149 | |
150 horizontalChanged: function() { | |
151 this.dimension = this.horizontal ? 'width' : 'height'; | |
152 }, | |
153 | |
154 openedChanged: function() { | |
155 this.update(); | |
156 this.fire('core-collapse-open', this.opened); | |
157 }, | |
158 | |
159 /** | |
160 * Toggle the opened state. | |
161 * | |
162 * @method toggle | |
163 */ | |
164 toggle: function() { | |
165 this.opened = !this.opened; | |
166 }, | |
167 | |
168 setTransitionDuration: function(duration) { | |
169 var s = this.target.style; | |
170 s.transition = duration ? (this.dimension + ' ' + duration + 's') : null; | |
171 if (duration === 0) { | |
172 this.async('transitionEnd'); | |
173 } | |
174 }, | |
175 | |
176 transitionEnd: function() { | |
177 if (this.opened && !this.fixedSize) { | |
178 this.updateSize('auto', null); | |
179 } | |
180 this.setTransitionDuration(null); | |
181 this.toggleClosedClass(!this.opened); | |
182 this.asyncFire('core-resize', null, this.target); | |
183 }, | |
184 | |
185 toggleClosedClass: function(closed) { | |
186 this.hasClosedClass = closed; | |
187 this.target.classList.toggle('core-collapse-closed', closed); | |
188 }, | |
189 | |
190 updateSize: function(size, duration, forceEnd) { | |
191 this.setTransitionDuration(duration); | |
192 this.calcSize(); | |
193 var s = this.target.style; | |
194 var nochange = s[this.dimension] === size; | |
195 s[this.dimension] = size; | |
196 // transitonEnd will not be called if the size has not changed | |
197 if (forceEnd && nochange) { | |
198 this.transitionEnd(); | |
199 } | |
200 }, | |
201 | |
202 update: function() { | |
203 if (!this.target) { | |
204 return; | |
205 } | |
206 if (!this.isTargetReady) { | |
207 this.targetChanged(); | |
208 } | |
209 this.horizontalChanged(); | |
210 this[this.opened ? 'show' : 'hide'](); | |
211 }, | |
212 | |
213 calcSize: function() { | |
214 return this.target.getBoundingClientRect()[this.dimension] + 'px'; | |
215 }, | |
216 | |
217 getComputedSize: function() { | |
218 return getComputedStyle(this.target)[this.dimension]; | |
219 }, | |
220 | |
221 show: function() { | |
222 this.toggleClosedClass(false); | |
223 // for initial update, skip the expanding animation to optimize | |
224 // performance e.g. skip calcSize | |
225 if (!this.afterInitialUpdate) { | |
226 this.transitionEnd(); | |
227 return; | |
228 } | |
229 if (!this.fixedSize) { | |
230 this.updateSize('auto', null); | |
231 var s = this.calcSize(); | |
232 if (s == '0px') { | |
233 this.transitionEnd(); | |
234 return; | |
235 } | |
236 this.updateSize(0, null); | |
237 } | |
238 this.async(function() { | |
239 this.updateSize(this.size || s, this.duration, true); | |
240 }); | |
241 }, | |
242 | |
243 hide: function() { | |
244 // don't need to do anything if it's already hidden | |
245 if (this.hasClosedClass && !this.fixedSize) { | |
246 return; | |
247 } | |
248 if (this.fixedSize) { | |
249 // save the size before hiding it | |
250 this.size = this.getComputedSize(); | |
251 } else { | |
252 this.updateSize(this.calcSize(), null); | |
253 } | |
254 this.async(function() { | |
255 this.updateSize(0, this.duration); | |
256 }); | |
257 } | |
258 | |
259 }); | |
260 | |
261 </script> | |
262 </polymer-element> | |
OLD | NEW |