source: imaps-frontend/node_modules/bootstrap/js/src/collapse.js

main
Last change on this file was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 7.4 KB
Line 
1/**
2 * --------------------------------------------------------------------------
3 * Bootstrap collapse.js
4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
5 * --------------------------------------------------------------------------
6 */
7
8import BaseComponent from './base-component.js'
9import EventHandler from './dom/event-handler.js'
10import SelectorEngine from './dom/selector-engine.js'
11import {
12 defineJQueryPlugin,
13 getElement,
14 reflow
15} from './util/index.js'
16
17/**
18 * Constants
19 */
20
21const NAME = 'collapse'
22const DATA_KEY = 'bs.collapse'
23const EVENT_KEY = `.${DATA_KEY}`
24const DATA_API_KEY = '.data-api'
25
26const EVENT_SHOW = `show${EVENT_KEY}`
27const EVENT_SHOWN = `shown${EVENT_KEY}`
28const EVENT_HIDE = `hide${EVENT_KEY}`
29const EVENT_HIDDEN = `hidden${EVENT_KEY}`
30const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
31
32const CLASS_NAME_SHOW = 'show'
33const CLASS_NAME_COLLAPSE = 'collapse'
34const CLASS_NAME_COLLAPSING = 'collapsing'
35const CLASS_NAME_COLLAPSED = 'collapsed'
36const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`
37const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'
38
39const WIDTH = 'width'
40const HEIGHT = 'height'
41
42const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'
43const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]'
44
45const Default = {
46 parent: null,
47 toggle: true
48}
49
50const DefaultType = {
51 parent: '(null|element)',
52 toggle: 'boolean'
53}
54
55/**
56 * Class definition
57 */
58
59class Collapse extends BaseComponent {
60 constructor(element, config) {
61 super(element, config)
62
63 this._isTransitioning = false
64 this._triggerArray = []
65
66 const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
67
68 for (const elem of toggleList) {
69 const selector = SelectorEngine.getSelectorFromElement(elem)
70 const filterElement = SelectorEngine.find(selector)
71 .filter(foundElement => foundElement === this._element)
72
73 if (selector !== null && filterElement.length) {
74 this._triggerArray.push(elem)
75 }
76 }
77
78 this._initializeChildren()
79
80 if (!this._config.parent) {
81 this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())
82 }
83
84 if (this._config.toggle) {
85 this.toggle()
86 }
87 }
88
89 // Getters
90 static get Default() {
91 return Default
92 }
93
94 static get DefaultType() {
95 return DefaultType
96 }
97
98 static get NAME() {
99 return NAME
100 }
101
102 // Public
103 toggle() {
104 if (this._isShown()) {
105 this.hide()
106 } else {
107 this.show()
108 }
109 }
110
111 show() {
112 if (this._isTransitioning || this._isShown()) {
113 return
114 }
115
116 let activeChildren = []
117
118 // find active children
119 if (this._config.parent) {
120 activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)
121 .filter(element => element !== this._element)
122 .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))
123 }
124
125 if (activeChildren.length && activeChildren[0]._isTransitioning) {
126 return
127 }
128
129 const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)
130 if (startEvent.defaultPrevented) {
131 return
132 }
133
134 for (const activeInstance of activeChildren) {
135 activeInstance.hide()
136 }
137
138 const dimension = this._getDimension()
139
140 this._element.classList.remove(CLASS_NAME_COLLAPSE)
141 this._element.classList.add(CLASS_NAME_COLLAPSING)
142
143 this._element.style[dimension] = 0
144
145 this._addAriaAndCollapsedClass(this._triggerArray, true)
146 this._isTransitioning = true
147
148 const complete = () => {
149 this._isTransitioning = false
150
151 this._element.classList.remove(CLASS_NAME_COLLAPSING)
152 this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)
153
154 this._element.style[dimension] = ''
155
156 EventHandler.trigger(this._element, EVENT_SHOWN)
157 }
158
159 const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
160 const scrollSize = `scroll${capitalizedDimension}`
161
162 this._queueCallback(complete, this._element, true)
163 this._element.style[dimension] = `${this._element[scrollSize]}px`
164 }
165
166 hide() {
167 if (this._isTransitioning || !this._isShown()) {
168 return
169 }
170
171 const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)
172 if (startEvent.defaultPrevented) {
173 return
174 }
175
176 const dimension = this._getDimension()
177
178 this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
179
180 reflow(this._element)
181
182 this._element.classList.add(CLASS_NAME_COLLAPSING)
183 this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)
184
185 for (const trigger of this._triggerArray) {
186 const element = SelectorEngine.getElementFromSelector(trigger)
187
188 if (element && !this._isShown(element)) {
189 this._addAriaAndCollapsedClass([trigger], false)
190 }
191 }
192
193 this._isTransitioning = true
194
195 const complete = () => {
196 this._isTransitioning = false
197 this._element.classList.remove(CLASS_NAME_COLLAPSING)
198 this._element.classList.add(CLASS_NAME_COLLAPSE)
199 EventHandler.trigger(this._element, EVENT_HIDDEN)
200 }
201
202 this._element.style[dimension] = ''
203
204 this._queueCallback(complete, this._element, true)
205 }
206
207 _isShown(element = this._element) {
208 return element.classList.contains(CLASS_NAME_SHOW)
209 }
210
211 // Private
212 _configAfterMerge(config) {
213 config.toggle = Boolean(config.toggle) // Coerce string values
214 config.parent = getElement(config.parent)
215 return config
216 }
217
218 _getDimension() {
219 return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT
220 }
221
222 _initializeChildren() {
223 if (!this._config.parent) {
224 return
225 }
226
227 const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)
228
229 for (const element of children) {
230 const selected = SelectorEngine.getElementFromSelector(element)
231
232 if (selected) {
233 this._addAriaAndCollapsedClass([element], this._isShown(selected))
234 }
235 }
236 }
237
238 _getFirstLevelChildren(selector) {
239 const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)
240 // remove children if greater depth
241 return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))
242 }
243
244 _addAriaAndCollapsedClass(triggerArray, isOpen) {
245 if (!triggerArray.length) {
246 return
247 }
248
249 for (const element of triggerArray) {
250 element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)
251 element.setAttribute('aria-expanded', isOpen)
252 }
253 }
254
255 // Static
256 static jQueryInterface(config) {
257 const _config = {}
258 if (typeof config === 'string' && /show|hide/.test(config)) {
259 _config.toggle = false
260 }
261
262 return this.each(function () {
263 const data = Collapse.getOrCreateInstance(this, _config)
264
265 if (typeof config === 'string') {
266 if (typeof data[config] === 'undefined') {
267 throw new TypeError(`No method named "${config}"`)
268 }
269
270 data[config]()
271 }
272 })
273 }
274}
275
276/**
277 * Data API implementation
278 */
279
280EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
281 // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
282 if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {
283 event.preventDefault()
284 }
285
286 for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
287 Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
288 }
289})
290
291/**
292 * jQuery
293 */
294
295defineJQueryPlugin(Collapse)
296
297export default Collapse
Note: See TracBrowser for help on using the repository browser.