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.

333 lines
11 KiB

  1. /*!
  2. * Bootstrap tab.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/event-handler.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
  8. typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/selector-engine', './base-component'], factory) :
  9. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tab = factory(global.EventHandler, global.SelectorEngine, global.Base));
  10. })(this, (function (EventHandler, SelectorEngine, BaseComponent) { 'use strict';
  11. const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
  12. const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
  13. const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
  14. const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
  15. /**
  16. * --------------------------------------------------------------------------
  17. * Bootstrap (v5.1.3): util/index.js
  18. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  19. * --------------------------------------------------------------------------
  20. */
  21. const getSelector = element => {
  22. let selector = element.getAttribute('data-bs-target');
  23. if (!selector || selector === '#') {
  24. let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
  25. // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
  26. // `document.querySelector` will rightfully complain it is invalid.
  27. // See https://github.com/twbs/bootstrap/issues/32273
  28. if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
  29. return null;
  30. } // Just in case some CMS puts out a full URL with the anchor appended
  31. if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
  32. hrefAttr = `#${hrefAttr.split('#')[1]}`;
  33. }
  34. selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
  35. }
  36. return selector;
  37. };
  38. const getElementFromSelector = element => {
  39. const selector = getSelector(element);
  40. return selector ? document.querySelector(selector) : null;
  41. };
  42. const isDisabled = element => {
  43. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  44. return true;
  45. }
  46. if (element.classList.contains('disabled')) {
  47. return true;
  48. }
  49. if (typeof element.disabled !== 'undefined') {
  50. return element.disabled;
  51. }
  52. return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
  53. };
  54. /**
  55. * Trick to restart an element's animation
  56. *
  57. * @param {HTMLElement} element
  58. * @return void
  59. *
  60. * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
  61. */
  62. const reflow = element => {
  63. // eslint-disable-next-line no-unused-expressions
  64. element.offsetHeight;
  65. };
  66. const getjQuery = () => {
  67. const {
  68. jQuery
  69. } = window;
  70. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  71. return jQuery;
  72. }
  73. return null;
  74. };
  75. const DOMContentLoadedCallbacks = [];
  76. const onDOMContentLoaded = callback => {
  77. if (document.readyState === 'loading') {
  78. // add listener on the first call when the document is in loading state
  79. if (!DOMContentLoadedCallbacks.length) {
  80. document.addEventListener('DOMContentLoaded', () => {
  81. DOMContentLoadedCallbacks.forEach(callback => callback());
  82. });
  83. }
  84. DOMContentLoadedCallbacks.push(callback);
  85. } else {
  86. callback();
  87. }
  88. };
  89. const defineJQueryPlugin = plugin => {
  90. onDOMContentLoaded(() => {
  91. const $ = getjQuery();
  92. /* istanbul ignore if */
  93. if ($) {
  94. const name = plugin.NAME;
  95. const JQUERY_NO_CONFLICT = $.fn[name];
  96. $.fn[name] = plugin.jQueryInterface;
  97. $.fn[name].Constructor = plugin;
  98. $.fn[name].noConflict = () => {
  99. $.fn[name] = JQUERY_NO_CONFLICT;
  100. return plugin.jQueryInterface;
  101. };
  102. }
  103. });
  104. };
  105. /**
  106. * --------------------------------------------------------------------------
  107. * Bootstrap (v5.1.3): tab.js
  108. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  109. * --------------------------------------------------------------------------
  110. */
  111. /**
  112. * ------------------------------------------------------------------------
  113. * Constants
  114. * ------------------------------------------------------------------------
  115. */
  116. const NAME = 'tab';
  117. const DATA_KEY = 'bs.tab';
  118. const EVENT_KEY = `.${DATA_KEY}`;
  119. const DATA_API_KEY = '.data-api';
  120. const EVENT_HIDE = `hide${EVENT_KEY}`;
  121. const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
  122. const EVENT_SHOW = `show${EVENT_KEY}`;
  123. const EVENT_SHOWN = `shown${EVENT_KEY}`;
  124. const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
  125. const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu';
  126. const CLASS_NAME_ACTIVE = 'active';
  127. const CLASS_NAME_FADE = 'fade';
  128. const CLASS_NAME_SHOW = 'show';
  129. const SELECTOR_DROPDOWN = '.dropdown';
  130. const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
  131. const SELECTOR_ACTIVE = '.active';
  132. const SELECTOR_ACTIVE_UL = ':scope > li > .active';
  133. const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]';
  134. const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
  135. const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active';
  136. /**
  137. * ------------------------------------------------------------------------
  138. * Class Definition
  139. * ------------------------------------------------------------------------
  140. */
  141. class Tab extends BaseComponent__default.default {
  142. // Getters
  143. static get NAME() {
  144. return NAME;
  145. } // Public
  146. show() {
  147. if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && this._element.classList.contains(CLASS_NAME_ACTIVE)) {
  148. return;
  149. }
  150. let previous;
  151. const target = getElementFromSelector(this._element);
  152. const listElement = this._element.closest(SELECTOR_NAV_LIST_GROUP);
  153. if (listElement) {
  154. const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE;
  155. previous = SelectorEngine__default.default.find(itemSelector, listElement);
  156. previous = previous[previous.length - 1];
  157. }
  158. const hideEvent = previous ? EventHandler__default.default.trigger(previous, EVENT_HIDE, {
  159. relatedTarget: this._element
  160. }) : null;
  161. const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, {
  162. relatedTarget: previous
  163. });
  164. if (showEvent.defaultPrevented || hideEvent !== null && hideEvent.defaultPrevented) {
  165. return;
  166. }
  167. this._activate(this._element, listElement);
  168. const complete = () => {
  169. EventHandler__default.default.trigger(previous, EVENT_HIDDEN, {
  170. relatedTarget: this._element
  171. });
  172. EventHandler__default.default.trigger(this._element, EVENT_SHOWN, {
  173. relatedTarget: previous
  174. });
  175. };
  176. if (target) {
  177. this._activate(target, target.parentNode, complete);
  178. } else {
  179. complete();
  180. }
  181. } // Private
  182. _activate(element, container, callback) {
  183. const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? SelectorEngine__default.default.find(SELECTOR_ACTIVE_UL, container) : SelectorEngine__default.default.children(container, SELECTOR_ACTIVE);
  184. const active = activeElements[0];
  185. const isTransitioning = callback && active && active.classList.contains(CLASS_NAME_FADE);
  186. const complete = () => this._transitionComplete(element, active, callback);
  187. if (active && isTransitioning) {
  188. active.classList.remove(CLASS_NAME_SHOW);
  189. this._queueCallback(complete, element, true);
  190. } else {
  191. complete();
  192. }
  193. }
  194. _transitionComplete(element, active, callback) {
  195. if (active) {
  196. active.classList.remove(CLASS_NAME_ACTIVE);
  197. const dropdownChild = SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode);
  198. if (dropdownChild) {
  199. dropdownChild.classList.remove(CLASS_NAME_ACTIVE);
  200. }
  201. if (active.getAttribute('role') === 'tab') {
  202. active.setAttribute('aria-selected', false);
  203. }
  204. }
  205. element.classList.add(CLASS_NAME_ACTIVE);
  206. if (element.getAttribute('role') === 'tab') {
  207. element.setAttribute('aria-selected', true);
  208. }
  209. reflow(element);
  210. if (element.classList.contains(CLASS_NAME_FADE)) {
  211. element.classList.add(CLASS_NAME_SHOW);
  212. }
  213. let parent = element.parentNode;
  214. if (parent && parent.nodeName === 'LI') {
  215. parent = parent.parentNode;
  216. }
  217. if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) {
  218. const dropdownElement = element.closest(SELECTOR_DROPDOWN);
  219. if (dropdownElement) {
  220. SelectorEngine__default.default.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement).forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE));
  221. }
  222. element.setAttribute('aria-expanded', true);
  223. }
  224. if (callback) {
  225. callback();
  226. }
  227. } // Static
  228. static jQueryInterface(config) {
  229. return this.each(function () {
  230. const data = Tab.getOrCreateInstance(this);
  231. if (typeof config === 'string') {
  232. if (typeof data[config] === 'undefined') {
  233. throw new TypeError(`No method named "${config}"`);
  234. }
  235. data[config]();
  236. }
  237. });
  238. }
  239. }
  240. /**
  241. * ------------------------------------------------------------------------
  242. * Data Api implementation
  243. * ------------------------------------------------------------------------
  244. */
  245. EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
  246. if (['A', 'AREA'].includes(this.tagName)) {
  247. event.preventDefault();
  248. }
  249. if (isDisabled(this)) {
  250. return;
  251. }
  252. const data = Tab.getOrCreateInstance(this);
  253. data.show();
  254. });
  255. /**
  256. * ------------------------------------------------------------------------
  257. * jQuery
  258. * ------------------------------------------------------------------------
  259. * add .Tab to jQuery only if jQuery is present
  260. */
  261. defineJQueryPlugin(Tab);
  262. return Tab;
  263. }));
  264. //# sourceMappingURL=tab.js.map