codekingpro/portable-devtools
114k
1/**2 * --------------------------------------------------------------------------3 * Bootstrap (v4.6.2): collapse.js4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)5 * --------------------------------------------------------------------------6 */7 8import $ from 'jquery'9import Util from './util'10 11/**12 * Constants13 */14 15const NAME = 'collapse'16const VERSION = '4.6.2'17const DATA_KEY = 'bs.collapse'18const EVENT_KEY = `.${DATA_KEY}`19const DATA_API_KEY = '.data-api'20const JQUERY_NO_CONFLICT = $.fn[NAME]21 22const CLASS_NAME_SHOW = 'show'23const CLASS_NAME_COLLAPSE = 'collapse'24const CLASS_NAME_COLLAPSING = 'collapsing'25const CLASS_NAME_COLLAPSED = 'collapsed'26 27const DIMENSION_WIDTH = 'width'28const DIMENSION_HEIGHT = 'height'29 30const EVENT_SHOW = `show${EVENT_KEY}`31const EVENT_SHOWN = `shown${EVENT_KEY}`32const EVENT_HIDE = `hide${EVENT_KEY}`33const EVENT_HIDDEN = `hidden${EVENT_KEY}`34const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`35 36const SELECTOR_ACTIVES = '.show, .collapsing'37const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]'38 39const Default = {40 toggle: true,41 parent: ''42}43 44const DefaultType = {45 toggle: 'boolean',46 parent: '(string|element)'47}48 49/**50 * Class definition51 */52 53class Collapse {54 constructor(element, config) {55 this._isTransitioning = false56 this._element = element57 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 = selector72 this._triggerArray.push(elem)73 }74 }75 76 this._parent = this._config.parent ? this._getParent() : null77 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 // Getters88 static get VERSION() {89 return VERSION90 }91 92 static get Default() {93 return Default94 }95 96 // Public97 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 return109 }110 111 let actives112 let activesData113 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.parent119 }120 121 return elem.classList.contains(CLASS_NAME_COLLAPSE)122 })123 124 if (actives.length === 0) {125 actives = null126 }127 }128 129 if (actives) {130 activesData = $(actives).not(this._selector).data(DATA_KEY)131 if (activesData && activesData._isTransitioning) {132 return133 }134 }135 136 const startEvent = $.Event(EVENT_SHOW)137 $(this._element).trigger(startEvent)138 if (startEvent.isDefaultPrevented()) {139 return140 }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] = 0156 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 return192 }193 194 const startEvent = $.Event(EVENT_HIDE)195 $(this._element).trigger(startEvent)196 if (startEvent.isDefaultPrevented()) {197 return198 }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.length211 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 = isTransitioning246 }247 248 dispose() {249 $.removeData(this._element, DATA_KEY)250 251 this._config = null252 this._parent = null253 this._element = null254 this._triggerArray = null255 this._isTransitioning = null256 }257 258 // Private259 _getConfig(config) {260 config = {261 ...Default,262 ...config263 }264 config.toggle = Boolean(config.toggle) // Coerce string values265 Util.typeCheckConfig(NAME, config, DefaultType)266 return config267 }268 269 _getDimension() {270 const hasWidth = $(this._element).hasClass(DIMENSION_WIDTH)271 return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT272 }273 274 _getParent() {275 let parent276 277 if (Util.isElement(this._config.parent)) {278 parent = this._config.parent279 280 // It's a jQuery object281 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 parent299 }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 // Static312 static _getTargetFromElement(element) {313 const selector = Util.getSelectorFromElement(element)314 return selector ? document.querySelector(selector) : null315 }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 = false329 }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 implementation349 */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 element353 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 * jQuery371 */372 373$.fn[NAME] = Collapse._jQueryInterface374$.fn[NAME].Constructor = Collapse375$.fn[NAME].noConflict = () => {376 $.fn[NAME] = JQUERY_NO_CONFLICT377 return Collapse._jQueryInterface378}379 380export default Collapse381 