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-accordion.a0f28ea648b29da812a1.bundle.js

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

472 lines 18.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-accordion"],{
4
5 /***/ "../assets/dev/js/frontend/handlers/accessibility/nested-title-keyboard-handler.js":
6 /*!*****************************************************************************************!*\
7 !*** ../assets/dev/js/frontend/handlers/accessibility/nested-title-keyboard-handler.js ***!
8 \*****************************************************************************************/
9 /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
10
11
12
13 var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js");
14 Object.defineProperty(exports, "__esModule", ({
15 value: true
16 }));
17 exports["default"] = void 0;
18 __webpack_require__(/*! core-js/modules/es.array.includes.js */ "../node_modules/core-js/modules/es.array.includes.js");
19 __webpack_require__(/*! core-js/modules/esnext.iterator.constructor.js */ "../node_modules/core-js/modules/esnext.iterator.constructor.js");
20 __webpack_require__(/*! core-js/modules/esnext.iterator.filter.js */ "../node_modules/core-js/modules/esnext.iterator.filter.js");
21 __webpack_require__(/*! core-js/modules/esnext.iterator.find.js */ "../node_modules/core-js/modules/esnext.iterator.find.js");
22 var _base = _interopRequireDefault(__webpack_require__(/*! ../base */ "../assets/dev/js/frontend/handlers/base.js"));
23 class NestedTitleKeyboardHandler extends _base.default {
24 __construct(settings) {
25 super.__construct(settings);
26 this.directionNext = 'next';
27 this.directionPrevious = 'previous';
28 this.focusableElementSelector = 'audio, button, canvas, details, iframe, input, select, summary, textarea, video, [accesskey], [contenteditable], [href], [tabindex]:not([tabindex="-1"])';
29 }
30 getWidgetNumber() {
31 return this.$element.find('> .elementor-widget-container > .e-n-tabs, > .e-n-tabs').attr('data-widget-number');
32 }
33 getDefaultSettings() {
34 return {
35 selectors: {
36 itemTitle: `[id*="e-n-tab-title-${this.getWidgetNumber()}"]`,
37 itemContainer: `[id*="e-n-tab-content-${this.getWidgetNumber()}"]`
38 },
39 ariaAttributes: {
40 titleStateAttribute: 'aria-selected',
41 activeTitleSelector: '[aria-selected="true"]'
42 },
43 datasets: {
44 titleIndex: 'data-tab-index'
45 },
46 keyDirection: {
47 ArrowLeft: elementorFrontendConfig.is_rtl ? this.directionNext : this.directionPrevious,
48 ArrowUp: this.directionPrevious,
49 ArrowRight: elementorFrontendConfig.is_rtl ? this.directionPrevious : this.directionNext,
50 ArrowDown: this.directionNext
51 }
52 };
53 }
54 getDefaultElements() {
55 const selectors = this.getSettings('selectors');
56 return {
57 $itemTitles: this.findElement(selectors.itemTitle),
58 $itemContainers: this.findElement(selectors.itemContainer),
59 $focusableContainerElements: this.getFocusableElements(this.findElement(selectors.itemContainer))
60 };
61 }
62 getFocusableElements($elements) {
63 return $elements.find(this.focusableElementSelector).not('[disabled], [inert]');
64 }
65 getKeyDirectionValue(event) {
66 const direction = this.getSettings('keyDirection')[event.key];
67 return this.directionNext === direction ? 1 : -1;
68 }
69
70 /**
71 * @param {HTMLElement} itemTitleElement
72 *
73 * @return {string}
74 */
75 getTitleIndex(itemTitleElement) {
76 const {
77 titleIndex: indexAttribute
78 } = this.getSettings('datasets');
79 return itemTitleElement.getAttribute(indexAttribute);
80 }
81
82 /**
83 * @param {string|number} titleIndex
84 *
85 * @return {string}
86 */
87 getTitleFilterSelector(titleIndex) {
88 const {
89 titleIndex: indexAttribute
90 } = this.getSettings('datasets');
91 return `[${indexAttribute}="${titleIndex}"]`;
92 }
93 getActiveTitleElement() {
94 const activeTitleFilter = this.getSettings('ariaAttributes').activeTitleSelector;
95 return this.elements.$itemTitles.filter(activeTitleFilter);
96 }
97 onInit() {
98 super.onInit(...arguments);
99 }
100 bindEvents() {
101 this.elements.$itemTitles.on(this.getTitleEvents());
102 this.elements.$focusableContainerElements.on(this.getContentElementEvents());
103 }
104 unbindEvents() {
105 this.elements.$itemTitles.off(this.getTitleEvents());
106 this.elements.$focusableContainerElements.children().off(this.getContentElementEvents());
107 }
108 getTitleEvents() {
109 return {
110 keydown: this.handleTitleKeyboardNavigation.bind(this)
111 };
112 }
113 getContentElementEvents() {
114 return {
115 keydown: this.handleContentElementKeyboardNavigation.bind(this)
116 };
117 }
118 isDirectionKey(event) {
119 const directionKeys = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'];
120 return directionKeys.includes(event.key);
121 }
122 isActivationKey(event) {
123 const activationKeys = ['Enter', ' '];
124 return activationKeys.includes(event.key);
125 }
126 handleTitleKeyboardNavigation(event) {
127 if (this.isDirectionKey(event)) {
128 event.preventDefault();
129 const currentTitleIndex = parseInt(this.getTitleIndex(event.currentTarget)) || 1,
130 numberOfTitles = this.elements.$itemTitles.length,
131 titleIndexUpdated = this.getTitleIndexFocusUpdated(event, currentTitleIndex, numberOfTitles);
132 this.changeTitleFocus(titleIndexUpdated);
133 event.stopPropagation();
134 } else if (this.isActivationKey(event)) {
135 event.preventDefault();
136 if (this.handeTitleLinkEnterOrSpaceEvent(event)) {
137 return;
138 }
139 const titleIndex = this.getTitleIndex(event.currentTarget);
140 elementorFrontend.elements.$window.trigger('elementor/nested-elements/activate-by-keyboard', {
141 widgetId: this.getID(),
142 titleIndex
143 });
144 } else if ('Escape' === event.key) {
145 this.handleTitleEscapeKeyEvents(event);
146 }
147 }
148 handeTitleLinkEnterOrSpaceEvent(event) {
149 const isLinkElement = 'a' === event?.currentTarget?.tagName?.toLowerCase();
150 if (!elementorFrontend.isEditMode() && isLinkElement) {
151 event?.currentTarget?.click();
152 event.stopPropagation();
153 }
154 return isLinkElement;
155 }
156 getTitleIndexFocusUpdated(event, currentTitleIndex, numberOfTitles) {
157 let titleIndexUpdated = 0;
158 switch (event.key) {
159 case 'Home':
160 titleIndexUpdated = 1;
161 break;
162 case 'End':
163 titleIndexUpdated = numberOfTitles;
164 break;
165 default:
166 const directionValue = this.getKeyDirectionValue(event),
167 isEndReached = numberOfTitles < currentTitleIndex + directionValue,
168 isStartReached = 0 === currentTitleIndex + directionValue;
169 if (isEndReached) {
170 titleIndexUpdated = 1;
171 } else if (isStartReached) {
172 titleIndexUpdated = numberOfTitles;
173 } else {
174 titleIndexUpdated = currentTitleIndex + directionValue;
175 }
176 }
177 return titleIndexUpdated;
178 }
179 changeTitleFocus(titleIndexUpdated) {
180 const $newTitle = this.elements.$itemTitles.filter(this.getTitleFilterSelector(titleIndexUpdated));
181 this.setTitleTabindex(titleIndexUpdated);
182 $newTitle.trigger('focus');
183 }
184 setTitleTabindex(titleIndex) {
185 this.elements.$itemTitles.attr('tabindex', '-1');
186 const $newTitle = this.elements.$itemTitles.filter(this.getTitleFilterSelector(titleIndex));
187 $newTitle.attr('tabindex', '0');
188 }
189 handleTitleEscapeKeyEvents() {}
190 handleContentElementKeyboardNavigation(event) {
191 if ('Tab' === event.key && !event.shiftKey) {
192 this.handleContentElementTabEvents(event);
193 } else if ('Escape' === event.key) {
194 event.preventDefault();
195 event.stopPropagation();
196 this.handleContentElementEscapeEvents(event);
197 }
198 }
199 handleContentElementEscapeEvents() {
200 this.getActiveTitleElement().trigger('focus');
201 }
202 handleContentElementTabEvents() {}
203 }
204 exports["default"] = NestedTitleKeyboardHandler;
205
206 /***/ }),
207
208 /***/ "../modules/nested-accordion/assets/js/frontend/handlers/nested-accordion-title-keyboard-handler.js":
209 /*!**********************************************************************************************************!*\
210 !*** ../modules/nested-accordion/assets/js/frontend/handlers/nested-accordion-title-keyboard-handler.js ***!
211 \**********************************************************************************************************/
212 /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
213
214
215
216 var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js");
217 Object.defineProperty(exports, "__esModule", ({
218 value: true
219 }));
220 exports["default"] = void 0;
221 var _nestedTitleKeyboardHandler = _interopRequireDefault(__webpack_require__(/*! elementor-frontend/handlers/accessibility/nested-title-keyboard-handler */ "../assets/dev/js/frontend/handlers/accessibility/nested-title-keyboard-handler.js"));
222 class NestedAccordionTitleKeyboardHandler extends _nestedTitleKeyboardHandler.default {
223 __construct() {
224 super.__construct(...arguments);
225 const config = arguments.length <= 0 ? undefined : arguments[0];
226 this.toggleTitle = config.toggleTitle;
227 }
228 getDefaultSettings() {
229 const parentSettings = super.getDefaultSettings();
230 return {
231 ...parentSettings,
232 selectors: {
233 itemTitle: '.e-n-accordion-item-title',
234 itemContainer: '.e-n-accordion-item > .e-con'
235 },
236 ariaAttributes: {
237 titleStateAttribute: 'aria-expanded',
238 activeTitleSelector: '[aria-expanded="true"]'
239 },
240 datasets: {
241 titleIndex: 'data-accordion-index'
242 }
243 };
244 }
245 handeTitleLinkEnterOrSpaceEvent(event) {
246 this.toggleTitle(event);
247 }
248 handleContentElementEscapeEvents(event) {
249 this.getActiveTitleElement().trigger('focus');
250 this.toggleTitle(event);
251 }
252 handleTitleEscapeKeyEvents(event) {
253 const detailsNode = event?.currentTarget?.parentElement,
254 isOpen = detailsNode?.open;
255 if (isOpen) {
256 this.toggleTitle(event);
257 }
258 }
259 }
260 exports["default"] = NestedAccordionTitleKeyboardHandler;
261
262 /***/ }),
263
264 /***/ "../modules/nested-accordion/assets/js/frontend/handlers/nested-accordion.js":
265 /*!***********************************************************************************!*\
266 !*** ../modules/nested-accordion/assets/js/frontend/handlers/nested-accordion.js ***!
267 \***********************************************************************************/
268 /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
269
270
271
272 var _interopRequireDefault = __webpack_require__(/*! @babel/runtime/helpers/interopRequireDefault */ "../node_modules/@babel/runtime/helpers/interopRequireDefault.js");
273 Object.defineProperty(exports, "__esModule", ({
274 value: true
275 }));
276 exports["default"] = void 0;
277 __webpack_require__(/*! core-js/modules/esnext.iterator.constructor.js */ "../node_modules/core-js/modules/esnext.iterator.constructor.js");
278 __webpack_require__(/*! core-js/modules/esnext.iterator.find.js */ "../node_modules/core-js/modules/esnext.iterator.find.js");
279 __webpack_require__(/*! core-js/modules/esnext.iterator.for-each.js */ "../node_modules/core-js/modules/esnext.iterator.for-each.js");
280 var _base = _interopRequireDefault(__webpack_require__(/*! elementor-frontend/handlers/base */ "../assets/dev/js/frontend/handlers/base.js"));
281 var _nestedAccordionTitleKeyboardHandler = _interopRequireDefault(__webpack_require__(/*! ./nested-accordion-title-keyboard-handler */ "../modules/nested-accordion/assets/js/frontend/handlers/nested-accordion-title-keyboard-handler.js"));
282 class NestedAccordion extends _base.default {
283 constructor() {
284 super(...arguments);
285 this.animations = new Map();
286 }
287 getDefaultSettings() {
288 return {
289 selectors: {
290 accordion: '.e-n-accordion',
291 accordionContentContainers: '.e-n-accordion > .e-con',
292 accordionItems: '.e-n-accordion-item',
293 accordionItemTitles: '.e-n-accordion-item-title',
294 accordionItemTitlesText: '.e-n-accordion-item-title-text',
295 accordionContent: '.e-n-accordion-item > .e-con',
296 directAccordionItems: ':scope > .e-n-accordion-item',
297 directAccordionItemTitles: ':scope > .e-n-accordion-item > .e-n-accordion-item-title'
298 },
299 default_state: 'expanded',
300 attributes: {
301 index: 'data-accordion-index',
302 ariaLabelledBy: 'aria-labelledby'
303 }
304 };
305 }
306 getDefaultElements() {
307 const selectors = this.getSettings('selectors');
308 return {
309 $accordion: this.findElement(selectors.accordion),
310 $contentContainers: this.findElement(selectors.accordionContentContainers),
311 $accordionItems: this.findElement(selectors.accordionItems),
312 $accordionTitles: this.findElement(selectors.accordionItemTitles),
313 $accordionContent: this.findElement(selectors.accordionContent)
314 };
315 }
316 onInit() {
317 super.onInit(...arguments);
318 this.injectKeyboardHandler();
319 }
320 injectKeyboardHandler() {
321 if ('nested-accordion.default' === this.getSettings('elementName')) {
322 new _nestedAccordionTitleKeyboardHandler.default({
323 $element: this.$element,
324 toggleTitle: this.clickListener.bind(this)
325 });
326 }
327 }
328 linkContainer(event) {
329 const {
330 container,
331 index,
332 targetContainer,
333 action: {
334 type
335 }
336 } = event.detail,
337 view = container.view.$el,
338 id = container.model.get('id'),
339 currentId = this.$element.data('id');
340 if (id === currentId) {
341 const {
342 $accordionItems
343 } = this.getDefaultElements();
344 let accordionItem, contentContainer;
345 switch (type) {
346 case 'move':
347 [accordionItem, contentContainer] = this.move(view, index, targetContainer, $accordionItems);
348 break;
349 case 'duplicate':
350 [accordionItem, contentContainer] = this.duplicate(view, index, targetContainer, $accordionItems);
351 break;
352 default:
353 break;
354 }
355 if (undefined !== accordionItem) {
356 accordionItem.appendChild(contentContainer);
357 }
358 this.updateIndexValues();
359 this.updateListeners(view);
360 elementor.$preview[0].contentWindow.dispatchEvent(new CustomEvent('elementor/elements/link-data-bindings'));
361 }
362 }
363 move(view, index, targetContainer, accordionItems) {
364 return [accordionItems[index], targetContainer.view.$el[0]];
365 }
366 duplicate(view, index, targetContainer, accordionItems) {
367 return [accordionItems[index + 1], targetContainer.view.$el[0]];
368 }
369 updateIndexValues() {
370 const {
371 $accordionContent,
372 $accordionItems
373 } = this.getDefaultElements(),
374 settings = this.getSettings(),
375 itemIdBase = $accordionItems[0].getAttribute('id').slice(0, -1);
376 $accordionItems.each((index, element) => {
377 element.setAttribute('id', `${itemIdBase}${index}`);
378 element.querySelector(settings.selectors.accordionItemTitles).setAttribute(settings.attributes.index, index + 1);
379 element.querySelector(settings.selectors.accordionItemTitles).setAttribute('aria-controls', `${itemIdBase}${index}`);
380 element.querySelector(settings.selectors.accordionItemTitlesText).setAttribute('data-binding-index', index + 1);
381 $accordionContent[index].setAttribute(settings.attributes.ariaLabelledBy, `${itemIdBase}${index}`);
382 });
383 }
384 updateListeners(view) {
385 this.elements.$accordionTitles = view.find(this.getSettings('selectors.accordionItemTitles'));
386 this.elements.$accordionItems = view.find(this.getSettings('selectors.accordionItems'));
387 this.elements.$accordionTitles.on('click', this.clickListener.bind(this));
388 }
389 bindEvents() {
390 this.elements.$accordionTitles.on('click', this.clickListener.bind(this));
391 elementorFrontend.elements.$window.on('elementor/nested-container/atomic-repeater', this.linkContainer.bind(this));
392 }
393 unbindEvents() {
394 this.elements.$accordionTitles.off();
395 }
396 clickListener(event) {
397 event.preventDefault();
398 this.elements = this.getDefaultElements();
399 const settings = this.getSettings(),
400 accordionItem = event?.currentTarget?.closest(settings.selectors.accordionItems),
401 accordion = event?.currentTarget?.closest(settings.selectors.accordion),
402 itemSummary = accordionItem.querySelector(settings.selectors.accordionItemTitles),
403 accordionContent = accordionItem.querySelector(settings.selectors.accordionContent),
404 {
405 max_items_expended: maxItemsExpended
406 } = this.getElementSettings(),
407 directAccordionItems = accordion.querySelectorAll(settings.selectors.directAccordionItems),
408 directAccordionItemTitles = accordion.querySelectorAll(settings.selectors.directAccordionItemTitles);
409 if ('one' === maxItemsExpended) {
410 this.closeAllItems(directAccordionItems, directAccordionItemTitles);
411 }
412 if (!accordionItem.open) {
413 this.prepareOpenAnimation(accordionItem, itemSummary, accordionContent);
414 } else {
415 this.closeAccordionItem(accordionItem, itemSummary);
416 }
417 }
418 animateItem(accordionItem, startHeight, endHeight, isOpen) {
419 accordionItem.style.overflow = 'hidden';
420 let animation = this.animations.get(accordionItem);
421 if (animation) {
422 animation.cancel();
423 }
424 animation = accordionItem.animate({
425 height: [startHeight, endHeight]
426 }, {
427 duration: this.getAnimationDuration()
428 });
429 animation.onfinish = () => this.onAnimationFinish(accordionItem, isOpen);
430 this.animations.set(accordionItem, animation);
431 accordionItem.querySelector('summary')?.setAttribute('aria-expanded', isOpen);
432 }
433 closeAccordionItem(accordionItem, accordionItemTitle) {
434 const startHeight = `${accordionItem.offsetHeight}px`,
435 endHeight = `${accordionItemTitle.offsetHeight}px`;
436 this.animateItem(accordionItem, startHeight, endHeight, false);
437 }
438 prepareOpenAnimation(accordionItem, accordionItemTitle, accordionItemContent) {
439 accordionItem.style.overflow = 'hidden';
440 accordionItem.style.height = `${accordionItem.offsetHeight}px`;
441 accordionItem.open = true;
442 window.requestAnimationFrame(() => this.openAccordionItem(accordionItem, accordionItemTitle, accordionItemContent));
443 }
444 openAccordionItem(accordionItem, accordionItemTitle, accordionItemContent) {
445 const startHeight = `${accordionItem.offsetHeight}px`,
446 endHeight = `${accordionItemTitle.offsetHeight + accordionItemContent.offsetHeight}px`;
447 this.animateItem(accordionItem, startHeight, endHeight, true);
448 }
449 onAnimationFinish(accordionItem, isOpen) {
450 accordionItem.open = isOpen;
451 this.animations.set(accordionItem, null);
452 accordionItem.style.height = accordionItem.style.overflow = '';
453 }
454 closeAllItems(items, titles) {
455 titles.forEach((title, index) => {
456 this.closeAccordionItem(items[index], title);
457 });
458 }
459 getAnimationDuration() {
460 const {
461 size,
462 unit
463 } = this.getElementSettings('n_accordion_animation_duration');
464 return size * ('ms' === unit ? 1 : 1000);
465 }
466 }
467 exports["default"] = NestedAccordion;
468
469 /***/ })
470
471 }]);
472 //# sourceMappingURL=nested-accordion.a0f28ea648b29da812a1.bundle.js.map