]> git.ipfire.org Git - ipfire.org.git/blob - static/scss/bootstrap-4.0.0-alpha.6/js/src/collapse.js
.gitignore: Add .vscode
[ipfire.org.git] / static / scss / bootstrap-4.0.0-alpha.6 / js / src / collapse.js
1 import Util from './util'
2
3
4 /**
5 * --------------------------------------------------------------------------
6 * Bootstrap (v4.0.0-alpha.6): collapse.js
7 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8 * --------------------------------------------------------------------------
9 */
10
11 const Collapse = (($) => {
12
13
14 /**
15 * ------------------------------------------------------------------------
16 * Constants
17 * ------------------------------------------------------------------------
18 */
19
20 const NAME = 'collapse'
21 const VERSION = '4.0.0-alpha.6'
22 const DATA_KEY = 'bs.collapse'
23 const EVENT_KEY = `.${DATA_KEY}`
24 const DATA_API_KEY = '.data-api'
25 const JQUERY_NO_CONFLICT = $.fn[NAME]
26 const TRANSITION_DURATION = 600
27
28 const Default = {
29 toggle : true,
30 parent : ''
31 }
32
33 const DefaultType = {
34 toggle : 'boolean',
35 parent : 'string'
36 }
37
38 const Event = {
39 SHOW : `show${EVENT_KEY}`,
40 SHOWN : `shown${EVENT_KEY}`,
41 HIDE : `hide${EVENT_KEY}`,
42 HIDDEN : `hidden${EVENT_KEY}`,
43 CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
44 }
45
46 const ClassName = {
47 SHOW : 'show',
48 COLLAPSE : 'collapse',
49 COLLAPSING : 'collapsing',
50 COLLAPSED : 'collapsed'
51 }
52
53 const Dimension = {
54 WIDTH : 'width',
55 HEIGHT : 'height'
56 }
57
58 const Selector = {
59 ACTIVES : '.card > .show, .card > .collapsing',
60 DATA_TOGGLE : '[data-toggle="collapse"]'
61 }
62
63
64 /**
65 * ------------------------------------------------------------------------
66 * Class Definition
67 * ------------------------------------------------------------------------
68 */
69
70 class Collapse {
71
72 constructor(element, config) {
73 this._isTransitioning = false
74 this._element = element
75 this._config = this._getConfig(config)
76 this._triggerArray = $.makeArray($(
77 `[data-toggle="collapse"][href="#${element.id}"],` +
78 `[data-toggle="collapse"][data-target="#${element.id}"]`
79 ))
80
81 this._parent = this._config.parent ? this._getParent() : null
82
83 if (!this._config.parent) {
84 this._addAriaAndCollapsedClass(this._element, this._triggerArray)
85 }
86
87 if (this._config.toggle) {
88 this.toggle()
89 }
90 }
91
92
93 // getters
94
95 static get VERSION() {
96 return VERSION
97 }
98
99 static get Default() {
100 return Default
101 }
102
103
104 // public
105
106 toggle() {
107 if ($(this._element).hasClass(ClassName.SHOW)) {
108 this.hide()
109 } else {
110 this.show()
111 }
112 }
113
114 show() {
115 if (this._isTransitioning) {
116 throw new Error('Collapse is transitioning')
117 }
118
119 if ($(this._element).hasClass(ClassName.SHOW)) {
120 return
121 }
122
123 let actives
124 let activesData
125
126 if (this._parent) {
127 actives = $.makeArray($(this._parent).find(Selector.ACTIVES))
128 if (!actives.length) {
129 actives = null
130 }
131 }
132
133 if (actives) {
134 activesData = $(actives).data(DATA_KEY)
135 if (activesData && activesData._isTransitioning) {
136 return
137 }
138 }
139
140 const startEvent = $.Event(Event.SHOW)
141 $(this._element).trigger(startEvent)
142 if (startEvent.isDefaultPrevented()) {
143 return
144 }
145
146 if (actives) {
147 Collapse._jQueryInterface.call($(actives), 'hide')
148 if (!activesData) {
149 $(actives).data(DATA_KEY, null)
150 }
151 }
152
153 const dimension = this._getDimension()
154
155 $(this._element)
156 .removeClass(ClassName.COLLAPSE)
157 .addClass(ClassName.COLLAPSING)
158
159 this._element.style[dimension] = 0
160 this._element.setAttribute('aria-expanded', true)
161
162 if (this._triggerArray.length) {
163 $(this._triggerArray)
164 .removeClass(ClassName.COLLAPSED)
165 .attr('aria-expanded', true)
166 }
167
168 this.setTransitioning(true)
169
170 const complete = () => {
171 $(this._element)
172 .removeClass(ClassName.COLLAPSING)
173 .addClass(ClassName.COLLAPSE)
174 .addClass(ClassName.SHOW)
175
176 this._element.style[dimension] = ''
177
178 this.setTransitioning(false)
179
180 $(this._element).trigger(Event.SHOWN)
181 }
182
183 if (!Util.supportsTransitionEnd()) {
184 complete()
185 return
186 }
187
188 const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
189 const scrollSize = `scroll${capitalizedDimension}`
190
191 $(this._element)
192 .one(Util.TRANSITION_END, complete)
193 .emulateTransitionEnd(TRANSITION_DURATION)
194
195 this._element.style[dimension] = `${this._element[scrollSize]}px`
196 }
197
198 hide() {
199 if (this._isTransitioning) {
200 throw new Error('Collapse is transitioning')
201 }
202
203 if (!$(this._element).hasClass(ClassName.SHOW)) {
204 return
205 }
206
207 const startEvent = $.Event(Event.HIDE)
208 $(this._element).trigger(startEvent)
209 if (startEvent.isDefaultPrevented()) {
210 return
211 }
212
213 const dimension = this._getDimension()
214 const offsetDimension = dimension === Dimension.WIDTH ?
215 'offsetWidth' : 'offsetHeight'
216
217 this._element.style[dimension] = `${this._element[offsetDimension]}px`
218
219 Util.reflow(this._element)
220
221 $(this._element)
222 .addClass(ClassName.COLLAPSING)
223 .removeClass(ClassName.COLLAPSE)
224 .removeClass(ClassName.SHOW)
225
226 this._element.setAttribute('aria-expanded', false)
227
228 if (this._triggerArray.length) {
229 $(this._triggerArray)
230 .addClass(ClassName.COLLAPSED)
231 .attr('aria-expanded', false)
232 }
233
234 this.setTransitioning(true)
235
236 const complete = () => {
237 this.setTransitioning(false)
238 $(this._element)
239 .removeClass(ClassName.COLLAPSING)
240 .addClass(ClassName.COLLAPSE)
241 .trigger(Event.HIDDEN)
242 }
243
244 this._element.style[dimension] = ''
245
246 if (!Util.supportsTransitionEnd()) {
247 complete()
248 return
249 }
250
251 $(this._element)
252 .one(Util.TRANSITION_END, complete)
253 .emulateTransitionEnd(TRANSITION_DURATION)
254 }
255
256 setTransitioning(isTransitioning) {
257 this._isTransitioning = isTransitioning
258 }
259
260 dispose() {
261 $.removeData(this._element, DATA_KEY)
262
263 this._config = null
264 this._parent = null
265 this._element = null
266 this._triggerArray = null
267 this._isTransitioning = null
268 }
269
270
271 // private
272
273 _getConfig(config) {
274 config = $.extend({}, Default, config)
275 config.toggle = Boolean(config.toggle) // coerce string values
276 Util.typeCheckConfig(NAME, config, DefaultType)
277 return config
278 }
279
280 _getDimension() {
281 const hasWidth = $(this._element).hasClass(Dimension.WIDTH)
282 return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
283 }
284
285 _getParent() {
286 const parent = $(this._config.parent)[0]
287 const selector =
288 `[data-toggle="collapse"][data-parent="${this._config.parent}"]`
289
290 $(parent).find(selector).each((i, element) => {
291 this._addAriaAndCollapsedClass(
292 Collapse._getTargetFromElement(element),
293 [element]
294 )
295 })
296
297 return parent
298 }
299
300 _addAriaAndCollapsedClass(element, triggerArray) {
301 if (element) {
302 const isOpen = $(element).hasClass(ClassName.SHOW)
303 element.setAttribute('aria-expanded', isOpen)
304
305 if (triggerArray.length) {
306 $(triggerArray)
307 .toggleClass(ClassName.COLLAPSED, !isOpen)
308 .attr('aria-expanded', isOpen)
309 }
310 }
311 }
312
313
314 // static
315
316 static _getTargetFromElement(element) {
317 const selector = Util.getSelectorFromElement(element)
318 return selector ? $(selector)[0] : null
319 }
320
321 static _jQueryInterface(config) {
322 return this.each(function () {
323 const $this = $(this)
324 let data = $this.data(DATA_KEY)
325 const _config = $.extend(
326 {},
327 Default,
328 $this.data(),
329 typeof config === 'object' && config
330 )
331
332 if (!data && _config.toggle && /show|hide/.test(config)) {
333 _config.toggle = false
334 }
335
336 if (!data) {
337 data = new Collapse(this, _config)
338 $this.data(DATA_KEY, data)
339 }
340
341 if (typeof config === 'string') {
342 if (data[config] === undefined) {
343 throw new Error(`No method named "${config}"`)
344 }
345 data[config]()
346 }
347 })
348 }
349
350 }
351
352
353 /**
354 * ------------------------------------------------------------------------
355 * Data Api implementation
356 * ------------------------------------------------------------------------
357 */
358
359 $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
360 event.preventDefault()
361
362 const target = Collapse._getTargetFromElement(this)
363 const data = $(target).data(DATA_KEY)
364 const config = data ? 'toggle' : $(this).data()
365
366 Collapse._jQueryInterface.call($(target), config)
367 })
368
369
370 /**
371 * ------------------------------------------------------------------------
372 * jQuery
373 * ------------------------------------------------------------------------
374 */
375
376 $.fn[NAME] = Collapse._jQueryInterface
377 $.fn[NAME].Constructor = Collapse
378 $.fn[NAME].noConflict = function () {
379 $.fn[NAME] = JQUERY_NO_CONFLICT
380 return Collapse._jQueryInterface
381 }
382
383 return Collapse
384
385 })(jQuery)
386
387 export default Collapse