2 import Util from './util'
5 * --------------------------------------------------------------------------
6 * Bootstrap (v4.1.3): collapse.js
7 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8 * --------------------------------------------------------------------------
11 const Collapse = (($) => {
13 * ------------------------------------------------------------------------
15 * ------------------------------------------------------------------------
18 const NAME = 'collapse'
19 const VERSION = '4.1.3'
20 const DATA_KEY = 'bs.collapse'
21 const EVENT_KEY = `.${DATA_KEY}`
22 const DATA_API_KEY = '.data-api'
23 const JQUERY_NO_CONFLICT = $.fn[NAME]
32 parent : '(string|element)'
36 SHOW : `show${EVENT_KEY}`,
37 SHOWN : `shown${EVENT_KEY}`,
38 HIDE : `hide${EVENT_KEY}`,
39 HIDDEN : `hidden${EVENT_KEY}`,
40 CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
45 COLLAPSE : 'collapse',
46 COLLAPSING : 'collapsing',
47 COLLAPSED : 'collapsed'
56 ACTIVES : '.show, .collapsing',
57 DATA_TOGGLE : '[data-toggle="collapse"]'
61 * ------------------------------------------------------------------------
63 * ------------------------------------------------------------------------
67 constructor(element, config) {
68 this._isTransitioning = false
69 this._element = element
70 this._config = this._getConfig(config)
71 this._triggerArray = $.makeArray(document.querySelectorAll(
72 `[data-toggle="collapse"][href="#${element.id}"],` +
73 `[data-toggle="collapse"][data-target="#${element.id}"]`
75 const toggleList = [].slice.call(document.querySelectorAll(Selector.DATA_TOGGLE))
76 for (let i = 0, len = toggleList.length; i < len; i++) {
77 const elem = toggleList[i]
78 const selector = Util.getSelectorFromElement(elem)
79 const filterElement = [].slice.call(document.querySelectorAll(selector))
80 .filter((foundElem) => foundElem === element)
82 if (selector !== null && filterElement.length > 0) {
83 this._selector = selector
84 this._triggerArray.push(elem)
88 this._parent = this._config.parent ? this._getParent() : null
90 if (!this._config.parent) {
91 this._addAriaAndCollapsedClass(this._element, this._triggerArray)
94 if (this._config.toggle) {
101 static get VERSION() {
105 static get Default() {
112 if ($(this._element).hasClass(ClassName.SHOW)) {
120 if (this._isTransitioning ||
121 $(this._element).hasClass(ClassName.SHOW)) {
129 actives = [].slice.call(this._parent.querySelectorAll(Selector.ACTIVES))
130 .filter((elem) => elem.getAttribute('data-parent') === this._config.parent)
132 if (actives.length === 0) {
138 activesData = $(actives).not(this._selector).data(DATA_KEY)
139 if (activesData && activesData._isTransitioning) {
144 const startEvent = $.Event(Event.SHOW)
145 $(this._element).trigger(startEvent)
146 if (startEvent.isDefaultPrevented()) {
151 Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide')
153 $(actives).data(DATA_KEY, null)
157 const dimension = this._getDimension()
160 .removeClass(ClassName.COLLAPSE)
161 .addClass(ClassName.COLLAPSING)
163 this._element.style[dimension] = 0
165 if (this._triggerArray.length) {
166 $(this._triggerArray)
167 .removeClass(ClassName.COLLAPSED)
168 .attr('aria-expanded', true)
171 this.setTransitioning(true)
173 const complete = () => {
175 .removeClass(ClassName.COLLAPSING)
176 .addClass(ClassName.COLLAPSE)
177 .addClass(ClassName.SHOW)
179 this._element.style[dimension] = ''
181 this.setTransitioning(false)
183 $(this._element).trigger(Event.SHOWN)
186 const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
187 const scrollSize = `scroll${capitalizedDimension}`
188 const transitionDuration = Util.getTransitionDurationFromElement(this._element)
191 .one(Util.TRANSITION_END, complete)
192 .emulateTransitionEnd(transitionDuration)
194 this._element.style[dimension] = `${this._element[scrollSize]}px`
198 if (this._isTransitioning ||
199 !$(this._element).hasClass(ClassName.SHOW)) {
203 const startEvent = $.Event(Event.HIDE)
204 $(this._element).trigger(startEvent)
205 if (startEvent.isDefaultPrevented()) {
209 const dimension = this._getDimension()
211 this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
213 Util.reflow(this._element)
216 .addClass(ClassName.COLLAPSING)
217 .removeClass(ClassName.COLLAPSE)
218 .removeClass(ClassName.SHOW)
220 const triggerArrayLength = this._triggerArray.length
221 if (triggerArrayLength > 0) {
222 for (let i = 0; i < triggerArrayLength; i++) {
223 const trigger = this._triggerArray[i]
224 const selector = Util.getSelectorFromElement(trigger)
225 if (selector !== null) {
226 const $elem = $([].slice.call(document.querySelectorAll(selector)))
227 if (!$elem.hasClass(ClassName.SHOW)) {
228 $(trigger).addClass(ClassName.COLLAPSED)
229 .attr('aria-expanded', false)
235 this.setTransitioning(true)
237 const complete = () => {
238 this.setTransitioning(false)
240 .removeClass(ClassName.COLLAPSING)
241 .addClass(ClassName.COLLAPSE)
242 .trigger(Event.HIDDEN)
245 this._element.style[dimension] = ''
246 const transitionDuration = Util.getTransitionDurationFromElement(this._element)
249 .one(Util.TRANSITION_END, complete)
250 .emulateTransitionEnd(transitionDuration)
253 setTransitioning(isTransitioning) {
254 this._isTransitioning = isTransitioning
258 $.removeData(this._element, DATA_KEY)
263 this._triggerArray = null
264 this._isTransitioning = null
274 config.toggle = Boolean(config.toggle) // Coerce string values
275 Util.typeCheckConfig(NAME, config, DefaultType)
280 const hasWidth = $(this._element).hasClass(Dimension.WIDTH)
281 return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT
286 if (Util.isElement(this._config.parent)) {
287 parent = this._config.parent
289 // It's a jQuery object
290 if (typeof this._config.parent.jquery !== 'undefined') {
291 parent = this._config.parent[0]
294 parent = document.querySelector(this._config.parent)
298 `[data-toggle="collapse"][data-parent="${this._config.parent}"]`
300 const children = [].slice.call(parent.querySelectorAll(selector))
301 $(children).each((i, element) => {
302 this._addAriaAndCollapsedClass(
303 Collapse._getTargetFromElement(element),
311 _addAriaAndCollapsedClass(element, triggerArray) {
313 const isOpen = $(element).hasClass(ClassName.SHOW)
315 if (triggerArray.length) {
317 .toggleClass(ClassName.COLLAPSED, !isOpen)
318 .attr('aria-expanded', isOpen)
325 static _getTargetFromElement(element) {
326 const selector = Util.getSelectorFromElement(element)
327 return selector ? document.querySelector(selector) : null
330 static _jQueryInterface(config) {
331 return this.each(function () {
332 const $this = $(this)
333 let data = $this.data(DATA_KEY)
337 ...typeof config === 'object' && config ? config : {}
340 if (!data && _config.toggle && /show|hide/.test(config)) {
341 _config.toggle = false
345 data = new Collapse(this, _config)
346 $this.data(DATA_KEY, data)
349 if (typeof config === 'string') {
350 if (typeof data[config] === 'undefined') {
351 throw new TypeError(`No method named "${config}"`)
360 * ------------------------------------------------------------------------
361 * Data Api implementation
362 * ------------------------------------------------------------------------
365 $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
366 // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
367 if (event.currentTarget.tagName === 'A') {
368 event.preventDefault()
371 const $trigger = $(this)
372 const selector = Util.getSelectorFromElement(this)
373 const selectors = [].slice.call(document.querySelectorAll(selector))
374 $(selectors).each(function () {
375 const $target = $(this)
376 const data = $target.data(DATA_KEY)
377 const config = data ? 'toggle' : $trigger.data()
378 Collapse._jQueryInterface.call($target, config)
383 * ------------------------------------------------------------------------
385 * ------------------------------------------------------------------------
388 $.fn[NAME] = Collapse._jQueryInterface
389 $.fn[NAME].Constructor = Collapse
390 $.fn[NAME].noConflict = function () {
391 $.fn[NAME] = JQUERY_NO_CONFLICT
392 return Collapse._jQueryInterface
398 export default Collapse