1/* âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 POPOVER â widget behavior (Phase-2 widget system; moved out of main.js) 3 4 Self-contained, no hard dependencies. As a shortcode widget the popover 5 ALWAYS loads: popover.php un-gates it via the hzostudio_component_markers 6 filter, because [hzo_popover] can render from surfaces the loader's 7 post_content scan can't see. The popover IIFE was removed from main.js so 8 behavior lives here. The edge-flip positioner stays in main.js 9 (hzo.flipIfClipped) â it is a cross-component primitive shared with the 10 tooltip â so its use is guarded at event time (never a hard dep; main.js is 11 conditionally enqueued and an unregistered dep would silently drop this 12 script). 13 14 BEHAVIOR (unchanged from main.js): a richer dropdown surface â click/focus 15 toggles a panel that MAY hold interactive content. NOT trapped (Tab c
15an 16 leave); Esc + click-outside close; focus returns to the trigger on Esc; 17 one popover open at a time. Markup contract (component-patterns §44): 18 .hzo-popover > .hzo-popover__trigger[aria-controls][aria-expanded] 19 + .hzo-popover__panel[id]. Add .hzo-popover__panel--right to right-align. 20 21 OPTION CONTRACT â every setting is a data-attribute on `.hzo-popover`, so 22 the block pattern (author writes attributes) and the [hzo_popover] shortcode 23 (popover.php generates them from settings) drive the SAME behavior. Every 24 option defaults OFF/legacy â attribute-free markup behaves exactly as before: 25 26 data-hzo-popover-flip "false" â skip the viewport edge-flip check on 27 open (panel stays in its authored placement even 28 when it would clip). Default: flip when clipped, 29 exactly as before. 30 31 A11y (WAI-ARIA APG Disclosure): trigger carries aria-expanded (+ 32 aria-controls in markup); Esc closes and returns focus to the trigger; 33 click-outside closes without moving focus; the panel is NOT a modal so 34 focus is never trapped. Esc/click-outside dismiss is NOT configurable â 35 a popover can never become an inescapable surface (WCAG 2.1.2). 36 37 Public API (new â the old main.js IIFE exposed nothing): 38 hzo.popover = { init, setup, open, close } (init(scope) for AJAX content) 39 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 40(function () { 41 'use strict'; 42 43 var openPop = null; // one popover open at a time (module-wide) 44 45 function boolAttr(el, name, def) { 46 var v = el.getAttribute('data-hzo-popover-' + name); 47 if (v === null || v === '') { return def; } 48 return v !== 'false' && v !== '0'; 49 } 50 51 function panelOf(pop) { return pop.querySelector('.hzo-popover__panel'); } 52 function triggerOf(pop) { return pop.querySelector('.hzo-popover__trigger'); } 53 54 function open(pop) { 55 if (!pop) { return; } 56 if (openPop && openPop !== pop) { close(openPop, false); } 57 pop.classList.add('is-open'); 58 var t = triggerOf(pop); 59 if (t) { t.setAttribute('aria-expanded', 'true'); } 60 // Edge-flip via the shared main.js primitive â guarded at event time 61 // (main.js is conditionally enqueued; missing primitive = no flip, 62 // the popover still opens in its default placement). 63 var panel = panelOf(pop); 64 if (panel && boolAttr(pop, 'flip', true) && 65 window.hzo && typeof window.hzo.flipIfClipped === 'function') { 66 window.hzo.flipIfClipped(panel, 'hzo-popover__panel--flip', 'hzo-popover__panel--flip-x'); 67 } 68 openPop = pop; 69 } 70 71 function close(pop, returnFocus) { 72 if (!pop) { return; } 73 pop.classList.remove('is-open'); 74 var t = triggerOf(pop); 75 if (t) { t.setAttribute('aria-expanded', 'false'); } 76 if (returnFocus && t) { t.focus(); } 77 if (openPop === pop) { openPop = null; } 78 } 79 80 /* Wire one popover's trigger + Esc handling. Idempotent via the ready 81 guard (only set once a trigger exists, so a popover whose trigger is 82 injected later can still be wired by a re-init). */ 83 function setup(pop) { 84 if (!pop || pop.getAttribute('data-hzo-popover-ready') === 'true') { return; } 85 var t = triggerOf(pop); 86 if (!t) { return; } // no trigger â nothing to wire (matches old behavior) 87 pop.setAttribute('data-hzo-popover-ready', 'true'); 88 89 if (!t.hasAttribute('aria-expanded')) { t.setAttribute('aria-expanded', 'false'); } 90 91 t.addEventListener('click', function () { 92 if (pop.classList.contains('is-open')) { close(pop, false); } else { open(pop); } 93 }); 94 95 // Esc closes + returns focus to the trigger. stopPropagation so an 96 // enclosing overlay (modal/drawer) doesn't also close on the same 97 // keypress â the popover is the innermost dismissable surface. 98 pop.addEventListener('keydown', function (e) { 99 if (e.key === 'Escape' && pop.classList.contains('is-open')) { 100 e.stopPropagation(); 101 close(pop, true); 102 } 103 }); 104 } 105 106 function init(scope) { 107 var pops = (scope || document).querySelectorAll('.hzo-popover'); 108 for (var i = 0; i < pops.length; i++) { setup(pops[i]); } 109 } 110 111 // Click-outside closes the open popover (no focus move) â document-level, 112 // so it also covers popovers injected after init. 113 document.addEventListener('click', function (e) { 114 if (openPop && !openPop.contains(e.target)) { close(openPop, false); } 115 }); 116 117 if (document.readyState === 'loading') { 118 document.addEventListener('DOMContentLoaded', function () { init(); }); 119 } else { 120 init(); 121 } 122 123 // Shared engine namespace (widget-convention surface; the old main.js 124 // popover IIFE exposed no public names, so nothing historical to preserve). 125 window.hzo = window.hzo || {}; 126 window.hzo.popover = { init: init, setup: setup, open: open, close: close }; 127})();
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.