You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

495 lines
15 KiB

  1. /*!
  2. * Bootstrap collapse.js v5.1.3 (https://getbootstrap.com/)
  3. * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. */
  6. (function (global, factory) {
  7. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
  8. typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
  10. })(this, (function (Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
  11. const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
  12. const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
  13. const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
  14. const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
  15. const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
  16. const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
  17. /**
  18. * --------------------------------------------------------------------------
  19. * Bootstrap (v5.1.3): util/index.js
  20. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  21. * --------------------------------------------------------------------------
  22. */
  23. const toType = obj => {
  24. if (obj === null || obj === undefined) {
  25. return `${obj}`;
  26. }
  27. return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
  28. };
  29. const getSelector = element => {
  30. let selector = element.getAttribute('data-bs-target');
  31. if (!selector || selector === '#') {
  32. let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
  33. // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
  34. // `document.querySelector` will rightfully complain it is invalid.
  35. // See https://github.com/twbs/bootstrap/issues/32273
  36. if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
  37. return null;
  38. } // Just in case some CMS puts out a full URL with the anchor appended
  39. if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
  40. hrefAttr = `#${hrefAttr.split('#')[1]}`;
  41. }
  42. selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
  43. }
  44. return selector;
  45. };
  46. const getSelectorFromElement = element => {
  47. const selector = getSelector(element);
  48. if (selector) {
  49. return document.querySelector(selector) ? selector : null;
  50. }
  51. return null;
  52. };
  53. const getElementFromSelector = element => {
  54. const selector = getSelector(element);
  55. return selector ? document.querySelector(selector) : null;
  56. };
  57. const isElement = obj => {
  58. if (!obj || typeof obj !== 'object') {
  59. return false;
  60. }
  61. if (typeof obj.jquery !== 'undefined') {
  62. obj = obj[0];
  63. }
  64. return typeof obj.nodeType !== 'undefined';
  65. };
  66. const getElement = obj => {
  67. if (isElement(obj)) {
  68. // it's a jQuery object or a node element
  69. return obj.jquery ? obj[0] : obj;
  70. }
  71. if (typeof obj === 'string' && obj.length > 0) {
  72. return document.querySelector(obj);
  73. }
  74. return null;
  75. };
  76. const typeCheckConfig = (componentName, config, configTypes) => {
  77. Object.keys(configTypes).forEach(property => {
  78. const expectedTypes = configTypes[property];
  79. const value = config[property];
  80. const valueType = value && isElement(value) ? 'element' : toType(value);
  81. if (!new RegExp(expectedTypes).test(valueType)) {
  82. throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
  83. }
  84. });
  85. };
  86. /**
  87. * Trick to restart an element's animation
  88. *
  89. * @param {HTMLElement} element
  90. * @return void
  91. *
  92. * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
  93. */
  94. const reflow = element => {
  95. // eslint-disable-next-line no-unused-expressions
  96. element.offsetHeight;
  97. };
  98. const getjQuery = () => {
  99. const {
  100. jQuery
  101. } = window;
  102. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  103. return jQuery;
  104. }
  105. return null;
  106. };
  107. const DOMContentLoadedCallbacks = [];
  108. const onDOMContentLoaded = callback => {
  109. if (document.readyState === 'loading') {
  110. // add listener on the first call when the document is in loading state
  111. if (!DOMContentLoadedCallbacks.length) {
  112. document.addEventListener('DOMContentLoaded', () => {
  113. DOMContentLoadedCallbacks.forEach(callback => callback());
  114. });
  115. }
  116. DOMContentLoadedCallbacks.push(callback);
  117. } else {
  118. callback();
  119. }
  120. };
  121. const defineJQueryPlugin = plugin => {
  122. onDOMContentLoaded(() => {
  123. const $ = getjQuery();
  124. /* istanbul ignore if */
  125. if ($) {
  126. const name = plugin.NAME;
  127. const JQUERY_NO_CONFLICT = $.fn[name];
  128. $.fn[name] = plugin.jQueryInterface;
  129. $.fn[name].Constructor = plugin;
  130. $.fn[name].noConflict = () => {
  131. $.fn[name] = JQUERY_NO_CONFLICT;
  132. return plugin.jQueryInterface;
  133. };
  134. }
  135. });
  136. };
  137. /**
  138. * --------------------------------------------------------------------------
  139. * Bootstrap (v5.1.3): collapse.js
  140. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  141. * --------------------------------------------------------------------------
  142. */
  143. /**
  144. * ------------------------------------------------------------------------
  145. * Constants
  146. * ------------------------------------------------------------------------
  147. */
  148. const NAME = 'collapse';
  149. const DATA_KEY = 'bs.collapse';
  150. const EVENT_KEY = `.${DATA_KEY}`;
  151. const DATA_API_KEY = '.data-api';
  152. const Default = {
  153. toggle: true,
  154. parent: null
  155. };
  156. const DefaultType = {
  157. toggle: 'boolean',
  158. parent: '(null|element)'
  159. };
  160. const EVENT_SHOW = `show${EVENT_KEY}`;
  161. const EVENT_SHOWN = `shown${EVENT_KEY}`;
  162. const EVENT_HIDE = `hide${EVENT_KEY}`;
  163. const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
  164. const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
  165. const CLASS_NAME_SHOW = 'show';
  166. const CLASS_NAME_COLLAPSE = 'collapse';
  167. const CLASS_NAME_COLLAPSING = 'collapsing';
  168. const CLASS_NAME_COLLAPSED = 'collapsed';
  169. const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`;
  170. const CLASS_NAME_HORIZONTAL = 'collapse-horizontal';
  171. const WIDTH = 'width';
  172. const HEIGHT = 'height';
  173. const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';
  174. const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]';
  175. /**
  176. * ------------------------------------------------------------------------
  177. * Class Definition
  178. * ------------------------------------------------------------------------
  179. */
  180. class Collapse extends BaseComponent__default.default {
  181. constructor(element, config) {
  182. super(element);
  183. this._isTransitioning = false;
  184. this._config = this._getConfig(config);
  185. this._triggerArray = [];
  186. const toggleList = SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE);
  187. for (let i = 0, len = toggleList.length; i < len; i++) {
  188. const elem = toggleList[i];
  189. const selector = getSelectorFromElement(elem);
  190. const filterElement = SelectorEngine__default.default.find(selector).filter(foundElem => foundElem === this._element);
  191. if (selector !== null && filterElement.length) {
  192. this._selector = selector;
  193. this._triggerArray.push(elem);
  194. }
  195. }
  196. this._initializeChildren();
  197. if (!this._config.parent) {
  198. this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());
  199. }
  200. if (this._config.toggle) {
  201. this.toggle();
  202. }
  203. } // Getters
  204. static get Default() {
  205. return Default;
  206. }
  207. static get NAME() {
  208. return NAME;
  209. } // Public
  210. toggle() {
  211. if (this._isShown()) {
  212. this.hide();
  213. } else {
  214. this.show();
  215. }
  216. }
  217. show() {
  218. if (this._isTransitioning || this._isShown()) {
  219. return;
  220. }
  221. let actives = [];
  222. let activesData;
  223. if (this._config.parent) {
  224. const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
  225. actives = SelectorEngine__default.default.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)); // remove children if greater depth
  226. }
  227. const container = SelectorEngine__default.default.findOne(this._selector);
  228. if (actives.length) {
  229. const tempActiveData = actives.find(elem => container !== elem);
  230. activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null;
  231. if (activesData && activesData._isTransitioning) {
  232. return;
  233. }
  234. }
  235. const startEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW);
  236. if (startEvent.defaultPrevented) {
  237. return;
  238. }
  239. actives.forEach(elemActive => {
  240. if (container !== elemActive) {
  241. Collapse.getOrCreateInstance(elemActive, {
  242. toggle: false
  243. }).hide();
  244. }
  245. if (!activesData) {
  246. Data__default.default.set(elemActive, DATA_KEY, null);
  247. }
  248. });
  249. const dimension = this._getDimension();
  250. this._element.classList.remove(CLASS_NAME_COLLAPSE);
  251. this._element.classList.add(CLASS_NAME_COLLAPSING);
  252. this._element.style[dimension] = 0;
  253. this._addAriaAndCollapsedClass(this._triggerArray, true);
  254. this._isTransitioning = true;
  255. const complete = () => {
  256. this._isTransitioning = false;
  257. this._element.classList.remove(CLASS_NAME_COLLAPSING);
  258. this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
  259. this._element.style[dimension] = '';
  260. EventHandler__default.default.trigger(this._element, EVENT_SHOWN);
  261. };
  262. const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
  263. const scrollSize = `scroll${capitalizedDimension}`;
  264. this._queueCallback(complete, this._element, true);
  265. this._element.style[dimension] = `${this._element[scrollSize]}px`;
  266. }
  267. hide() {
  268. if (this._isTransitioning || !this._isShown()) {
  269. return;
  270. }
  271. const startEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
  272. if (startEvent.defaultPrevented) {
  273. return;
  274. }
  275. const dimension = this._getDimension();
  276. this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;
  277. reflow(this._element);
  278. this._element.classList.add(CLASS_NAME_COLLAPSING);
  279. this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
  280. const triggerArrayLength = this._triggerArray.length;
  281. for (let i = 0; i < triggerArrayLength; i++) {
  282. const trigger = this._triggerArray[i];
  283. const elem = getElementFromSelector(trigger);
  284. if (elem && !this._isShown(elem)) {
  285. this._addAriaAndCollapsedClass([trigger], false);
  286. }
  287. }
  288. this._isTransitioning = true;
  289. const complete = () => {
  290. this._isTransitioning = false;
  291. this._element.classList.remove(CLASS_NAME_COLLAPSING);
  292. this._element.classList.add(CLASS_NAME_COLLAPSE);
  293. EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
  294. };
  295. this._element.style[dimension] = '';
  296. this._queueCallback(complete, this._element, true);
  297. }
  298. _isShown(element = this._element) {
  299. return element.classList.contains(CLASS_NAME_SHOW);
  300. } // Private
  301. _getConfig(config) {
  302. config = { ...Default,
  303. ...Manipulator__default.default.getDataAttributes(this._element),
  304. ...config
  305. };
  306. config.toggle = Boolean(config.toggle); // Coerce string values
  307. config.parent = getElement(config.parent);
  308. typeCheckConfig(NAME, config, DefaultType);
  309. return config;
  310. }
  311. _getDimension() {
  312. return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;
  313. }
  314. _initializeChildren() {
  315. if (!this._config.parent) {
  316. return;
  317. }
  318. const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
  319. SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem)).forEach(element => {
  320. const selected = getElementFromSelector(element);
  321. if (selected) {
  322. this._addAriaAndCollapsedClass([element], this._isShown(selected));
  323. }
  324. });
  325. }
  326. _addAriaAndCollapsedClass(triggerArray, isOpen) {
  327. if (!triggerArray.length) {
  328. return;
  329. }
  330. triggerArray.forEach(elem => {
  331. if (isOpen) {
  332. elem.classList.remove(CLASS_NAME_COLLAPSED);
  333. } else {
  334. elem.classList.add(CLASS_NAME_COLLAPSED);
  335. }
  336. elem.setAttribute('aria-expanded', isOpen);
  337. });
  338. } // Static
  339. static jQueryInterface(config) {
  340. return this.each(function () {
  341. const _config = {};
  342. if (typeof config === 'string' && /show|hide/.test(config)) {
  343. _config.toggle = false;
  344. }
  345. const data = Collapse.getOrCreateInstance(this, _config);
  346. if (typeof config === 'string') {
  347. if (typeof data[config] === 'undefined') {
  348. throw new TypeError(`No method named "${config}"`);
  349. }
  350. data[config]();
  351. }
  352. });
  353. }
  354. }
  355. /**
  356. * ------------------------------------------------------------------------
  357. * Data Api implementation
  358. * ------------------------------------------------------------------------
  359. */
  360. EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  361. // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
  362. if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {
  363. event.preventDefault();
  364. }
  365. const selector = getSelectorFromElement(this);
  366. const selectorElements = SelectorEngine__default.default.find(selector);
  367. selectorElements.forEach(element => {
  368. Collapse.getOrCreateInstance(element, {
  369. toggle: false
  370. }).toggle();
  371. });
  372. });
  373. /**
  374. * ------------------------------------------------------------------------
  375. * jQuery
  376. * ------------------------------------------------------------------------
  377. * add .Collapse to jQuery only if jQuery is present
  378. */
  379. defineJQueryPlugin(Collapse);
  380. return Collapse;
  381. }));
  382. //# sourceMappingURL=collapse.js.map