aboutsummaryrefslogtreecommitdiff
path: root/js/panels/Timeline/Collapser.js
diff options
context:
space:
mode:
Diffstat (limited to 'js/panels/Timeline/Collapser.js')
-rw-r--r--js/panels/Timeline/Collapser.js320
1 files changed, 320 insertions, 0 deletions
diff --git a/js/panels/Timeline/Collapser.js b/js/panels/Timeline/Collapser.js
new file mode 100644
index 00000000..d286d84c
--- /dev/null
+++ b/js/panels/Timeline/Collapser.js
@@ -0,0 +1,320 @@
1/*
2 * Collapser: Takes two elements and creates a visual "expando:" clicking on one element expands/collapses the other.
3 * Required properties:
4 * clicker: The element that will be clicked on.
5 * content: The element that will expand or collapse as the clicker is clicked on.
6 * Optional properties:
7 * isCollapsed: Is the content collapsed. Set to true on serialization (or initialization) to start content in collapsed state.
8 * Can be manually set as well.
9 * collapsibleClass: The CSS class to apply to the content and the clicker when collapsed. Defaults to "collapsible-collapsed".
10 * isAnimated: Set to true to apply a transition to expand/collapse (defaults to false).
11 * transitionClass: If isAnimated is set to true, the component will apply transitionClass to the content during the
12 * collapse process. You can then define transitionClass in your style sheet with the desired CSS transitions.
13 * Defaults to "collapsible-transition".
14 * contentHeight: If both isAnimated and isCollapsedAtStart are set to true, set contentHeight to the height of the content
15 * (in pixels, but without the "px") when not collapsed. If this value is not set, the first time the content is expanded
16 * the transition will not work. Subsequent collapses (and expansions) will transition as expected.
17 * isLabelClickable: Boolean that indicates whether or not the clicker should have listener events. Defaults to true; set to
18 * false for collapsers that will only be operated remotely.
19 * toggle(): Manually toggle the expand/collapse of the content.
20 *
21 */
22var Montage = require("montage/core/core").Montage,
23 Component = require("montage/ui/component").Component,
24 Collapser = exports.Collapser = Montage.create(Component, {
25
26 // This component has no template.
27 hasTemplate:{
28 value: false
29 },
30
31 /* === BEGIN: Models === */
32
33 // contentHeight: Stores the height of the content just before collapse.
34 _contentHeight: {
35 value: 0
36 },
37 contentHeight: {
38 get: function() {
39 return this._contentHeight;
40 },
41 set: function(newVal) {
42 this._contentHeight = newVal;
43 }
44 },
45
46 // isCollapsing: true if the collapser is collapsing (or expanding); used in the draw cycle.
47 _isCollapsing: {
48 value: false
49 },
50
51 // isAnimated: boolean to apply transition to expand/collapse
52 _isAnimated : {
53 value: false
54 },
55 isAnimated: {
56 get: function() {
57 return this._isAnimated;
58 },
59 set: function(newVal) {
60 this._isAnimated = newVal;
61 }
62 },
63
64 _bypassAnimation : {
65 value: false
66 },
67 bypassAnimation: {
68 get: function() {
69 return this._bypassAnimation;
70 },
71 set: function(newVal) {
72 this._bypassAnimation= newVal;
73 }
74 },
75
76 // transitionClass: The CSS class to apply to the content during collapse to provide CSS transition.
77 // Note that this CSS class must be defined in your style sheet with the desired transitions.
78 _transitionClass : {
79 value: "collapsible-transition"
80 },
81 transitionClass: {
82 get: function() {
83 return this._transitionClass;
84 },
85 set: function(newVal) {
86 this._transitionClass = newVal;
87 }
88 },
89
90 // isCollapsed: is the content actually collapsed at this moment
91 _isCollapsed: {
92 value: ""
93 },
94 isCollapsed : {
95 get: function() {
96 return this._isCollapsed;
97 },
98 set: function(newVal) {
99 if (newVal !== this._isCollapsed) {
100 this._isCollapsed = newVal;
101 this.needsDraw = true;
102 }
103
104 }
105 },
106
107 // collapsedClass: the class to apply to the clicker and content when the content is collapsed.
108 _collapsedClass : {
109 value: "collapsible-collapsed"
110 },
111 collapsedClass: {
112 get: function() {
113 return this._collapsedClass;
114 },
115 set: function(newVal) {
116 this._collapsedClass = newVal;
117 }
118 },
119
120 // _origOverflowValue: Stores the original overflow value of the collapsible element.
121 // Why store the value? While the collapsible element is collapsed, obviously we will need overflow: hidden.
122 // But when the collapsible element is open, we will need overflow to return to its original value.
123 _origOverflowValue : {
124 value: false
125 },
126
127 // isLabelClickable: Boolean for whether or not the label is clickable. If set to false,
128 // the label click listener is never applied. For collapsibles that will only be operated remotely.
129 // Defaults to true.
130 _isLabelClickable : {
131 value: true
132 },
133 isLabelClickable : {
134 get: function() {
135 return this._isLabelClickable;
136 },
137 set: function(newVal) {
138 this._isLabelClickable = newVal;
139 }
140 },
141
142 // labelClickEvent: an event to fire when the label is clicked.
143 _labelClickEvent: {
144 value: false
145 },
146 labelClickEvent: {
147 get: function() {
148 return this._labelClickEvent;
149 },
150 set: function(newVal) {
151 this._labelClickEvent = newVal;
152 }
153 },
154
155 // toggle: manually toggle the collapser.
156 toggle: {
157 value: function() {
158 if (this.bypassAnimation) {
159 this.isAnimated = false;
160 }
161 this.myContent.classList.remove(this.transitionClass);
162 this.handleCollapserLabelClick();
163 }
164 },
165
166 /* === END: Models === */
167
168 /* === BEGIN: Draw cycle === */
169
170 prepareForDraw: {
171 value: function() {
172 // Add a click listener to the label for expand/collapse
173 if (this.isLabelClickable) {
174 this.clicker.identifier = "collapserLabel";
175 this.clicker.addEventListener("click", this, false);
176 }
177
178 // Get the original value of the overflow property:
179 this._origOverflowValue = window.getComputedStyle(this.myContent, null).getPropertyValue("overflow");
180
181 // If the content area is supposed to start out collapsed:
182 if (this.isCollapsed) {
183 this.myContent.style.height = "0px";
184 // Set the overflow to hidden if it's not already
185 if (this._origOverflowValue !== "hidden") {
186 this.myContent.style.overflow = "hidden";
187 }
188 this.myContent.classList.add(this.collapsedClass);
189 this.clicker.classList.add(this.collapsedClass);
190 } else {
191 this.myContent.style.height = "auto";
192 this.myContent.classList.remove(this.collapsedClass);
193 this.clicker.classList.remove(this.collapsedClass);
194 }
195 }
196 },
197 draw: {
198 value: function() {
199 // Is the content area expanding/collapsing?
200 this.myContent.classList.remove(this.transitionClass);
201 if (this._isCollapsing) {
202
203 if (this.isAnimated) {
204 // Apply the transition class to the content.
205 this.myContent.classList.add(this.transitionClass);
206
207 // Add a handler for the end of the transition, so we can tidy things up after
208 // the transition completes
209 this.myContent.identifier = "myContent";
210 this.myContent.addEventListener("webkitTransitionEnd", this, false);
211
212 this.myContent.style.overflow = "hidden";
213 }
214
215 // Next, are we expanding or collapsing?
216 if (this.myContent.classList.contains(this.collapsedClass)) {
217 // It's already collapsed so we are expanding
218 this.myContent.style.height = this.contentHeight + "px";
219 this.isCollapsed = false;
220
221 } else {
222 // It's expanded so we are collapsing
223 this.myContent.style.height = "0px";
224 this.isCollapsed = true;
225
226 // Set the overflow to hidden if it isn't already
227 if (this._origOverflowValue !== "hidden") {
228 this.myContent.style.overflow = "hidden";
229 }
230 }