vendor: 7,052 bytes, lines 1-194
1import { E as EventHandler, f as isDisabled, S as SelectorEngine, d as defineJQueryPlugin, B as BaseComponent, g as getNextActiveElement } from './dom.js?5.3.0'; 2 3/** 4 * -------------------------------------------------------------------------- 5 * Bootstrap tab.js 6 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) 7 * -------------------------------------------------------------------------- 8 */ 9 10/** 11 * Constants 12 */ 13 14const NAME = 'tab'; 15const DATA_KEY = 'bs.tab'; 16const EVENT_KEY = `.${DATA_KEY}`; 17const EVENT_HIDE = `hide${EVENT_KEY}`; 18const EVENT_HIDDEN = `hidden${EVENT_KEY}`; 19const EVENT_SHOW = `show${EVENT_KEY}`; 20const EVENT_SHOWN = `shown${EVENT_KEY}`; 21const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`; 22const EVENT_KEYDOWN = `keydown${EVENT_KEY}`; 23const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`; 24const ARROW_LEFT_KEY = 'ArrowLeft'; 25const ARROW_RIGHT_KEY = 'ArrowRight'; 26const ARROW_UP_KEY = 'ArrowUp'; 27const ARROW_DOWN_KEY = 'ArrowDown'; 28const HOME_KEY = 'Home'; 29const END_KEY = 'End'; 30const CLASS_NAME_ACTIVE = 'active'; 31const CLASS_NAME_FADE = 'fade'; 32const CLASS_NAME_SHOW = 'show'; 33const CLASS_DROPDOWN = 'dropdown'; 34const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; 35const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'; 36const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`; 37const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'; 38const SELECTOR_OUTER = '.nav-item, .list-group-item'; 39const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`; 40const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6 41const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`; 42const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`; 43 44/** 45 * Class definition 46 */ 47 48class Tab extends BaseComponent { 49 constructor(element) { 50 super(element); 51 this._parent = this._element.closest(SELECTOR_TAB_PANEL); 52 if (!this._parent) { 53 return; 54 // TODO: should throw exception in v6 55 // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`) 56 } 57 58 // Set up initial aria attributes 59 this._setInitialAttributes(this._parent, this._getChildren()); 60 EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)); 61 } 62 63 // Getters 64 static get NAME() { 65 return NAME; 66 } 67 68 // Public 69 show() { 70 // Shows this elem and deactivate the active sibling if exists 71 const innerElem = this._element; 72 if (this._elemIsActive(innerElem)) { 73 return; 74 } 75 76 // Search for active tab on same parent to deactivate it 77 const active = this._getActiveElem(); 78 //const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE, { 79 // relatedTarget: innerElem 80 // }) : null; 81 const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { 82 relatedTarget: active 83 }); 84 // if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) { 85 // return; 86 // } 87 this._deactivate(active, innerElem); 88 this._activate(innerElem, active); 89 } 90 91 // Private 92 _activate(element, relatedElem) { 93 if (!element) { 94 return; 95 } 96 element.classList.add(CLASS_NAME_ACTIVE); 97 this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section 98 99 const complete = () => { 100 if (element.getAttribute('role') !== 'tab') { 101 element.classList.add(CLASS_NAME_SHOW); 102 return; 103 } 104 element.removeAttribute('tabindex'); 105 element.setAttribute('aria-selected', true); 106 this._toggleDropDown(element, true); 107 EventHandler.trigger(element, EVENT_SHOWN, { 108 relatedTarget: relatedElem 109 }); 110 }; 111 this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE)); 112 } 113 _deactivate(element, relatedElem) { 114 if (!element) { 115 return; 116 } 117 element.classList.remove(CLASS_NAME_ACTIVE); 118 element.blur(); 119 this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too 120 121 const complete = () => { 122 if (element.getAttribute('role') !== 'tab') { 123 element.classList.remove(CLASS_NAME_SHOW); 124 return; 125 } 126 element.setAttribute('aria-selected', false); 127 element.setAttribute('tabindex', '-1'); 128 this._toggleDropDown(element, false); 129 EventHandler.trigger(element, EVENT_HIDDEN, { 130 relatedTarget: relatedElem 131 }); 132 }; 133 this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE)); 134 } 135 _keydown(event) { 136 if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) { 137 return; 138 } 139 event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page 140 event.preventDefault(); 141 const children = this._getChildren().filter(element => !isDisabled(element)); 142 let nextActiveElement; 143 if ([HOME_KEY, END_KEY].includes(event.key)) { 144 nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]; 145 } else { 146 const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key); 147 nextActiveElement = getNextActiveElement(children, event.target, isNext, true); 148 } 149 if (nextActiveElement) { 150 nextActiveElement.focus({ 151 preventScroll: true 152 }); 153 Tab.getOrCreateInstance(nextActiveElement).show(); 154 } 155 } 156 _getChildren() { 157 // collection of inner elements 158 return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent); 159 } 160 _getActiveElem() { 161 return this._getChildren().find(child => this._elemIsActive(child)) || null; 162 } 163 _setInitialAttributes(parent, children) { 164 this._setAttributeIfNotExists(parent, 'role', 'tablist'); 165 for (const child of children) { 166 this._setInitialAttributesOnChild(child); 167 } 168 } 169 _setInitialAttributesOnChild(child) { 170 child = this._getInnerElement(child); 171 const isActive = this._elemIsActive(child); 172 const outerElem = this._getOuterElement(child); 173 child.setAttribute('aria-selected', isActive); 174 if (outerElem !== child) { 175 this._setAttributeIfNotExists(outerElem, 'role', 'presentation'); 176 } 177 if (!isActive) { 178 child.setAttribute('tabindex', '-1'); 179 } 180 this._setAttributeIfNotExists(child, 'role', 'tab'); 181 182 // set attributes to the related panel too 183 this._setInitialAttributesOnTargetPanel(child); 184 } 185 _setInitialAttributesOnTargetPanel(child) { 186 const target = SelectorEngine.getElementFromSelector(child); 187 if (!target) { 188 return; 189 } 190 this._setAttributeIfNotExists(target, 'role', 'tabpanel'); 191 if (child.id) { 192 this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`); 193 } 194 }
195 _toggleDropDown(element, open) { 196 const outerElem = this._getOuterElement(element); 197 if (!outerElem.classList.contains(CLASS_DROPDOWN)) { 198 return; 199 } 200 const toggle = (selector, className) => { 201 const element = SelectorEngine.findOne(selector, outerElem); 202 if (element) { 203 element.classList.toggle(className, open); 204 } 205 }; 206 toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE); 207 toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW); 208 outerElem.setAttribute('aria-expanded', open); 209 } 210 _setAttributeIfNotExists(element, attribute, value) { 211 if (!element.hasAttribute(attribute)) { 212 element.setAttribute(attribute, value); 213 } 214 } 215 _elemIsActive(elem) { 216 return elem.classList.contains(CLASS_NAME_ACTIVE); 217 } 218 219 // Try to get the inner element (usually the .nav-link) 220 _getInnerElement(elem) { 221 return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem); 222 } 223 224 // Try to get the outer element (usually the .nav-item) 225 _getOuterElement(elem) { 226 return elem.closest(SELECTOR_OUTER) || elem; 227 } 228 229 // Static 230 static jQueryInterface(config) { 231 return this.each(function () { 232 const data = Tab.getOrCreateInstance(this); 233 if (typeof config !== 'string') { 234 return; 235 } 236 if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { 237 throw new TypeError(`No method named "${config}"`); 238 } 239 data[config](); 240 }); 241 } 242} 243 244/** 245 * Data API implementation 246 */ 247 248EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { 249 if (['A', 'AREA'].includes(this.tagName)) { 250 event.preventDefault(); 251 } 252 if (isDisabled(this)) { 253 return; 254 } 255 Tab.getOrCreateInstance(this).show(); 256}); 257 258/** 259 * Initialize on focus 260 */ 261EventHandler.on(window, EVENT_LOAD_DATA_API, () => { 262 for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) { 263 Tab.getOrCreateInstance(element); 264 } 265}); 266/** 267 * jQuery 268 */ 269 270defineJQueryPlugin(Tab); 271 272window.Joomla = window.Joomla || {}; 273window.bootstrap = window.bootstrap || {}; 274window.bootstrap.Tab = Tab; 275 276/** 277 * Initialise the Tabs interactivity 278 * 279 * @param {HTMLElement} el The element that will become a collapse 280 * @param {object} options The options for this collapse 281 */ 282Joomla.initialiseTabs = (el, options) => { 283 if (!(el instanceof Element) && options.isJoomla) { 284 const tab = document.querySelector(`${el}Content`); 285 if (tab) { 286 const related = Array.from(tab.children); 287 288 // Build the navigation 289 if (related.length) { 290 related.forEach(element => { 291 if (!element.classList.contains('tab-pane')) { 292 return; 293 } 294 const isActive = element.dataset.active !== ''; 295 const ul = document.querySelector(`${el}Tabs`); 296 if (ul) { 297 const link = document.createElement('a'); 298 link.href = `#${element.dataset.id}`; 299 link.classList.add('nav-link'); 300 if (isActive) { 301 link.classList.add('active'); 302 } 303 link.dataset.bsToggle = 'tab'; 304 link.setAttribute('role', 'tab'); 305 link.setAttribute('aria-controls', element.dataset.id); 306 link.setAttribute('aria-selected', element.dataset.id); 307 link.innerHTML = Joomla.sanitizeHtml(element.dataset.title); 308 const li = document.createElement('li'); 309 li.classList.add('nav-item'); 310 li.setAttribute('role', 'presentation'); 311 li.appendChild(link); 312 ul.appendChild(li); 313 314 // eslint-disable-next-line no-new 315 new window.bootstrap.Tab(li); 316 } 317 }); 318 } 319 } 320 } else { 321 Array.from(document.querySelectorAll(`${el} a`)).map(tab => new window.bootstrap.Tab(tab, options)); 322 } 323}; 324if (Joomla && Joomla.getOptions) { 325 // Get the elements/configurations from the PHP 326 const tabs = Joomla.getOptions('bootstrap.tabs'); 327 // Initialise the elements 328 if (typeof tabs === 'object' && tabs !== null) { 329 Object.keys(tabs).map(tab => Joomla.initialiseTabs(tab, tabs[tab])); 330 } 331} 332 333export { Tab as T };
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.