PluginProbe
Elementor Website Builder – more than just a page builder / 3.30.0-beta1
Elementor Website Builder – more than just a page builder v3.30.0-beta1
4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 All 457 releases
elementor / assets / js / nested-tabs.213892f3e7a826d32481.bundle.js

nested-tabs.213892f3e7a826d32481.bundle.js in Elementor Website Builder – more than just a page builder 3.30.0-beta1, at assets/js/nested-tabs.213892f3e7a826d32481.bundle.js

507 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*! elementor - v3.30.0 - 10-06-2025 */
2 "use strict";
3 (self["webpackChunkelementorFrontend"] = self["webpackChunkelementorFrontend"] || []).push([["nested-tabs"],{
4
5 /***/ "../assets/dev/js/frontend/utils/flex-horizontal-scroll.js":
6 /*!*****************************************************************!*\
7 !*** ../assets/dev/js/frontend/utils/flex-horizontal-scroll.js ***!
8 \*****************************************************************/
9 /***/ ((__unused_webpack_module, exports) => {
10
11
12
13 Object.defineProperty(exports, "__esModule", ({
14 value: true
15 }));
16 exports.changeScrollStatus = changeScrollStatus;
17 exports.setHorizontalScrollAlignment = setHorizontalScrollAlignment;
18 exports.setHorizontalTitleScrollValues = setHorizontalTitleScrollValues;
19 function changeScrollStatus(element, event) {
20 if ('mousedown' === event.type) {
21 element.classList.add('e-scroll');
22 element.dataset.pageX = event.pageX;
23 } else {
24 element.classList.remove('e-scroll', 'e-scroll-active');
25 element.dataset.pageX = '';
26 }
27 }
28
29 // This function was written using this example https://codepen.io/thenutz/pen/VwYeYEE.
30 function setHorizontalTitleScrollValues(element, horizontalScrollStatus, event) {
31 const isActiveScroll = element.classList.contains('e-scroll'),
32 isHorizontalScrollActive = 'enable' === horizontalScrollStatus,
33 headingContentIsWiderThanWrapper = element.scrollWidth > element.clientWidth;
34 if (!isActiveScroll || !isHorizontalScrollActive || !headingContentIsWiderThanWrapper) {
35 return;
36 }
37 event.preventDefault();
38 const previousPositionX = parseFloat(element.dataset.pageX),
39 mouseMoveX = event.pageX - previousPositionX,
40 maximumScrollValue = 5,
41 stepLimit = 20;
42 let toScrollDistanceX = 0;
43 if (stepLimit < mouseMoveX) {
44 toScrollDistanceX = maximumScrollValue;
45 } else if (stepLimit * -1 > mouseMoveX) {
46 toScrollDistanceX = -1 * maximumScrollValue;
47 } else {
48 toScrollDistanceX = mouseMoveX;
49 }
50 element.scrollLeft = element.scrollLeft - toScrollDistanceX;
51 element.classList.add('e-scroll-active');
52 }
53 function setHorizontalScrollAlignment(_ref) {
54 let {
55 element,
56 direction,
57 justifyCSSVariable,
58 horizontalScrollStatus
59 } = _ref;
60 if (!element) {
61 return;
62 }
63 if (isHorizontalScroll(element, horizontalScrollStatus)) {
64 initialScrollPosition(element, direction, justifyCSSVariable);
65 } else {
66 element.style.setProperty(justifyCSSVariable, '');
67 }
68 }
69 function isHorizontalScroll(element, horizontalScrollStatus) {
70 return element.clientWidth < getChildrenWidth(element.children) && 'enable' === horizontalScrollStatus;
71 }
72 function getChildrenWidth(children) {
73 let totalWidth = 0;
74 const parentContainer = children[0].parentNode,
75 computedStyles = getComputedStyle(parentContainer),
76 gap = parseFloat(computedStyles.gap) || 0; // Get the gap value or default to 0 if it's not specified
77
78 for (let i = 0; i < children.length; i++) {
79 totalWidth += children[i].offsetWidth + gap;
80 }
81 return totalWidth;
82 }
83 function initialScrollPosition(element, direction, justifyCSSVariable) {
84 const isRTL = elementorFrontend.config.is_rtl;
85 switch (direction) {
86 case 'end':
87 element.style.setProperty(justifyCSSVariable, 'start');
88 element.scrollLeft = isRTL ? -1 * getChildrenWidth(element.children) : getChildrenWidth(element.children);
89 break;
90 default:
91 element.style.setProperty(justifyCSSVariable, 'start');
92 element.scrollLeft = 0;
93 }
94 }
95
96 /***/ }),
97
98 /***/ "../modules/nested-tabs/assets/js/frontend/handlers/nested-tabs.js":
99 /*!*************************************************************************!*\
100 !*** ../modules/nested-tabs/assets/js/frontend/handlers/nested-tabs.js ***!
101 \*************************************************************************/
102 /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
103
104
105
106 var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js");
107 Object.defineProperty(exports, "__esModule", ({
108 value: true
109 }));
110 exports["default"] = void 0;
111 __webpack_require__(/*! core-js/modules/esnext.iterator.constructor.js */ "../node_modules/core-js/modules/esnext.iterator.constructor.js");
112 __webpack_require__(/*! core-js/modules/esnext.iterator.filter.js */ "../node_modules/core-js/modules/esnext.iterator.filter.js");
113 __webpack_require__(/*! core-js/modules/esnext.iterator.find.js */ "../node_modules/core-js/modules/esnext.iterator.find.js");
114 var _base = _interopRequireDefault(__webpack_require__(/*! elementor-frontend/handlers/base */ "../assets/dev/js/frontend/handlers/base.js"));
115 var _flexHorizontalScroll = __webpack_require__(/*! elementor-frontend-utils/flex-horizontal-scroll */ "../assets/dev/js/frontend/utils/flex-horizontal-scroll.js");
116 class NestedTabs extends _base.default {
117 /**
118 * @param {string|number} tabIndex
119 *
120 * @return {string}
121 */
122 getTabTitleFilterSelector(tabIndex) {
123 return `[${this.getSettings('dataAttributes').tabIndex}="${tabIndex}"]`;
124 }
125
126 /**
127 * @param {string|number} tabIndex
128 *
129 * @return {string}
130 */
131 getTabContentFilterSelector(tabIndex) {
132 return `*:nth-child(${tabIndex})`;
133 }
134
135 /**
136 * @param {HTMLElement} tabTitleElement
137 *
138 * @return {string}
139 */
140 getTabIndex(tabTitleElement) {
141 return tabTitleElement.getAttribute(this.getSettings('dataAttributes').tabIndex);
142 }
143 getActiveTabIndex() {
144 const settings = this.getSettings(),
145 activeTitleFilter = settings.ariaAttributes.activeTitleSelector,
146 tabIndexSelector = settings.dataAttributes.tabIndex,
147 $activeTitle = this.elements.$tabTitles.filter(activeTitleFilter);
148 return $activeTitle.attr(tabIndexSelector) || null;
149 }
150 getWidgetNumber() {
151 return this.$element.find('> .elementor-widget-container > .e-n-tabs, > .e-n-tabs').attr('data-widget-number');
152 }
153 getDefaultSettings() {
154 const widgetNumber = this.getWidgetNumber();
155 return {
156 selectors: {
157 widgetContainer: `[data-widget-number="${widgetNumber}"]`,
158 tabTitle: `[aria-controls*="e-n-tab-content-${widgetNumber}"]`,
159 tabTitleIcon: `[id*="e-n-tab-title-${widgetNumber}"] > .e-n-tab-icon`,
160 tabTitleText: `[id*="e-n-tab-title-${widgetNumber}"] > .e-n-tab-title-text`,
161 tabContent: `[data-widget-number="${widgetNumber}"] > .e-n-tabs-content > .e-con`,
162 headingContainer: `[data-widget-number="${widgetNumber}"] > .e-n-tabs-heading`,
163 activeTabContentContainers: `[id*="e-n-tab-content-${widgetNumber}"].e-active`
164 },
165 classes: {
166 active: 'e-active'
167 },
168 dataAttributes: {
169 tabIndex: 'data-tab-index'
170 },
171 ariaAttributes: {
172 titleStateAttribute: 'aria-selected',
173 activeTitleSelector: '[aria-selected="true"]'
174 },
175 showTabFn: 'show',
176 hideTabFn: 'hide',
177 toggleSelf: false,
178 hidePrevious: true,
179 autoExpand: true
180 };
181 }
182 getDefaultElements() {
183 const selectors = this.getSettings('selectors');
184 return {
185 $widgetContainer: this.findElement(selectors.widgetContainer),
186 $tabTitles: this.findElement(selectors.tabTitle),
187 $tabContents: this.findElement(selectors.tabContent),
188 $headingContainer: this.findElement(selectors.headingContainer)
189 };
190 }
191 getKeyboardNavigationSettings() {
192 return this.getSettings();
193 }
194 activateDefaultTab() {
195 const settings = this.getSettings();
196 const defaultActiveTab = this.getEditSettings('activeItemIndex') || 1,
197 originalToggleMethods = {
198 showTabFn: settings.showTabFn,
199 hideTabFn: settings.hideTabFn
200 };
201
202 // Toggle tabs without animation to avoid jumping
203 this.setSettings({
204 showTabFn: 'show',
205 hideTabFn: 'hide'
206 });
207 this.changeActiveTab(defaultActiveTab);
208
209 // Return back original toggle effects
210 this.setSettings(originalToggleMethods);
211 this.elements.$widgetContainer.addClass('e-activated');
212 }
213 deactivateActiveTab(newTabIndex) {
214 const settings = this.getSettings(),
215 activeClass = settings.classes.active,
216 activeTitleFilter = settings.ariaAttributes.activeTitleSelector,
217 activeContentFilter = '.' + activeClass,
218 $activeTitle = this.elements.$tabTitles.filter(activeTitleFilter),
219 $activeContent = this.elements.$tabContents.filter(activeContentFilter);
220 this.setTabDeactivationAttributes($activeTitle, newTabIndex);
221 $activeContent.removeClass(activeClass);
222 $activeContent[settings.hideTabFn](0, () => this.onHideTabContent($activeContent));
223 return $activeContent;
224 }
225 getTitleActivationAttributes() {
226 const titleStateAttribute = this.getSettings('ariaAttributes').titleStateAttribute;
227 return {
228 tabindex: '0',
229 [titleStateAttribute]: 'true'
230 };
231 }
232 setTabDeactivationAttributes($activeTitle) {
233 const titleStateAttribute = this.getSettings('ariaAttributes').titleStateAttribute;
234 $activeTitle.attr({
235 tabindex: '-1',
236 [titleStateAttribute]: 'false'
237 });
238 }
239 onHideTabContent() {}
240 activateTab(tabIndex) {
241 const settings = this.getSettings(),
242 activeClass = settings.classes.active,
243 animationDuration = 'show' === settings.showTabFn ? 0 : 400;
244 let $requestedTitle = this.elements.$tabTitles.filter(this.getTabTitleFilterSelector(tabIndex)),
245 $requestedContent = this.elements.$tabContents.filter(this.getTabContentFilterSelector(tabIndex));
246
247 // Check if the tabIndex exists.
248 if (!$requestedTitle.length) {
249 // Activate the previous tab and ensure that the tab index is not less than 1.
250 const previousTabIndex = Math.max(tabIndex - 1, 1);
251 $requestedTitle = this.elements.$tabTitles.filter(this.getTabTitleFilterSelector(previousTabIndex));
252 $requestedContent = this.elements.$tabContents.filter(this.getTabContentFilterSelector(previousTabIndex));
253 }
254 $requestedTitle.attr(this.getTitleActivationAttributes());
255 $requestedContent.addClass(activeClass);
256 $requestedContent[settings.showTabFn](animationDuration, () => this.onShowTabContent($requestedContent));
257 }
258 onShowTabContent($requestedContent) {
259 elementorFrontend.elements.$window.trigger('elementor-pro/motion-fx/recalc');
260 elementorFrontend.elements.$window.trigger('elementor/nested-tabs/activate', $requestedContent);
261 elementorFrontend.elements.$window.trigger('elementor/bg-video/recalc');
262 }
263 isActiveTab(tabIndex) {
264 const settings = this.getSettings(),
265 isActiveTabTitle = 'true' === this.elements.$tabTitles.filter(`[${settings.dataAttributes.tabIndex}="${tabIndex}"]`).attr(settings.ariaAttributes.titleStateAttribute),
266 isActiveTabContent = this.elements.$tabContents.filter(this.getTabContentFilterSelector(tabIndex)).hasClass(this.getActiveClass());
267 return isActiveTabTitle && isActiveTabContent;
268 }
269 onTabClick(event) {
270 event.preventDefault();
271 this.changeActiveTab(event.currentTarget?.getAttribute(this.getSettings('dataAttributes').tabIndex), true);
272 }
273 getTabEvents() {
274 return {
275 click: this.onTabClick.bind(this)
276 };
277 }
278 getHeadingEvents() {
279 const navigationWrapper = this.elements.$headingContainer[0];
280 return {
281 mousedown: _flexHorizontalScroll.changeScrollStatus.bind(this, navigationWrapper),
282 mouseup: _flexHorizontalScroll.changeScrollStatus.bind(this, navigationWrapper),
283 mouseleave: _flexHorizontalScroll.changeScrollStatus.bind(this, navigationWrapper),
284 mousemove: _flexHorizontalScroll.setHorizontalTitleScrollValues.bind(this, navigationWrapper, this.getHorizontalScrollSetting())
285 };
286 }
287 bindEvents() {
288 this.elements.$tabTitles.on(this.getTabEvents());
289 this.elements.$headingContainer.on(this.getHeadingEvents());
290 elementorFrontend.elements.$window.on('resize', this.onResizeUpdateHorizontalScrolling.bind(this));
291 elementorFrontend.elements.$window.on('resize', this.setTouchMode.bind(this));
292 elementorFrontend.elements.$window.on('elementor/nested-tabs/activate', this.reInitSwipers);
293 elementorFrontend.elements.$window.on('elementor/nested-elements/activate-by-keyboard', this.changeActiveTabByKeyboard.bind(this));
294 elementorFrontend.elements.$window.on('elementor/nested-container/atomic-repeater', this.linkContainer.bind(this));
295 }
296 unbindEvents() {
297 this.elements.$tabTitles.off();
298 this.elements.$headingContainer.off();
299 this.elements.$tabContents.children().off();
300 elementorFrontend.elements.$window.off('resize', this.onResizeUpdateHorizontalScrolling.bind(this));
301 elementorFrontend.elements.$window.off('resize', this.setTouchMode.bind(this));
302 elementorFrontend.elements.$window.off('elementor/nested-tabs/activate', this.reInitSwipers);
303 elementorFrontend.elements.$window.off('elementor/nested-elements/activate-by-keyboard', this.changeActiveTabByKeyboard.bind(this));
304 elementorFrontend.elements.$window.off('elementor/nested-container/atomic-repeater', this.linkContainer.bind(this));
305 }
306
307 /**
308 * Fixes issues where Swipers that have been initialized while a tab is not visible are not properly rendered
309 * and when switching to the tab the swiper will not respect any of the chosen `autoplay` related settings.
310 *
311 * This is triggered when switching to a nested tab, looks for Swipers in the tab content and reinitializes them.
312 *
313 * @param {Object} event - Incoming event.
314 * @param {Object} content - Active nested tab dom element.
315 */
316 reInitSwipers(event, content) {
317 const swiperElements = content.querySelectorAll('.swiper');
318 for (const element of swiperElements) {
319 if (!element.swiper) {
320 return;
321 }
322 element.swiper.initialized = false;
323 element.swiper.init();
324 }
325 }
326 onInit() {
327 super.onInit(...arguments);
328 if (this.getSettings('autoExpand')) {
329 this.activateDefaultTab();
330 }
331 (0, _flexHorizontalScroll.setHorizontalScrollAlignment)(this.getHorizontalScrollingSettings());
332 this.setTouchMode();
333 if ('nested-tabs.default' === this.getSettings('elementName')) {
334 __webpack_require__.e(/*! import() | nested-title-keyboard-handler */ "nested-title-keyboard-handler").then(__webpack_require__.bind(__webpack_require__, /*! elementor-frontend/handlers/accessibility/nested-title-keyboard-handler */ "../assets/dev/js/frontend/handlers/accessibility/nested-title-keyboard-handler.js")).then(_ref => {
335 let {
336 default: NestedTitleKeyboardHandler
337 } = _ref;
338 new NestedTitleKeyboardHandler(this.getKeyboardNavigationSettings());
339 }).catch(error => {
340 // eslint-disable-next-line no-console
341 console.error('Error importing module:', error);
342 });
343 }
344 }
345 onEditSettingsChange(propertyName, value) {
346 if ('activeItemIndex' === propertyName) {
347 this.changeActiveTab(value, false);
348 }
349 }
350 onElementChange(propertyName) {
351 if (this.checkSliderPropsToWatch(propertyName)) {
352 (0, _flexHorizontalScroll.setHorizontalScrollAlignment)(this.getHorizontalScrollingSettings());
353 }
354 }
355 checkSliderPropsToWatch(propertyName) {
356 return 0 === propertyName.indexOf('horizontal_scroll') || 'breakpoint_selector' === propertyName || 0 === propertyName.indexOf('tabs_justify_horizontal') || 0 === propertyName.indexOf('tabs_title_space_between');
357 }
358
359 /**
360 * @param {string} tabIndex
361 * @param {boolean} fromUser - Whether the call is caused by the user or internal.
362 */
363 changeActiveTab(tabIndex) {
364 let fromUser = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
365 // `document/repeater/select` is used only in the editor, only when the element
366 // is in the currently-edited document, and only when its not internal call,
367 if (fromUser && this.isEdit && this.isElementInTheCurrentDocument()) {
368 return window.top.$e.run('document/repeater/select', {
369 container: elementor.getContainer(this.$element.attr('data-id')),
370 index: parseInt(tabIndex)
371 });
372 }
373 const isActiveTab = this.isActiveTab(tabIndex),
374 settings = this.getSettings();
375 if ((settings.toggleSelf || !isActiveTab) && settings.hidePrevious) {
376 this.deactivateActiveTab(tabIndex);
377 }
378 if (!settings.hidePrevious && isActiveTab) {
379 this.deactivateActiveTab(tabIndex);
380 }
381 if (!isActiveTab) {
382 if (this.isAccordionVersion()) {
383 this.activateMobileTab(tabIndex);
384 return;
385 }
386 this.activateTab(tabIndex);
387 }
388 }
389 changeActiveTabByKeyboard(event, settings) {
390 if (settings.widgetId.toString() !== this.getID().toString()) {
391 return;
392 }
393 this.changeActiveTab(settings.titleIndex, true);
394 }
395 activateMobileTab(tabIndex) {
396 // Timeout time added to ensure that opening of the active tab starts after closing the other tab on Apple devices.
397 setTimeout(() => {
398 this.activateTab(tabIndex);
399 this.forceActiveTabToBeInViewport(tabIndex);
400 }, 10);
401 }
402 forceActiveTabToBeInViewport(tabIndex) {
403 if (!elementorFrontend.isEditMode()) {
404 return;
405 }
406 const $activeTabTitle = this.elements.$tabTitles.filter(this.getTabTitleFilterSelector(tabIndex));
407 if (!elementor.helpers.isInViewport($activeTabTitle[0])) {
408 $activeTabTitle[0].scrollIntoView({
409 block: 'center'
410 });
411 }
412 }
413 getActiveClass() {
414 const settings = this.getSettings();
415 return settings.classes.active;
416 }
417 getTabsDirection() {
418 const currentDevice = elementorFrontend.getCurrentDeviceMode();
419 return elementorFrontend.utils.controls.getResponsiveControlValue(this.getElementSettings(), 'tabs_justify_horizontal', '', currentDevice);
420 }
421 getHorizontalScrollSetting() {
422 const currentDevice = elementorFrontend.getCurrentDeviceMode();
423 return elementorFrontend.utils.controls.getResponsiveControlValue(this.getElementSettings(), 'horizontal_scroll', '', currentDevice);
424 }
425 isAccordionVersion() {
426 return 'contents' === this.elements.$headingContainer.css('display');
427 }
428 setTouchMode() {
429 const widgetSelector = this.getSettings('selectors').widgetContainer;
430 if (elementorFrontend.isEditMode() || 'resize' === event?.type) {
431 const responsiveDevices = ['mobile', 'mobile_extra', 'tablet', 'tablet_extra'],
432 currentDevice = elementorFrontend.getCurrentDeviceMode();
433 if (-1 !== responsiveDevices.indexOf(currentDevice)) {
434 this.$element.find(widgetSelector).attr('data-touch-mode', 'true');
435 return;
436 }
437 } else if ('ontouchstart' in window) {
438 this.$element.find(widgetSelector).attr('data-touch-mode', 'true');
439 return;
440 }
441 this.$element.find(widgetSelector).attr('data-touch-mode', 'false');
442 }
443 linkContainer(event) {
444 const {
445 container
446 } = event.detail,
447 id = container.model.get('id'),
448 currentId = this.$element.data('id'),
449 view = container.view.$el;
450 if (id === currentId) {
451 this.updateIndexValues();
452 this.updateListeners(view);
453 elementor.$preview[0].contentWindow.dispatchEvent(new CustomEvent('elementor/elements/link-data-bindings'));
454 }
455 if (!this.getActiveTabIndex()) {
456 const targetIndex = event.detail.index + 1 || 1;
457 this.changeActiveTab(targetIndex);
458 }
459 }
460 updateListeners(view) {
461 this.elements.$tabContents = view.find(this.getSettings('selectors.tabContent'));
462 this.elements.$tabTitles = view.find(this.getSettings('selectors.tabTitle'));
463 this.elements.$tabTitles.on(this.getTabEvents());
464 }
465 updateIndexValues() {
466 const {
467 $widgetContainer,
468 $tabContents,
469 $tabTitles
470 } = this.getDefaultElements(),
471 settings = this.getSettings(),
472 dataTabIndex = settings.dataAttributes.tabIndex,
473 widgetNumber = $widgetContainer.data('widgetNumber');
474 $tabTitles.each((index, element) => {
475 const newIndex = index + 1,
476 updatedTabID = `e-n-tab-title-${widgetNumber}${newIndex}`,
477 updatedContainerID = `e-n-tab-content-${widgetNumber}${newIndex}`;
478 element.setAttribute('id', updatedTabID);
479 element.setAttribute('style', `--n-tabs-title-order: ${newIndex}`);
480 element.setAttribute(dataTabIndex, newIndex);
481 element.setAttribute('aria-controls', updatedContainerID);
482 element.querySelector(settings.selectors.tabTitleIcon)?.setAttribute('data-binding-index', newIndex);
483 element.querySelector(settings.selectors.tabTitleText).setAttribute('data-binding-index', newIndex);
484 $tabContents[index].setAttribute('aria-labelledby', updatedTabID);
485 $tabContents[index].setAttribute(dataTabIndex, newIndex);
486 $tabContents[index].setAttribute('id', updatedContainerID);
487 $tabContents[index].setAttribute('style', `--n-tabs-title-order: ${newIndex}`);
488 });
489 }
490 onResizeUpdateHorizontalScrolling() {
491 (0, _flexHorizontalScroll.setHorizontalScrollAlignment)(this.getHorizontalScrollingSettings());
492 }
493 getHorizontalScrollingSettings() {
494 return {
495 element: this.elements.$headingContainer[0],
496 direction: this.getTabsDirection(),
497 justifyCSSVariable: '--n-tabs-heading-justify-content',
498 horizontalScrollStatus: this.getHorizontalScrollSetting()
499 };
500 }
501 }
502 exports["default"] = NestedTabs;
503
504 /***/ })
505
506 }]);
507 //# sourceMappingURL=nested-tabs.213892f3e7a826d32481.bundle.js.map