1/* global BREAKDANCE_ADDED_TO_CART */ 2(function () { 3 const { mergeObjects, matchMedia } = BreakdanceFrontend.utils; 4 5 class BreakdanceMiniCart { 6 defaultOptions = { 7 full_screen_at: "breakpoint_phone_landscape", 8 style: "dropdown", 9 cart: { 10 open_cart_on_add: false, 11 }, 12 }; 13 14 constructor(selector, options) { 15 this.open = this.open.bind(this); 16 this.onEnterClick = this.onEnterClick.bind(this); 17 this.onToggleClick = this.onToggleClick.bind(this); 18 this.onCloseClick = this.onCloseClick.bind(this); 19 this.onEscClick = this.onEscClick.bind(this); 20 this.onMaybeClickOutside = this.onMaybeClickOutside.bind(this); 21 this.onFocusOut = this.onFocusOut.bind(this); 22 23 this.selector = selector; 24 this.options = mergeObjects(this.defaultOptions, options); 25 this.activeClass = "bde-mini-cart-offcanvas--active"; 26 27 this.init(); 28 } 29 30 getScrollBarWidth() { 31 // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes 32 const documentWidth = document.documentElement.clientWidth 33 return Math.abs(window.innerWidth - documentWidth) 34 } 35 36 update(options) { 37 this.options = mergeObjects(this.defaultOptions, options); 38 this.destroy(); 39 this.init(); 40 } 41 42 destroy() { 43 this.unbindListeners(); 44 } 45 46 isOpen() { 47 return this.offcanvas.classList.contains(this.activeClass); 48 } 49 50 toggle() { 51 if (this.isOpen()) { 52 this.close(); 53 } else { 54 this.open(); 55 } 56 } 57 58 open() { 59 if (this.shouldStopScrolling()) { 60 this.setBalancePadding(); 61 document.documentElement.classList.add("bde-stop-scrolling"); 62 } 63 64 this.offcanvas.classList.add(this.activeClass); 65 document.documentElement.classList.add("is-breakdance-mini-cart-open"); 66 } 67 68 close() { 69 if (!this.isOpen()) return; 70 71 this.offcanvas.classList.remove(this.activeClass); 72 document.documentElement.classList.remove("is-breakdance-mini-cart-open"); 73 74 // Remove padding after transition to prevent flickering 75 this.offcanvas.addEventListener("transitionend", () => { 76 document.documentElement.classList.remove("bde-stop-scrolling"); 77 document.body.style.paddingRight = null; 78 }, { once: true }); 79 } 80 81 isFocusWithin() { 82 return this.element.contains(document.activeElement); 83 } 84 85 shouldStopScrolling() { 86 const isFullscreen = matchMedia(this.options.full_screen_at); 87 return isFullscreen || this.options.style === "sidebar"; 88 } 89 90 setBalancePadding() { 91 const element = document.body; 92 const scrollbarWidth = this.getScrollBarWidth(); 93 94 if (window.innerWidth <= element.clientWidth + scrollbarWidth) { 95 const currPadding = window.getComputedStyle(element).getPropertyValue("padding-right"); 96 const currPaddingAsNum = Number.parseFloat(currPadding); 97 const newPadding = currPaddingAsNum + scrollbarWidth; 98 99 // Give padding to element to balance the hidden scrollbar width 100 if (newPadding > 0) element.style.paddingRight = `${newPadding}px`; 101 } 102 } 103 104 focusInside() { 105 const firstNode = this.element.querySelector(".bde-mini-cart-item-thumbnail, .bde-mini-cart-item-title"); 106 const container = this.element.querySelector(".bde-mini-cart-offcanvas-body"); 107 108 container.addEventListener("transitionend", () => { 109 firstNode?.focus(); 110 }, { once: true }); 111 } 112 113 focusToggle() { 114 this.toggleButton.focus(); 115 } 116 117 onEnterClick(event) { 118 if (!["Enter", " "].includes(event.key)) return; 119 event.preventDefault(); 120 121 this.toggle(); 122 this.focusInside(); 123 this.element.addEventListener("focusout", this.onFocusOut); 124 } 125 126 onToggleClick(event) { 127 event.preventDefault(); 128 this.toggle(); 129 } 130 131 onCloseClick() { 132 this.close(); 133 } 134 135 onEscClick(event) { 136 if (event.key !== "Escape") return; 137 if (!this.isOpen()) return; 138 139 this.close(); 140 this.focusToggle(); 141 } 142 143 onMaybeClickOutside(event) { 144 if (!this.element.contains(event.target)) { 145 this.close(); 146 } 147 } 148 149 onFocusOut(event) { 150 if (!event.relatedTarget) return; 151 if (this.element.contains(event.relatedTarget)) return; 152 153 this.close(); 154 this.element.removeEventListener("focusout", this.onFocusOut); 155 } 156 157 bindListeners() { 158 if (!this.toggleButton || !this.closeButton || !this.overlay) { 159 return; 160 } 161 162 this.toggleButton.addEventListener("click", this.onToggleClick); 163 this.toggleButton.addEventListener("keydown", this.onEnterClick); 164 this.closeButton.addEventListener("click", this.onCloseClick); 165 this.overlay.addEventListener("click", this.onCloseClick); 166 167 addEventListener("click", this.onMaybeClickOutside); 168 addEventListener("keydown", this.onEscClick); 169 170 if (this.options.cart.open_cart_on_add) { 171 // Ajax add to cart 172 jQuery(document.body).on("added_to_cart", this.open); 173 174 // Non-ajax add to cart
175 if (typeof BREAKDANCE_ADDED_TO_CART !== "undefined") this.open(); 176 } 177 } 178 179 unbindListeners() { 180 if (!this.toggleButton || !this.closeButton || !this.overlay) { 181 return; 182 } 183 184 this.toggleButton.removeEventListener("click", this.onToggleClick); 185 this.toggleButton.removeEventListener("keydown", this.onEnterClick); 186 this.closeButton.removeEventListener("click", this.onCloseClick); 187 this.overlay.removeEventListener("click", this.onToggleClick); 188 this.element.removeEventListener("focusout", this.onFocusOut); 189 190 removeEventListener("click", this.onMaybeClickOutside); 191 removeEventListener("keydown", this.onEscClick); 192 193 jQuery(document.body).off("added_to_cart", this.open); 194 } 195 196 init() { 197 this.element = document.querySelector(this.selector); 198 199 if (!this.element) return; 200 201 this.toggleButton = this.element.querySelector(".bde-mini-cart-toggle"); 202 this.closeButton = this.element.querySelector(".bde-mini-cart-offcanvas__close-button"); 203 this.offcanvas = this.element.querySelector(".bde-mini-cart-offcanvas"); 204 this.overlay = this.element.querySelector(".bde-mini-cart-offcanvas-overlay"); 205 206 this.bindListeners(); 207 } 208 } 209 210 window.BreakdanceMiniCart = BreakdanceMiniCart; 211})();
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.