talos

Форк
0
380 строк · 9.5 Кб
1
/**
2
 * --------------------------------------------------------------------------
3
 * Bootstrap (v4.6.1): 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.1'
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
381

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.