PageSourceSearch

https://www.paulmitchell.com/cdn/shop/t/424/assets/component-mini-cart.js?v=173252806257553476501783361503

js paulmitchell.com collected 2026-09-24 09:49:15 UTC 12,357 bytes, 214 lines download raw bytes

1import{LitElement,createRef,css,html,nothing,ref,unsafeHTML,when}from"@lit";import{storefrontClient}from"@nova/storefront";import styles from"@nova/styles";import{formatPrice,t}from"@nova/utils";class NovaMiniCart extends LitElement{static properties={initialItems:{type:Array},items:{type:Array,state:!0},emptyCartCtaText:{type:String},emptyCartCtaUrl:{type:String},callOutText:{type:String},freeShippingData:{type:Object},compareAtPrices:{type:Object,state:!0},continueSell:{type:Object,state:!0},maxValues:{type:Object,state:!0},latestPrices:{type:Object,state:!0}};get count(){return this.items.reduce((acc,current)=>acc+Number.parseInt(current.quantity),0)}get subtotal(){return this.items.reduce((acc,current)=>{const key=current.key,hasLineDiscount=Number.parseFloat(current.line_level_total_discount||0)>0,latest=this.latestPrices?.[key],priceToUse=!hasLineDiscount&&typeof latest=="number"?latest:Number.parseFloat(current.final_price);return acc+priceToUse*current.quantity},0)}get formattedSubtotal(){return formatPrice(this.subtotal)}async queryVariantData(items){const compareAtPrices={},continueSell={},maxValues={},latestPrices={};await Promise.all(items.map(async item=>{const data=await this.getVariantData(item);data&&(compareAtPrices[item.key]=data.compareAtPrice,continueSell[item.key]=data.inventoryPolicy,maxValues[item.key]=data.max,latestPrices[item.key]=data.price)})),this.compareAtPrices=compareAtPrices,this.continueSell=continueSell,this.maxValues=maxValues,this.latestPrices=latestPrices}getVariantData=async currentItem=>{const variantData=await storefrontClient.getVariantById(currentItem.variant_id);if(!variantData){const matchedItem=this.initialItems.find(item=>item.key===currentItem.key);return matchedItem?{price:Number.parseFloat(matchedItem.final_price||0),compareAtPrice:Number.parseFloat(matchedItem.compare_at_price||0),max:matchedItem.inventory_quantity,inventoryPolicy:matchedItem.inventory_policy!=="continue"}:null}return{price:variantData.price,compareAtPrice:variantData.compareAtPrice,max:variantData.quantityAvailable,inventoryPolicy:variantData.currentlyNotInStock}};get itemCount(){return this.items.reduce((sum,item)=>sum+(Number(item.quantity)||0),0)}$cartDrawer=createRef();$cartItemCountWrapper=createRef();constructor(){super(),document.addEventListener("nova:cart:open",this._handleOpenCart),document.addEventListener("nova:cart:close",this._handleCloseCart),document.addEventListener("nova:cart:refresh",this.refresh),document.addEventListener("discount_kit:cart_changed",this.refresh),this.initialItems=[],this.items=[],this.compareAtPrices={},this.continueSell={},this.maxValues={},this.latestPrices={}}connectedCallback(){super.connectedCallback(),this.emptyCartCtaText=this.getAttribute("empty-cart-cta-text"),this.emptyCartCtaUrl=this.getAttribute("empty-cart-cta-url"),this.callOutText=this.getAttribute("call-out-text"),this._initializeFreeShippingData()}_initializeFreeShippingData(){typeof __NOVA_GLOBAL__<"u"?this.freeShippingData={freeShippingThreshold:__NOVA_GLOBAL__.freeShippingThreshold||0,gwpThreshold:__NOVA_GLOBAL__.gwpThreshold||0}:this.freeShippingData={freeShippingThreshold:0,gwpThreshold:0}}disconnectedCallback(){document.removeEventListener("nova:cart:open",this._handleOpenCart),document.removeEventListener("nova:cart:close",this._handleCloseCart),document.removeEventListener("nova:cart:refresh",this.refresh)}_handleOpenCart=async()=>{await this.updateComplete,this.$cartDrawer.value&&this.$cartDrawer.value.show(),this.$cartItemCountWrapper.value&&this.$cartItemCountWrapper.value.removeAttribute("aria-hidden")};_handleCloseCart=async()=>{await this.updateComplete,this.$cartDrawer.value&&this.$cartDrawer.value.hide()};_hardRefreshCart=async()=>{const res=await fetch("/cart.js");if(!res.ok)throw new Error("Error fetching cart for hard refresh");const cart=await res.json(),updates={};for(const item of cart.items)updates[item.key]=item.quantity;const updateRes=await fetch("/cart/update.js",{method:"POST",headers:{"Content-Type":"application/json",Accept:"application/json"},body:JSON.stringify({updates})});if(!updateRes.ok)throw new Error("Error updating cart for hard refresh");return updateRes.json()};refresh=async()=>{try{await this._hardRefreshCart();const response=await fetch("/cart.js");if(!response.ok)throw new Error("Error fetching cart");const data=await response.json();this.items=data.items,this.queryVariantData(this.items),await this.updateComplete,this.$cartItemCountWrapper.value&&this.$cartItemCountWrapper.value.setAttribute("aria-hidden",!0)}catch(e){console.error("Error fetching cart",e)}};willUpdate(changedProperties){changedProperties.has("initialItems")&&(this.items=this.initialItems,this.queryVariantData(this.items)),changedProperties.has("items")&&(document.dispatchEvent(new CustomEvent("nova:cart:items-count",{detail:{count:this.count}})),document.dispatchEvent(new CustomEvent("nova:changeQuantityFinished"),{bubbles:!0}))}get callOutSection(){const div=document.createElement("div");return div.classList.add("call-out-container"),div.innerHTML=this.callOutText,div}_renderEmptyState=()=>html`
2      <div class="empty-state">
3        <p>${t("cart.general.empty")}</p>
4        <a href=${this.emptyCartCtaUrl} class="btn btn--primary">
5          ${unsafeHTML(this.emptyCartCtaText)}
6        </a>
7      </div>
8    `;continue_shopping;_renderItems=()=>html`
9      ${__NOVA_GLOBAL__.showCartProgressBar===!0?html`<nova-free-shipping-threshold .freeShippingData=${this.freeShippingData}></nova-free-shipping-threshold>`:nothing}
10      ${this.callOutText?this.callOutSection:nothing}
11      <div class="items" part="items">
12        ${this.items.map(item=>{const key=item.key,hasLineDiscount=Number.parseFloat(item.line_level_total_discount||0)>0,latest=this.latestPrices?.[key],priceToUse=!hasLineDiscount&&typeof latest=="number"?latest:Number.parseFloat(item.final_price),mergedItem={...item,final_price:priceToUse};return html`
13            <nova-cart-item
14              @nova-change=${this.refresh}
15              .item=${mergedItem}
16              .compareAtPrice=${this.compareAtPrices[key]||nothing}
17              .inventoryPolicy=${this.continueSell[key]||nothing}
18              .max=${this.maxValues[key]||nothing}
19            >
20            </nova-cart-item>
21          `})}
22      </div>
23      <!-- BEAM START -->
24      <div data-beam-widget="select-nonprofit"  data-cart-empty="true" style="padding:20px 20px; border-top: 0.820513px solid rgb(182, 170, 155);">
25        <beam-select-nonprofit
26          apiKey="pTGFJhV8Ll7U.31ace206-a512-4997-bc68-221745d6510b"
27          storeId="437"
28          lang="en"
29        ></beam-select-nonprofit>
30      </div>
31      <!-- BEAM END -->
32      <div slot="footer" class="summary">
33        ${this.itemCount?html`<div class="summary__item-count">
34              <p>${t("cart.general.order_summary")}</p>
35              <p>${this.itemCount} ${t("cart.general.items")}</p>
36            </div>`:nothing}
37        <div class="summary__total">
38          <p>${t("cart.general.subtotal")}</p>
39          <p>${this.formattedSubtotal}</p>
40        </div>
41        <sl-button href="/checkout">${t("cart.general.checkout")}</sl-button>
42      </div>
43    `;render(){return html`
44      <sl-drawer class="drawer" ${ref(this.$cartDrawer)}>
45      <div slot="label" ${ref(this.$cartItemCountWrapper)}>
46        <span>${t("cart.general.title")}</span>
47       ${this.itemCount?html`<span class="header__item-count">(${this.itemCount})</span>`:nothing}
48      </div>
49        ${when(!this.items.length,this._renderEmptyState,this._renderItems)}
50      </sl-drawer>
51    `}static styles=[styles.flex,css`
52      :host {
53        display: block;
54      }
55      .drawer {
56        --size: 100%;
57        --header-spacing: 30px 30px 20px 30px;
58        --body-spacing: 0px 30px;
59        --footer-spacing: 0;
60      }
61      @media screen and (min-width: 430px) {
62        .drawer {
63          --size: 430px;
64        }
65      }
66      .drawer::part(base) {
67        z-index: var(--nova-z-index-navigation);
68      }
69      .drawer::part(overlay) {
70        fill: var(--nova-utility-overlay, rgba(0, 0, 0, 0.5));
71      }
72      .drawer::part(panel) {
73        background-color: var(--nova-color-brand-beige);
74        overflow: hidden;
75      }
76      .drawer::part(header) {
77        align-items: baseline;
78        border-bottom: 1px solid var(--nova-color-brand-taupe);
79      }
80      .header__item-count {
81        font-size: 18px;
82        font-weight: 400;
83        font-family: var(--nova-font-italic-primary);
84      }
85      .drawer::part(title) {
86        text-transform: uppercase;
87        font-family: var(--nova-font-bold);
88        font-size: 18px;
89        line-height: 130%;
90        font-weight: 700;
91        padding: 20px;
92      }
93      .drawer::part(header-actions) {
94        padding: var(--nova-space-sm);
95      }
96      .drawer::part(close-button) {
97        width: 16px;
98        height: 16px;
99      }
100      .drawer::part(close-button__base) {
101        color: black;
102        padding: 0;
103        font-size: 20px;
104      }
105      .drawer::part(body) {
106        padding: 0;
107      }
108      .empty-state {
109        display: flex;
110        row-gap: var(--nova-space-sm);
111        flex-direction: column;
112        justify-content: flex-start;
113        align-items: center;
114        padding: var(--nova-space-xl) var(--nova-space-sm);
115      }
116      @media screen and (min-width: 768px) {
117        .empty-state {
118          padding-top: var(--nova-space-lg);
119        }
120      }
121      .empty-state p {
122        font-size: 14px;
123        margin: 0;
124      }
125      sl-button,
126      a.btn {
127        display: flex;
128      }
129      sl-button::part(base),
130      a.btn {
131        background-color: var(--nova-color-white);
132        color: var(--nova-color-brand-charcoal);
133        min-height: 35px;
134        width: 100%;
135        border: none;
136        border-radius: var(--nova-border-rounded);
137        align-items: center;
138        font: var(--nova-h8-font);
139        text-transform: uppercase;
140        transition: background-color 0.2s, color 0.2s, border-color 0.2s;
141      }
142      a.btn {
143        padding-inline: var(--nova-button-padding-x);
144        width: auto;
145        text-decoration: none;
146      }
147      sl-button::part(base):hover,
148      a.btn:hover {
149        background-color: var(--nova-color-brand-charcoal);
150        color: var(--nova-color-white);
151      }
152      .call-out-container {
153        padding: var(--nova-space-sm);
154        border-bottom: 1px solid var(--nova-color-brand-taupe);
155      }
156      .call-out-container :where(h1, h2, h3, h4, h5, h6, p) {
157        font: var(--nova-h7-font);
158        text-transform: uppercase;
159        margin: 0;
160      }
161      .call-out-container em {
162        font-weight: 400;
163        font-family: var(--nova-font-italic-primary);
164      }
165      .call-out-container a {
166        color: inherit;
167      }
168      .items {
169        padding-top: 20px;
170      }
171      .summary {
172        border-top: 1px solid var(--nova-color-brand-taupe);
173        padding: var(--nova-space-sm);
174        font-size: 14px;
175        font-style: normal;
176        font-weight: 400;
177        line-height: 140%;
178      }
179      .summary__total,
180      .summary__item-count {
181        display: flex;
182        justify-content: space-between;
183      }
184      .summary__item-count {
185        margin-bottom: var(--nova-space-xsm);
186      }
187      .summary__total {
188        margin-bottom: var(--nova-space-sm);
189      }
190      .summary__total p,
191      .summary__item-count p {
192        margin: 0;
193      }
194      .summary sl-button::part(base) {
195        background-color: var(--nova-color-brand-charcoal);
196        color: var(--nova-color-white);
197        text-transform: uppercase;
198      }
199
200      /* HACK: The main font family 'HelveticaNeueETPro-75-Bold' renders the ® symbol on the baseline instead of raising it like a superscript.
201        Apply a hack to visually raise the ® symbol and make it appear as a true superscript. */
202      .superscript {
203        /* HACK: Need to enforce superscript for heading font family */
204        font-size: 50% !important;
205        vertical-align: super;
206      }
207
208      em .superscript {
209        font-size: inherit !important;
210        vertical-align: baseline;
211        font-style: italic;
212      }
213    `]}customElements.define("nova-mini-cart",NovaMiniCart);
214//# sourceMappingURL=/cdn/shop/t/424/assets/component-mini-cart.js.map?v=173252806257553476501783361503

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.