1document.addEventListener('alpine:init', () => { 2 Alpine.data('ThemeModule_CartItems', () => { 3 return { 4 itemsRoot: null, 5 locked: null, 6 init() { 7 this.itemsRoot = this.$root; 8 9 window.addEventListener('shapes:modalcart:afteradditem', (e) => { 10 Alpine.morph( 11 this.itemsRoot, 12 querySelectorInHTMLString( 13 '[data-cart-items]', 14 e.detail.response.sections['cart-items'] 15 ).outerHTML 16 ); 17 }); 18 19 window.addEventListener('shapes:modalcart:cartqtychange', (e) => { 20 Alpine.morph( 21 this.itemsRoot, 22 querySelectorInHTMLString( 23 '[data-cart-items]', 24 e.detail.response.sections['cart-items'] 25 ).outerHTML 26 ); 27 28 this.$nextTick(() => { 29 this.itemsRoot.querySelectorAll('input').forEach((inputEl) => { 30 inputEl.value = inputEl.getAttribute('value'); 31 }); 32 }); 33 34 const liveRegionText = parseDOMFromString( 35 e.detail.response.sections['cart-live-region'] 36 ).firstElementChild.textContent; 37 38 const cartStatus = document.getElementById('cart-live-region-text'); 39 40 cartStatus.textContent = liveRegionText; 41 42 cartStatus.setAttribute('aria-hidden', false); 43 44 if (e.detail.originalTarget) { 45 this.$nextTick(() => { 46 if (!this.itemsRoot.contains(e.detail.originalTarget)) { 47 let focusRoot; 48 49 if (this.itemsRoot.closest('[role="dialog"]')) { 50 focusRoot = 51 this.itemsRoot.closest('[role="dialog"]').parentNode; 52 } else { 53 focusRoot = this.itemsRoot; 54 } 55 56 this.$focus.within(focusRoot).first(); 57 } 58 }); 59 } 60 }); 61 }, 62 itemQuantityChange(key, value) { 63 if (this.locked) return; 64 65 const lineItemEl = this.$el.closest('li[data-cart-item-key]'); 66 const id = key ? key : lineItemEl.getAttribute('data-cart-item-key'), 67 quantity = parseInt(!isNaN(value) ? value : this.$el.value, 10), 68 sections = 'cart-items,cart-footer,cart-item-count,cart-live-region'; 69 70 const config = fetchConfigDefaults(); 71 72 config.body = JSON.stringify({ 73 id, 74 quantity, 75 sections, 76 sections_url: window.location.pathname, 77 }); 78 79 const lastValue = parseInt(this.$el.dataset.lastValue, 10); 80 81 if (quantity !== lastValue) { 82 lineItemEl.classList.add('opacity-50', 'cursor-progress'); 83 84 const currentCount = Alpine.raw(Alpine.store('cart_count').count); 85 86 this.locked = lineItemEl; 87 88 fetch(theme.routes.cart_change_url, config) 89 .then((res) => res.json()) 90 .then((data) => { 91 if (data.item_count === currentCount) { 92 // Quantity change had no effect -- most likely, there is no more inventory 93 const errorEl = lineItemEl.querySelector( 94 '[data-cart-quantity-error]' 95 ); 96 97 errorEl.textContent = theme.strings.cartAddError.replace( 98 '{{ title }}', 99 errorEl.dataset.itemTitle 100 ); 101 102 errorEl.classList.remove('hidden'); 103 } else { 104 document.body.dispatchEvent( 105 new CustomEvent('shapes:modalcart:cartqtychange', { 106 bubbles: true, 107 detail: { response: data, originalTarget: lineItemEl }, 108 }) 109 ); 110 } 111 }) 112 .catch(() => { 113 document.getElementById('cart-errors').textContent = 114 theme.strings.cartError; 115 }) 116 .finally(() => { 117 this.locked = null; 118 }); 119 } 120 }, 121 removeItem(key) { 122 this.itemQuantityChange(key, 0); 123 }, 124 _adjustQty(el, operation) { 125 const inputEl = el 126 .closest('li[key]') 127 .querySelector('input[type="number"]'); 128
129 const lastValue = parseInt(inputEl.dataset.lastValue, 10); 130 131 let newValue; 132 133 if (operation == 'increment') { 134 newValue = lastValue + 1; 135 } 136 137 if (operation == 'decrement') { 138 if (lastValue > 1) { 139 newValue = lastValue - 1; 140 } else { 141 newValue = 1; 142 } 143 } 144 145 if (operation == 'remove') { 146 newValue = 0; 147 } 148 149 inputEl.value = newValue; 150 151 // this.$nextTick(() => { 152 inputEl.dispatchEvent(new Event('change', { bubbles: true })); 153 // }); 154 }, 155 increment() { 156 this._adjustQty(this.$el, 'increment'); 157 }, 158 decrement() { 159 this._adjustQty(this.$el, 'decrement'); 160 }, 161 remove() { 162 this._adjustQty(this.$el, 'remove'); 163 }, 164 }; 165 }); 166});
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.