Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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