tab.js 10 KB

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