PluginProbe
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor / 2.0.7
Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor v2.0.7
2.0.13 2.0.12 2.0.11 2.0.10 2.0.9 trunk 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8
blockenberg / blocks / modal / frontend.js

frontend.js in Blockenberg — 600+ Advanced Gutenberg Blocks & AI Agent for WordPress Block Editor 2.0.7, at blocks/modal/frontend.js

132 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (function () {
2 function toArray(nl) {
3 return Array.prototype.slice.call(nl || []);
4 }
5
6 // Track last focused element before modal opens (for focus restoration)
7 var lastFocused = null;
8
9 // All focusable selectors
10 var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),textarea:not([disabled]),select:not([disabled]),[tabindex]:not([tabindex="-1"])';
11
12 function trapFocus(modal) {
13 var focusable = toArray(modal.querySelectorAll(FOCUSABLE)).filter(function (el) {
14 return el.offsetParent !== null;
15 });
16 if (!focusable.length) return;
17
18 var first = focusable[0];
19 var last = focusable[focusable.length - 1];
20
21 modal.addEventListener('keydown', function onKeyDown(e) {
22 if (e.key !== 'Tab') return;
23 if (focusable.length === 1) {
24 e.preventDefault();
25 return;
26 }
27 if (e.shiftKey) {
28 if (document.activeElement === first) {
29 e.preventDefault();
30 last.focus();
31 }
32 } else {
33 if (document.activeElement === last) {
34 e.preventDefault();
35 first.focus();
36 }
37 }
38 });
39
40 // Focus first focusable
41 first.focus();
42 }
43
44 function openModal(modal) {
45 lastFocused = document.activeElement;
46 modal.setAttribute('aria-hidden', 'false');
47 modal.classList.add('is-open');
48 document.body.style.overflow = 'hidden';
49 trapFocus(modal);
50 }
51
52 function closeModal(modal) {
53 modal.setAttribute('aria-hidden', 'true');
54 modal.classList.remove('is-open');
55 document.body.style.overflow = '';
56 if (lastFocused) {
57 lastFocused.focus();
58 lastFocused = null;
59 }
60 }
61
62 function initModalBlock(block) {
63 var modalId = block.getAttribute('data-modal-id');
64 if (!modalId) return;
65
66 var modal = block.querySelector('.bkbg-modal-overlay#' + modalId);
67 var triggers = toArray(block.querySelectorAll('[data-bkbg-modal-open="' + modalId + '"]'));
68 var closeBtns = toArray(block.querySelectorAll('[data-bkbg-modal-close="' + modalId + '"]'));
69
70 if (!modal) return;
71
72 var closeOnOverlay = modal.getAttribute('data-close-overlay') !== '0';
73 var closeOnEsc = modal.getAttribute('data-close-esc') !== '0';
74 var triggerMode = modal.getAttribute('data-trigger-mode');
75 var autoDelay = parseInt(modal.getAttribute('data-auto-delay'), 10) || 3000;
76
77 // Open triggers
78 triggers.forEach(function (btn) {
79 btn.addEventListener('click', function () {
80 openModal(modal);
81 });
82 });
83
84 // Close buttons
85 closeBtns.forEach(function (btn) {
86 btn.addEventListener('click', function () {
87 closeModal(modal);
88 });
89 });
90
91 // Close on overlay click
92 if (closeOnOverlay) {
93 modal.addEventListener('click', function (e) {
94 if (e.target === modal) {
95 closeModal(modal);
96 }
97 });
98 }
99
100 // Close on ESC key
101 if (closeOnEsc) {
102 document.addEventListener('keydown', function (e) {
103 if (e.key === 'Escape' && modal.classList.contains('is-open')) {
104 closeModal(modal);
105 }
106 });
107 }
108
109 // Auto-open
110 if (triggerMode === 'auto') {
111 // Only auto-open once per session
112 var storageKey = 'bkbg_modal_seen_' + modalId;
113 if (!sessionStorage.getItem(storageKey)) {
114 setTimeout(function () {
115 openModal(modal);
116 sessionStorage.setItem(storageKey, '1');
117 }, autoDelay);
118 }
119 }
120 }
121
122 function init() {
123 toArray(document.querySelectorAll('.bkbg-modal-block')).forEach(initModalBlock);
124 }
125
126 if (document.readyState === 'loading') {
127 document.addEventListener('DOMContentLoaded', init);
128 } else {
129 init();
130 }
131 })();
132