1/* âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ 2 BUTTONS â widget behavior (Phase-2 widget system; moved out of main.js) 3 4 Self-contained, no hard dependencies. As a shortcode widget the buttons 5 component ALWAYS loads: buttons.php un-gates it via the 6 hzostudio_component_markers filter (a no-op today â buttons never had a 7 gate entry â kept for convention uniformity). The add-to-cart IIFE was 8 moved here from main.js; attribute-free markup behaves exactly as before. 9 10 ADD-TO-CART STATE MACHINE (UI surface â no real cart POST). Drives the 11 default â loading â added flow by class + aria, exposed for an integration 12 layer (assets/js/commerce.js on WC-active sites) to wire to a real cart. 13 A button with `data-hzo-add-to-cart` enters `.is-loading` (aria-busy) on 14 click and dispatches a bubbling `hzo:addtocart` CustomEvent 15 (detail { button }); the integration does the real POST, then calls 16 hzo.cart.added(btn) / hzo.cart.reset(btn). With 17 `data-hzo-add-to-cart-demo` it self-resolves so the gallery shows the full 18 flow. aria-disabled buttons (out-of-stock) and in-flight buttons never 19 (re)fire. Markup contract: component-patterns.md §8.10. 20 21 OPTION CONTRACT â settings are data-attributes on the button element, so 22 the block-pattern surface (author writes attributes) and the [hzo_button] 23 shortcode (buttons.php emits the markup) drive the SAME behavior. Every 24 option defaults OFF/legacy: 25 26 data-hzo-buttons-message custom "added" toast text 27 (default "Added to cart") 28 data-hzo-buttons-rest "in-cart" â the added state is sticky instead 29 of reverting (set the label yourself, e.g. 30 "In cart") â the declarative twin of 31 hzo.cart.added(btn, { rest: 'in-cart' }) 32 data-hzo-buttons-added-ms ms the â confirmation shows before the button 33 settles back to default (int; default 1600) 34
35 Explicit opts passed to hzo.cart.added(btn, opts) always win â the 36 attributes only fill opts an integration left unset â so every existing 37 caller is unaffected. 38 39 A11y (WAI-ARIA APG Button pattern): the component uses native <button> / 40 <a> elements, so roles and the Enter/Space keyboard map come for free 41 (click fires natively). In-flight state is announced via aria-busy; 42 out-of-stock is aria-disabled plus a text label (never colour alone). 43 Reduced motion: the loading/added visuals are pure class-driven state 44 swaps â buttons.css slows the spinner and freezes the icon-slide arrow 45 under prefers-reduced-motion; this file animates nothing. 46 47 Behavior is fully event-delegated (ONE document-level click listener bound 48 once at module scope), so dynamically injected buttons (AJAX loops, 49 product-grid swaps) work with no re-init. init/setup exist for the widget 50 convention and stamp the idempotency marker (data-hzo-buttons-ready) on 51 add-to-cart buttons; they wire nothing per-instance today. 52 53 Shared primitives (hzo.toast â components/toast/toast.js) are guarded at 54 event time, never assumed at parse time: script load order across 55 component bundles and the conditionally-enqueued main.js is NOT guaranteed. 56 57 Public API (back-compat â commerce.js and integrations call these): 58 hzo.cart = { loading, added, reset } (historical names, verbatim) 59 hzo.buttons = { init, setup, loading, added, reset } 60 âââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââââ */ 61(function () { 62 'use strict'; 63 64 var ADDED_MS = 1600; 65 66 function loading(btn) { 67 btn.classList.remove('is-added'); 68 btn.classList.add('is-loading'); 69 btn.setAttribute('aria-busy', 'true'); 70 } 71 72 function added(btn, opts) { 73 opts = opts || {}; 74 btn.classList.remove('is-loading'); 75 btn.setAttribute('aria-busy', 'false'); 76 btn.classList.add('is-added'); 77 if (window.hzo && window.hzo.toast) { 78 window.hzo.toast(opts.message || btn.getAttribute('data-hzo-buttons-message') || 'Added to cart', { type: 'success' }); 79 } 80 // Brief confirmation, then settle back to default â the toast persists 81 // the outcome. Pass { rest: 'in-cart' } (or author 82 // data-hzo-buttons-rest="in-cart") to keep a sticky state (label is the 83 // integration's call, e.g. "In cart"). 84 var rest = opts.rest || btn.getAttribute('data-hzo-buttons-rest') || ''; 85 if (rest === 'in-cart') { return; } 86 var ms = opts.ms || parseInt(btn.getAttribute('data-hzo-buttons-added-ms') || '', 10) || ADDED_MS; 87 window.setTimeout(function () { btn.classList.remove('is-added'); }, ms); 88 } 89 90 function reset(btn) { 91 btn.classList.remove('is-loading', 'is-added'); 92 btn.setAttribute('aria-busy', 'false'); 93 } 94 95 /* No per-instance wiring is needed (behavior is delegated below) â setup 96 just stamps the widget-convention ready marker, idempotently. */ 97 function setup(btn) { 98 if (!btn || btn.getAttribute('data-hzo-buttons-ready') === 'true') { return; } 99 btn.setAttribute('data-hzo-buttons-ready', 'true'); 100 } 101 102 function init(scope) { 103 var btns = (scope || document).querySelectorAll('[data-hzo-add-to-cart]'); 104 for (var i = 0; i < btns.length; i++) { setup(btns[i]); } 105 } 106 107 // Delegated at the document level, bound ONCE at module scope â 108 // dynamically-inserted buttons work with no re-init. 109 document.addEventListener('click', function (e) { 110 var btn = e.target.closest ? e.target.closest('[data-hzo-add-to-cart]') : null; 111 if (!btn) { return; } 112 e.preventDefault(); 113 // Out-of-stock (aria-disabled) or an in-flight add never (re)fires. 114 if (btn.getAttribute('aria-disabled') === 'true' || btn.classList.contains('is-loading')) { return; } 115 setup(btn); // covers buttons injected after init (AJAX content) 116 loading(btn); 117 btn.dispatchEvent(new CustomEvent('hzo:addtocart', { bubbles: true, detail: { button: btn } })); 118 // Self-contained demo (no integration listening): resolve to "added". 119 if (btn.hasAttribute('data-hzo-add-to-cart-demo')) { 120 window.setTimeout(function () { added(btn); }, 700); 121 } 122 }); 123 124 if (document.readyState === 'loading') { 125 document.addEventListener('DOMContentLoaded', function () { init(); }); 126 } else { 127 init(); 128 } 129 130 // Shared engine namespace. hzo.cart keeps its historical shape verbatim 131 // (commerce.js resolves add-to-cart through it at event time); hzo.buttons 132 // is the widget-convention surface for dynamically-injected instances. 133 window.hzo = window.hzo || {}; 134 window.hzo.cart = window.hzo.cart || {}; 135 window.hzo.cart.loading = loading; 136 window.hzo.cart.added = added; 137 window.hzo.cart.reset = reset; 138 window.hzo.buttons = { init: init, setup: setup, loading: loading, added: added, reset: reset }; 139})();
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.