PageSourceSearch

https://www.americaneagle.ae/scripts/minicart/Minicart.js

js americaneagle.ae collected 2026-10-02 13:25:45 UTC 5,682 bytes, 182 lines download raw bytes

1/* eslint-disable import/no-cycle, camelcase, max-classes-per-file,
2react/no-unused-state, react/destructuring-assignment */
3import {
4  h, Component, Fragment, render,
5} from '../preact.js';
6import htm from '../htm.js';
7
8import { store } from './api.js';
9import { loadCSS } from '../aem.js';
10import {
11  getCart,
12  removeItemFromCart,
13} from './cart.js';
14
15const html = htm.bind(h);
16let cartVisible = false;
17
18function ConfirmDeletionOverlay(props) {
19  const { close, confirm } = props;
20
21  return html`<div class="overlay-background">
22    <div class="overlay">
23      <button class="close" onclick=${close}>Close</button>
24      <div class="content">Are you sure you would like to remove this item from the shopping cart?</div>
25      <div class="actions">
26        <button onclick=${close}>Cancel</button>
27        <button onclick=${confirm}>OK</button>
28      </div>
29    </div>
30  </div>`;
31}
32
33class ProductCard extends Component {
34  constructor(props) {
35    super();
36    this.state = {
37      quantity: props.item.quantity,
38      quantityValid: true,
39      confirmDelete: false,
40    };
41  }
42
43  componentDidUpdate(prevProps) {
44    // Check if quantity props have changed
45    if (prevProps.item.quantity !== this.props.item.quantity) {
46      this.setState({ quantity: this.props.item.quantity, quantityValid: true });
47    }
48  }
49
50  static renderImage = (item) => {
51    let image;
52    if (item.configured_variant?.thumbnail?.url) {
53      image = item.configured_variant.thumbnail.url;
54    } else if (item.product?.thumbnail?.url) {
55      image = item.product.thumbnail.url;
56    }
57
58    const url = new URL(image);
59    url.search = '';
60
61    return html`<picture>
62      <source type="image/webp" srcset="${url}?fit=bounds&height=131&width=105&bg-color=255,255,255&format=webply&optimize=medium" />
63      <img class="product-image-photo" src="${url}?fit=bounds&height=131&width=105&quality=100&bg-color=255,255,255" max-width="105" max-height="131" alt=${item.product.name} />
64    </picture>`;
65  };
66
67  render(props, state) {
68    const { item, index, formatter } = props;
69    const {
70      product, prices, configurable_options,
71    } = props.item;
72
73    return html`<li>
74      <div class="minicart-product">
75        <div class="image">
76          <a href=${`/products/${product.url_key}/${product.sku.toLowerCase()}`}>${ProductCard.renderImage(item)}</a>
77        </div>
78        <div class="info">
79          <div class="name"><a href=${`/products/${product.url_key}/${product.sku.toLowerCase()}`} dangerouslySetInnerHTML=${{ __html: product.name }} /></div>
80          ${configurable_options && (html`<div class="options">
81            <input type="checkbox" id="see-options-${index}" />
82            <label for="see-options-${index}">See Details</label>
83            <dl>
84              ${configurable_options.map(({ option_label, value_label }) => html`<${Fragment}>
85                <dt>${option_label}:</dt>
86                <dd>${value_label}</dd>
87              <//>`)}
88            </dl>
89          </div>`)}
90          <div class="price">${formatter.format(prices.price.value)}</div>
91          <div class="quantity">
92            QTY: ${state.quantity}
93          </div>
94        </div>
95        <div class="actions">
96          <button>Edit</button>
97          <button onclick=${() => this.setState({ confirmDelete: true })}>Remove</button>
98        </div>
99        ${state.confirmDelete && html`<${ConfirmDeletionOverlay} close=${() => this.setState({ confirmDelete: false })} confirm=${() => this.props.api.removeItemFromCart(item.uid, 'Cart Quick View')} />`}
100      </div>
101    </li>`;
102  }
103}
104
105export class Minicart extends Component {
106  constructor() {
107    super();
108    this.state = {
109      loading: true,
110      cart: {},
111    };
112    this.formatter = new Intl.NumberFormat('en-US', {
113      style: 'currency',
114      currency: 'USD',
115    });
116  }
117
118  componentDidMount() {
119    // Subscribe to store changes
120    this.props.api.store.subscribe((cart) => {
121      this.setState({ cart, loading: false });
122    });
123  }
124
125  render(props, state) {
126    if (!props.visible) {
127      return null;
128    }
129
130    const { close } = props.api;
131
132    const { cart } = state;
133    if (!cart.items || cart.items.length === 0) {
134      return html`<div class="minicart-panel empty">
135        <div class="minicart-header">
136          <button class="close" onClick=${() => close(false)}>Close</button>
137        </div>
138        <div className="cart-empty">You have no shopping items in your cart.</div>
139      </div>`;
140    }
141
142    return html`<div class="minicart-panel">
143      <div class="minicart-header">
144        <div class="title">Added to your bag</div>
145        <div class="subtotal">Sub-Total: <span class="price">${this.formatter.format(cart.totals.subtotal_excluding_tax.value)}</span></div>
146        <button class="close" onClick=${() => close(false)}>Close</button>
147      </div>
148      <ul class="minicart-list">
149        ${state.cart.items.map((item, index) => html`<${ProductCard} index=${index} item=${item} formatter=${this.formatter} api=${props.api} />`)}
150      </ul>
151      <div class="minicart-actions">
152        <button onClick=${() => { window.location.href = '/cart'; }}>Go to cart</button>
153      </div>
154    </div>`;
155  }
156}
157
158export async function toggle(refetch = true) {
159  if (!cartVisible) {
160    // Load CSS
161    await loadCSS('/styles/minicart.css');
162
163    if (refetch && store.getCartId()) {
164      await getCart();
165    }
166  }
167
168  cartVisible = !cartVisible;
169
170  const app = html`<${Minicart} visible=${cartVisible} api=${{
171    store,
172    removeItemFromCart,
173    close: toggle,
174  }} />`;
175  render(app, document.querySelector('.minicart-wrapper'));
176}
177
178export async function showCart() {
179  if (!cartVisible) {
180    await toggle(false);
181  }
182}

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.