1/* âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 DRAWER / OFF-CANVAS â widget behavior (Phase-2; moved out of main.js) 3 4 Edge-anchored side panel sharing the modal's overlay mechanics â focus trap 5 + shared background inert/scroll lock stay in main.js (hzo.trapFocus / 6 hzo.lockOverlayBackground; cross-overlay primitives). As a shortcode widget 7 the drawer ALWAYS loads (drawer.php un-gates it). The drawer IIFE was 8 removed from main.js so behavior lives here. commerce.js opens the 9 mini-cart through hzo.openDrawer â that public name is preserved. 10 11 OPTION CONTRACT â data-attributes on `.hzo-drawer`; block pattern and 12 [hzo_drawer] shortcode drive the same behavior. All options default 13 OFF/legacy â attribute-free markup behaves exactly as before: 14 15 data-hzo-drawer-static "true" â backdrop click does NOT close 16 data-hzo-drawer-esc "false" â Esc does NOT close 17 data-hzo-drawer-auto-open ms after init to auto-open (int; 0/absent = 18 off). Once per page load; skipped if another 19 overlay is already open at fire time. 20 21 Side/width are CSS concerns (.hzo-drawer--left, .hzo-drawer__panel--wide); 22 the shortcode maps its side/wide settings to those classes. 23 24 A11y (WAI-ARIA APG Dialog): panel carries role="dialog" + aria-modal; focus 25 trapped while open; background inert + scroll-locked; Esc closes (unless 26 opted out); focus returns to the trigger. Nested overlays stack via the 27 trap stack (a confirm modal over an open drawer works). 28 29 Public API (back-compat): hzo.openDrawer(idOrEl, trigger?) / 30 hzo.closeDrawer(idOrEl) · hzo.drawer = { init, setup, open, close }. 31 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 32(function () { 33 'use strict'; 34 35 function boolAttr(el, name, def) { 36 var v = el.getAttribute('data-hzo-drawer-' + name); 37 if (v === null || v === '') { return def; } 38 return v !== 'false' && v !== '0'; 39 } 40 41 function openDrawer(drawer, trigger) { 42 if (!drawer || drawer.classList.contains('is-open')) { return; } 43 if (!window.hzo || typeof window.hzo.trapFocus !== 'function') { return; } // primitives (main.js) missing â never half-open 44 drawer.classList.add('is-open'); 45 window.hzo.lockOverlayBackground(); 46 var panel = drawer.querySelector('.hzo-drawer__panel') || drawer; 47 var initial = drawer.querySelector('[data-hzo-autofocus]') || 48 drawer.querySelector('.hzo-drawer__close') || 49 panel; 50 drawer._hzoTrap = window.hzo.trapFocus(panel, { 51 onEscape: function () { if (boolAttr(drawer, 'esc', true)) { closeDrawer(drawer); } }, 52 initialFocus: initial, 53 returnFocus: trigger || document.activeElement 54 }); 55 } 56 57 function closeDrawer(drawer) { 58 if (!drawer || !drawer.classList.contains('is-open')) { return; } 59 drawer.classList.remove('is-open'); 60 if (window.hzo && window.hzo.unlockOverlayBackground) { window.hzo.unlockOverlayBackground(); } 61 if (drawer._hzoTrap) { drawer._hzoTrap(); drawer._hzoTrap = null; } // release() returns focus to the trigger 62 } 63 64 /* Portal to <body> + wire close affordances. Idempotent via ready guard. */ 65 function setup(drawer) { 66 if (!drawer || drawer.getAttribute('data-hzo-drawer-ready') === 'true') { return; } 67 drawer.setAttribute('data-hzo-drawer-ready', 'true'); 68 69 if (drawer.parentNode !== document.body) { document.body.appendChild(drawer); } 70 71 // G31: close only when the press ALSO began on the backdrop (a select/drag 72 // starting inside the panel and released on the backdrop must not close). 73 var pressTarget = null; 74 drawer.addEventListener('mousedown', function (e) { pressTarget = e.target; }); 75 drawer.addEventListener('click', function (e) { 76 if (e.target === drawer) { // backdrop click 77 if (pressTarget === drawer && !boolAttr(drawer, 'static', false)) { closeDrawer(drawer); } 78 pressTarget = null; 79 return; 80 } 81 if (e.target.closest('[data-hzo-drawer-close]')) { closeDrawer(drawer); } 82 }); 83
84 var delay = parseInt(drawer.getAttribute('data-hzo-drawer-auto-open') || '', 10); 85 if (delay > 0) { 86 setTimeout(function () { 87 if (document.body.classList.contains('hzo-overlay-open')) { return; } 88 openDrawer(drawer, null); 89 }, delay); 90 } 91 } 92 93 function init(scope) { 94 var drawers = (scope || document).querySelectorAll('.hzo-drawer'); 95 for (var d = 0; d < drawers.length; d++) { setup(drawers[d]); } 96 } 97 98 // Delegated triggers â dynamically-inserted triggers work too. 99 document.addEventListener('click', function (e) { 100 var trigger = e.target.closest('[data-hzo-drawer-open]'); 101 if (!trigger) { return; } 102 var drawer = document.getElementById(trigger.getAttribute('data-hzo-drawer-open')); 103 if (!drawer) { return; } 104 e.preventDefault(); 105 setup(drawer); // covers drawers injected after init (AJAX content) 106 openDrawer(drawer, trigger); 107 }); 108 109 if (document.readyState === 'loading') { 110 document.addEventListener('DOMContentLoaded', function () { init(); }); 111 } else { 112 init(); 113 } 114 115 // Shared engine namespace. openDrawer/closeDrawer keep their historical 116 // names (commerce.js mini-cart depends on them); hzo.drawer is the 117 // widget-convention surface for dynamically-injected instances. 118 window.hzo = window.hzo || {}; 119 window.hzo.openDrawer = function (idOrEl, trigger) { 120 var el = typeof idOrEl === 'string' ? document.getElementById(idOrEl) : idOrEl; 121 if (el) { setup(el); } 122 openDrawer(el, trigger); 123 }; 124 window.hzo.closeDrawer = function (idOrEl) { 125 closeDrawer(typeof idOrEl === 'string' ? document.getElementById(idOrEl) : idOrEl); 126 }; 127 window.hzo.drawer = { init: init, setup: setup, open: window.hzo.openDrawer, close: window.hzo.closeDrawer }; 128})();
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.