PageSourceSearch

https://horze.eu/cdn/shop/t/648/assets/cart-promotions-BULa_J4n.js

js horze.eu collected 2026-10-01 14:59:55 UTC 34,504 bytes, 539 lines download raw bytes

1import{a as g,t as G}from"./vendor-lit-reactive-element-Cjcwuima.js";import{b as C,x as l,E as h,e as v}from"./vendor-lit-html-nFsY_NSA.js";import{r as A}from"./vendor-lit-element-CtcYdrtK.js";import"./ds-Bp-orcBC.js";import{p as S,s as $,f as k,r as P}from"./main-BNay8OD8.js";import{t as o}from"./translations-CBNc1gDz.js";import{c as w}from"./combinedSelectLabels.utils-C0keL9fF.js";import{f as T,b as V,i as y,a as R}from"./fetchGiftCollectionProducts-ETwy0cyl.js";import"./vendor-lit-context-BDzdL6mz.js";import"./vendor-vue-runtime-dom-Dqgu9pQP.js";import"./vendor-vue-runtime-core-DAnRjgOf.js";import"./vendor-vue-reactivity-Df4oAq-x.js";import"./vendor-vue-shared-D3_Apz_L.js";import"./client-CYgaPOyd.js";const _={color:[],size:["color"],length:["color","size"]};function L(e,t,i){return!e||!t?!0:_[i].every(n=>{const s=w(e,n),c=w(t,n);return s===null||c===null?!0:s===c})}const z={tiers:[],restrictedTiers:[],nextTier:null,maxThreshold:0,currentValue:0};function O(e){const t=new Map;for(const i of[...e.claimed,...e.available,...e.next])t.has(i.code)||t.set(i.code,i);return[...t.values()].sort((i,n)=>i.threshold-n.threshold)}function E(e,t=0){var x;if(!(e!=null&&e.campaignActive))return z;const i=O(e);if(i.length===0)return z;const n=i.filter(a=>a.hasIncludes!==!0),s=i.filter(a=>a.hasIncludes===!0);if(n.length===0)return{...z,restrictedTiers:s.map(a=>({tier:a,isUnlocked:a.reached,isNext:!a.reached})),nextTier:s.find(a=>!a.reached)??null};const c=n.find(a=>!a.reached),d=n.map(a=>({tier:a,isUnlocked:a.reached,isNext:a===c})),u=n[n.length-1].threshold,p=c?c.currentValue:u,r=Math.max(0,p+t),m=t===0?d:d.map(a=>({...a,tier:{...a.tier,remaining:Math.max(0,a.tier.threshold-r)}}));return{tiers:m,restrictedTiers:s.map(a=>({tier:a,isUnlocked:a.reached,isNext:!a.reached})),nextTier:(c?((x=m.find(a=>a.isNext))==null?void 0:x.tier)??c:null)??s.find(a=>!a.reached)??null,maxThreshold:u,currentValue:r}}const I={offers:[],chosenCount:0,choosableCount:0,unlockedCount:0,lockedCount:0,nextLockedThreshold:null};function D(e){if(!(e!=null&&e.campaignActive))return I;const t=new Map(e.claimed.map(r=>[r.code,r])),i=O(e).filter(r=>r.type==="free_gift").map(r=>{const m=t.get(r.code),x=(m==null?void 0:m.claimedQuantity)??0,a=(m==null?void 0:m.remainingQuantity)??r.remainingQuantity??(x>0?0:1);return{tier:r,isUnlocked:r.reached,claimedCount:x,openCount:a,isActionable:r.reached&&a>0}});if(i.length===0)return I;const n=(r,m)=>r.reduce((x,a)=>x+m(a),0),s=i.filter(r=>r.isUnlocked),c=i.filter(r=>!r.isUnlocked),d=n(i,r=>r.claimedCount),u=n(s,r=>r.openCount),p=c.map(r=>r.tier.threshold);return{offers:i,chosenCount:d,choosableCount:u,unlockedCount:d+u,lockedCount:n(c,r=>r.openCount),nextLockedThreshold:p.length>0?Math.min(...p):null}}var F=Object.defineProperty,K=Object.getOwnPropertyDescriptor,b=(e,t,i,n)=>{for(var s=n>1?void 0:n?K(t,i):t,c=e.length-1,d;c>=0;c--)(d=e[c])&&(s=(n?d(t,i,s):d(s))||s);return n&&s&&F(t,i,s),s};function M(e){const t=e==null?void 0:e.items;return Array.isArray(t)?t.reduce((n,s)=>{const c=s==null?void 0:s.final_line_price;return n+(typeof c=="number"?c:0)},0)/100:null}const B=8e3;let f=class extends A{constructor(){super(...arguments),this.promotionState=null,this.isExpanded=!1,this._isSheetOpen=!1,this.pickerKey=null,this.pickerProducts=null,this.activeProduct=null,this.pickerFailed=!1,this.isLoadingVariant=!1,this.pendingProductId=null,this.variantRequestId=0,this.productCache=new Map,this.cartBasis=null,this.basisAtState=null,this.unsubscribe=null,this.unsubscribeCart=null,this.unsubscribeSlotPick=null,this.unsubscribeGiftChange=null,this.unsubscribeGiftAdded=null,this.addTimeoutId=null,this.replaceKey=null,this.isAddingGift=!1}get isSheetOpen(){return this._isSheetOpen}set isSheetOpen(e){this._isSheetOpen!==e&&(this._isSheetOpen=e,S("promotionSheetOpen",e))}get optimisticDelta(){return this.cartBasis===null||this.basisAtState===null?0:this.cartBasis-this.basisAtState}get model(){return E(this.promotionState,this.optimisticDelta)}get offers(){return D(this.promotionState)}connectedCallback(){super.connectedCallback(),this.unsubscribe=$("freeGiftStateUpdate",t=>{this.promotionState=t,this.basisAtState=this.cartBasis}),this.unsubscribeCart=$("cartUpdate",t=>{this.cartBasis=M(t)}),this.unsubscribeGiftAdded=$("promotionGiftAdded",()=>{this.finishAddingGift()}),this.unsubscribeGiftChange=$("promotionGiftChange",t=>{this.handleGiftChange(t??{})}),this.unsubscribeSlotPick=$("promotionSlotPick",t=>{this.handleSlotPick(t??{})});const e=window.freeGiftState;e&&(this.promotionState=e),this._isSheetOpen&&S("promotionSheetOpen",!0)}disconnectedCallback(){var e,t,i,n,s;super.disconnectedCallback(),(e=this.unsubscribe)==null||e.call(this),this.unsubscribe=null,(t=this.unsubscribeCart)==null||t.call(this),this.unsubscribeCart=null,(i=this.unsubscribeSlotPick)==null||i.call(this),this.unsubscribeSlotPick=null,(n=this.unsubscribeGiftChange)==null||n.call(this),this.unsubscribeGiftChange=null,(s=this.unsubscribeGiftAdded)==null||s.call(this),this.unsubscribeGiftAdded=null,this.addTimeoutId&&(clearTimeout(this.addTimeoutId),this.addTimeoutId=null),this._isSheetOpen&&S("promotionSheetOpen",!1)}createRenderRoot(){return this}handleToggle(){this.isExpanded=!this.isExpanded}handleOpenSheet(){this.isSheetOpen=!0}handleCloseSheet(){this.isSheetOpen=!1,this.closePicker()}handleChoose(e){const t=e.currentTarget,i=t.dataset.tierCode;if(!i)return;const n=t.dataset.collectionId;this.openPicker(i,n?()=>T(n):null)}handleGiftChange(e){const{tierCode:t,collectionId:i}=e;t&&(this.replaceKey=e.replaceKey??null,this.isSheetOpen=!0,this.openPicker(t,i?()=>T(i):null))}handleSlotPick(e){const{setCode:t,slotIndex:i,productIds:n}=e;!t||i===void 0||(this.isSheetOpen=!0,this.openPicker(`set:${t}:${i}`,n&&n.length>0?()=>V(n):null))}iconFor(e){return e.type==="free_shipping"?"free-shipping":"gift"}get headerIcon(){const{nextTier:e,tiers:t}=this.model;return e?this.iconFor(e):t.some(n=>n.tier.type==="free_gift")?"gift":"free-shipping"}tierLabel(e){return e.label?e.label:e.type==="free_shipping"?o("promotion.shipping.freeShippingFallback"):e.name}renderHeadline(){const{nextTier:e}=this.model;return e?C(o("promotion.timeline.headline",[k(e.remaining),this.tierLabel(e)])):C(o("promotion.timeline.headlineAll"))}renderStep(e){const{tier:t,isUnlocked:i,isNext:n}=e,s=i?o("promotion.timeline.unlocked"):o(n?"promotion.timeline.remaining":"promotion.timeline.from",[k(n?t.remaining:t.threshold)]);return l`
2            <hz-timeline-step
3                threshold=${t.threshold}
4                ?unlocked=${i}
5                ?next=${n}
6                icon=${this.iconFor(t)}
7            >
8                ${this.tierLabel(t)}
9                <span slot="note">${s}</span>
10                <span slot="threshold">
11                    ${k(t.threshold)}
12                </span>
13            </hz-timeline-step>
14        `}async openPicker(e,t){if(this.pickerKey=e,this.pickerProducts=null,this.activeProduct=null,this.pickerFailed=!1,!t){this.pickerFailed=!0;return}try{const i=await t();if(!this.isConnected||this.pickerKey!==e)return;const n=i.filter(y);this.pickerProducts=n;const s=n.length===1?n[0]:null;s&&await this.switchProduct(s.id)}catch(i){if(!this.isConnected)return;this.pickerFailed=!0,this.pickerProducts=[],P(new Error("Gift catalogue could not be loaded"),{tags:{cart_promotions:"gift_catalog_failed"},extra:{picker:e,cause:i}})}}closePicker(){this.pickerKey=null,this.pickerProducts=null,this.activeProduct=null,this.pickerFailed=!1,this.abandonVariantRequest()}abandonVariantRequest(){this.variantRequestId+=1,this.isLoadingVariant=!1,this.pendingProductId=null}async switchProduct(e){const t=++this.variantRequestId,i=this.productCache.get(e);if(i){this.isLoadingVariant=!1,this.pendingProductId=null,this.activeProduct=i;return}this.isLoadingVariant=!0,this.pendingProductId=e;try{const n=await R(e);if(!this.isConnected||(n&&this.productCache.set(e,n),t!==this.variantRequestId)||!n)return;this.activeProduct=n}catch(n){if(!this.isConnected)return;t===this.variantRequestId&&(this.pickerFailed=!0),P(new Error("Gift variant could not be loaded"),{tags:{cart_promotions:"gift_variant_failed"},extra:{productId:e,cause:n}})}finally{this.isConnected&&t===this.variantRequestId&&(this.isLoadingVariant=!1,this.pendingProductId=null)}}handleChooseProduct(e){const t=e.currentTarget.dataset.productId;t&&this.switchProduct(t)}handleVariantChange(e){var i;const t=(i=e.detail)==null?void 0:i.value;t&&this.switchProduct(t)}handleBackToList(){this.activeProduct=null,this.abandonVariantRequest()}get isSlotPick(){var e;return((e=this.pickerKey)==null?void 0:e.startsWith("set:"))===!0}handleConfirm(){var t;const e=(t=this.activeProduct)==null?void 0:t.variantId;!e||!this.pickerKey||(this.isAddingGift=!0,this.addTimeoutId&&clearTimeout(this.addTimeoutId),this.addTimeoutId=window.setTimeout(()=>{this.finishAddingGift()},B),this.dispatchEvent(new CustomEvent("giftadd",{bubbles:!0,composed:!0,detail:{productId:e,quantity:1,source:this.pickerKey,replaceKey:this.replaceKey}})))}finishAddingGift(){this.addTimeoutId&&(clearTimeout(this.addTimeoutId),this.addTimeoutId=null),this._isSheetOpen&&S("promotionSheetOpen",!1),this.isAddingGift=!1,this.replaceKey=null,this.closePicker(),this.isSheetOpen=!1}offerSummary(){const{chosenCount:e,choosableCount:t,unlockedCount:i,lockedCount:n,nextLockedThreshold:s}=this.offers,c=[];return i>0&&c.push(o("promotion.offers.summary",[e,i])),t>0&&c.push(o("promotion.offers.choosable",[t])),n>0&&s!==null&&c.push(o("promotion.offers.locked",[n,k(s)])),c.length===0?o("promotion.offers.openNone"):c.join(" · ")}renderOfferRow(){const{offers:e,choosableCount:t}=this.offers;if(e.length===0)return h;if(t===0)return h;const i=this.offerSummary();return l`
15            <div class="border-border border-b px-5 py-3">
16                <button
17                    type="button"
18                    class=${v({"flex w-full cursor-pointer items-center gap-2.5 p-2.5 text-left transition-colors":!0,"bg-brand-100":t>0,"bg-surface-50":t===0})}
19                    @click=${this.handleOpenSheet}
20                >
21                    <hz-badge
22                        tone="promo"
23                        size="small"
24                    >
25                        ${o("promotion.offers.badge")}
26                    </hz-badge>
27
28                    <span
29                        class="flex min-w-0 flex-1 flex-col gap-0.5"
30                    >
31                        <span
32                            class="text-13 tracking-1 font-bold uppercase"
33                        >
34                            ${o("promotion.offers.title")}
35                        </span>
36                        <span class="text-12 text-surface-800">
37                            ${i}
38                        </span>
39                    </span>
40
41                    <hz-icon
42                        type="chevron-right"
43                        size="small"
44                    ></hz-icon>
45                </button>
46            </div>
47        `}renderOfferTile(e){const{tier:t,isUnlocked:i,claimedCount:n,isActionable:s}=e,c=i?o("promotion.timeline.from",[k(t.threshold)]):o("promotion.gift.availableFrom",[k(t.threshold)]),d=n>0?o("promotion.offers.inCart"):o("promotion.gift.free"),u=l`
48            <span class="w-13 flex-none">
49                <hz-media-tile
50                    placeholder="gift"
51                    tone=${i?"promo":"neutral"}
52                ></hz-media-tile>
53            </span>
54
55            <span class="flex min-w-0 flex-1 flex-col gap-1">
56                <span
57                    class=${v({"text-10 tracking-2 font-bold uppercase":!0,"text-campaign-primary":i,"text-surface-800":!i})}
58                >
59                    ${c}
60                </span>
61                <span class="text-14 leading-13 font-bold">
62                    ${this.tierLabel(t)}
63                </span>
64                <span class="text-12 text-surface-800">
65                    ${s?o("promotion.offers.choosable",[e.openCount]):o("promotion.offers.locked",[e.openCount,k(t.threshold)])}
66                </span>
67            </span>
68
69            <span
70                class="flex flex-none flex-col items-end gap-1.5 whitespace-nowrap"
71            >
72                <span
73                    class=${v({"text-14 font-bold":!0,"text-success-100":n>0,"text-campaign-primary":n===0&&i,"text-surface-800":!i})}
74                >
75                    ${d}
76                </span>
77
78                ${s?l`
79                          <span
80                              class="text-11 tracking-1 text-campaign-primary font-bold uppercase underline underline-offset-2"
81                          >
82                              ${o("promotion.select.chooseProduct")}
83                          </span>
84                      `:l`
85                          <span class="text-12 text-surface-800">
86                              ${o("promotion.timeline.remaining",[k(t.remaining)])}
87                          </span>
88                      `}
89            </span>
90        `,p=["flex w-full gap-3 border p-3 text-left",n>0?"border-campaign-primary":"border-border"].join(" ");return s?l`
91            <li>
92                <button
93                    type="button"
94                    class="${p} hover:border-campaign-primary cursor-pointer transition-colors"
95                    data-tier-code=${t.code}
96                    data-collection-id=${t.collectionId??h}
97                    @click=${this.handleChoose}
98                >
99                    ${u}
100                </button>
101            </li>
102        `:l`<li class=${p}>${u}</li>`}renderOptionGroup(e,t){var u,p;if(e.length<2)return h;const i=o(`promotion.attributes.${t}`),s=e.some(r=>r.id===this.pendingProductId)?this.pendingProductId:(u=this.activeProduct)==null?void 0:u.id,c=((p=this.activeProduct)==null?void 0:p.combinedSelectLabels)??null,d=r=>r.active&&L(c,r.combinedSelectLabels,t);return l`
103            <hz-variant-picker
104                value=${s??h}
105                @change=${this.handleVariantChange}
106            >
107                <span slot="label">${i}</span>
108                ${e.map(r=>t==="color"?l`
109                              <hz-swatch
110                                  value=${r.id}
111                                  color=${r.colorHexCode??h}
112                                  label=${w(r.combinedSelectLabels,"color")??""}
113                                  ?disabled=${!d(r)}
114                              ></hz-swatch>
115                          `:l`
116                              <hz-option
117                                  value=${r.id}
118                                  ?disabled=${!d(r)}
119                              >
120                                  ${w(r.combinedSelectLabels,t)??""}
121                              </hz-option>
122                          `)}
123            </hz-variant-picker>
124        `}renderProductImage(e){return l`
125            <hz-media-tile
126                placeholder="gift"
127                tone="promo"
128            >
129                ${e.featuredImage?l`<img
130                          src=${e.featuredImage.url}
131                          alt=${e.featuredImage.altText??""}
132                          width=${e.featuredImage.width}
133                          height=${e.featuredImage.height}
134                          loading="lazy"
135                          decoding="async"
136                      />`:h}
137            </hz-media-tile>
138        `}renderProductStep(e){return l`
139            <div
140                class="promo-fade-in bg-surface-100 flex flex-col gap-2.5 p-3"
141            >
142                <span
143                    class="text-11 tracking-2 text-surface-800 uppercase"
144                >
145                    ${o("promotion.select.chooseProduct")}
146                </span>
147
148                <ul class="flex flex-col gap-2">
149                    ${e.map(t=>l`
150                            <li>
151                                <button
152                                    type="button"
153                                    class="border-border bg-surface-50 flex w-full cursor-pointer items-center gap-2.5 border p-0 pr-2.5 text-left"
154                                    data-product-id=${t.id}
155                                    @click=${this.handleChooseProduct}
156                                >
157                                    <span class="w-12 flex-none">
158                                        ${this.renderProductImage(t)}
159                                    </span>
160                                    <span
161                                        class="text-13 min-w-0 flex-1 leading-13 font-bold"
162                                    >
163                                        ${t.productTitle??t.title}
164                                    </span>
165                                    <hz-icon
166                                        type="chevron-right"
167                                        size="small"
168                                    ></hz-icon>
169                                </button>
170                            </li>
171                        `)}
172                </ul>
173            </div>
174        `}renderVariantStep(e,t){return l`
175            <div
176                class="promo-fade-in flex flex-col gap-3.5"
177                aria-busy=${this.isLoadingVariant?"true":h}
178            >
179                <div
180                    class="flex items-center justify-between gap-2.5"
181                >
182                    <span
183                        class="text-11 tracking-2 text-surface-800 uppercase"
184                    >
185                        ${o("promotion.select.chooseVariant")}
186                    </span>
187                    ${t?l`
188                              <hz-button
189                                  variant="link"
190                                  tone="promo"
191                                  size="small"
192                                  @click=${this.handleBackToList}
193                              >
194                                  ${o("promotion.select.showAll")}
195                              </hz-button>
196                          `:h}
197                </div>
198
199                <div class="flex items-center gap-2.5">
200                    <span class="w-12 flex-none">
201                        ${this.renderProductImage(e)}
202                    </span>
203                    <span
204                        class="flex min-w-0 flex-1 flex-col gap-0.5"
205                    >
206                        ${e.vendor?l`<span
207                                  class="text-10 tracking-2 text-surface-800 uppercase"
208                              >
209                                  ${e.vendor}
210                              </span>`:h}
211                        <span
212                            class="text-13 leading-13 font-bold"
213                        >
214                            ${e.productTitle??e.title}
215                        </span>
216                    </span>
217                </div>
218
219                ${this.renderOptionGroup(e.selectColor,"color")}
220                ${this.renderOptionGroup(e.selectSize,"size")}
221                ${this.renderOptionGroup(e.sizeLength,"length")}
222
223                <hz-button
224                    variant="primary"
225                    tone="auto"
226                    full-width
227                    ?loading=${this.isLoadingVariant||this.isAddingGift}
228                    ?disabled=${!e.variantId||!y(e)||this.isAddingGift}
229                    @click=${this.handleConfirm}
230                >
231                    ${y(e)?o("promotion.select.addToCart"):o("promotion.select.soldOut")}
232                </hz-button>
233            </div>
234        `}renderPicker(){if(this.pickerFailed)return l`
235                <div
236                    class="bg-surface-100 text-13 text-surface-800 p-3.5"
237                    role="alert"
238                >
239                    ${o("cartAddModal.error")}
240                </div>
241            `;const e=this.pickerProducts;return e===null?this.renderProductStepSkeleton():e.length===0?h:this.activeProduct?this.renderVariantStep(this.activeProduct,e.length>1):this.isLoadingVariant?this.renderVariantStepSkeleton(e.length>1):this.renderProductStep(e)}renderProductStepSkeleton(){return l`
242            <div
243                class="bg-surface-100 flex flex-col gap-2.5 p-3"
244                aria-busy="true"
245                aria-live="polite"
246                aria-label=${o("promotion.app.selectGift")}
247            >
248                <span
249                    class="text-11 tracking-2 text-surface-800 uppercase"
250                >
251                    ${o("promotion.select.chooseProduct")}
252                </span>
253
254                <ul class="flex flex-col gap-2">
255                    ${[0,1,2].map(()=>l`
256                            <li
257                                class="border-border bg-surface-50 flex w-full items-center gap-2.5 border pr-2.5"
258                            >
259                                <span class="w-12 flex-none">
260                                    <span
261                                        class="skeleton block aspect-[4/5] w-full"
262                                    ></span>
263                                </span>
264                                <span
265                                    class="skeleton h-3.5 min-w-0 flex-1"
266                                ></span>
267                                <span
268                                    class="skeleton size-3.5 flex-none"
269                                ></span>
270                            </li>
271                        `)}
272                </ul>
273            </div>
274        `}renderVariantStepSkeleton(e){const t=i=>l`
275            <div class="flex flex-col gap-2">
276                <span class="skeleton h-3 w-16"></span>
277                <div class="flex flex-wrap gap-2">
278                    ${[0,1,2,3,4].map(()=>i)}
279                </div>
280            </div>
281        `;return l`
282            <div
283                class="bg-surface-100 flex flex-col gap-3.5 p-3.5"
284                aria-busy="true"
285                aria-live="polite"
286                aria-label=${o("promotion.app.selectGift")}
287            >
288                <div
289                    class="flex items-center justify-between gap-2.5"
290                >
291                    <span
292                        class="text-11 tracking-2 text-surface-800 uppercase"
293                    >
294                        ${o("promotion.select.chooseVariant")}
295                    </span>
296                    ${e?l`<span
297                              class="skeleton h-3 w-20"
298                          ></span>`:h}
299                </div>
300
301                <div class="flex items-center gap-2.5">
302                    <span
303                        class="skeleton aspect-[4/5] w-12 flex-none"
304                    ></span>
305                    <span
306                        class="flex min-w-0 flex-1 flex-col gap-1"
307                    >
308                        <span class="skeleton h-2.5 w-16"></span>
309                        <span class="skeleton h-3.5 w-36"></span>
310                    </span>
311                </div>
312
313                ${t(l`<span
314                        class="flex size-11 flex-none items-center justify-center"
315                    >
316                        <span
317                            class="skeleton size-6 !rounded-full"
318                        ></span>
319                    </span>`)}
320                ${t(l`<span
321                        class="skeleton h-9.5 w-11 flex-none"
322                    ></span>`)}
323
324                <span class="skeleton block h-11 w-full"></span>
325            </div>
326        `}renderSheet(){const{offers:e}=this.offers;return e.length===0?h:l`
327            <!--
328                Abdunkeln und Weichzeichnen dahinter, damit die Flaeche nicht
329                im Warenkorb zu schweben scheint. Gleiche Sprache wie der
330                Drawer und das Wunschlisten-Modal, nur schwaecher: deren
331                10px-Unschaerfe liegt hinter einem GANZEN Drawer, diese liegt
332                mitten im Warenkorb und soll ihn zuruecksetzen, nicht
333                unkenntlich machen.
334
335                Verankert an der Buehne, dem naechsten positionierten
336                Vorfahren, deckt also alles unter der Drawer-Kopfzeile ab. Die
337                Kopfzeile steht ausserhalb dieses Kastens und bleibt dadurch
338                scharf und bedienbar, ohne dass jemand ihre Hoehe kennen muss.
339
340                z-30 und nicht z-10: die Warenkorbzeilen tragen sticky
341                positionierte Titelzeilen auf z-10. Bei gleichem z-index
342                entscheidet die DOM-Reihenfolge, und die Vue-Insel steht im
343                Liquid nach diesem Element — die Titel lagen damit VOR dem
344                Backdrop. Das Sheet darueber auf z-40. Die Insel traegt
345                zusaetzlich isolate, damit ihre z-index-Werte gar nicht erst
346                hier heraufreichen.
347
348                Geschlossen ist die Flaeche durchsichtig UND
349                pointer-events-none — sonst laege ein unsichtbarer Klickfaenger
350                ueber dem gesamten Warenkorb.
351
352                Die Klassen stehen als Ausdruck im Attribut und NICHT in einem
353                classMap: dessen Schluessel landen ab dem zweiten Render in
354                classList.add/remove, und die werfen bei einem Token mit
355                Leerzeichen. Ein Schluessel wie 'pointer-events-none opacity-0',
356                der beim Oeffnen umschlaegt, bricht damit den Render ab — das
357                Sheet ging nicht mehr auf. Mehrwortige classMap-Schluessel sind
358                nur solange harmlos, wie ihr Wert sich nie aendert.
359            -->
360            <div
361                class="${this.isSheetOpen?"opacity-100":"pointer-events-none opacity-0"} absolute inset-0 z-30 bg-black/30 backdrop-blur-[3px] transition-opacity duration-200"
362                aria-hidden="true"
363                @click=${this.handleCloseSheet}
364            ></div>
365
366            <div
367                class="pointer-events-none absolute inset-0 z-40 flex flex-col justify-end"
368            >
369                <hz-bottom-sheet
370                    class="pointer-events-auto"
371                    full-height
372                    tone=${this.isSlotPick?"neutral":"promo"}
373                    ?open=${this.isSheetOpen}
374                    close-label=${o("promotion.app.close")}
375                    @close=${this.handleCloseSheet}
376                >
377                    <!--
378                        Set und Geschenk tragen dieselben Farben wie ihre
379                        Gruppen im Warenkorb: Rust fuer die Aktion, Schwarz
380                        fuer das Set. Der Grundton des Badges IST schon
381                        schwarz auf weiss — 'promo' ueberschreibt ihn nur.
382                    -->
383                    <hz-badge
384                        slot="mark"
385                        tone=${this.isSlotPick?"auto":"promo"}
386                        size="small"
387                    >
388                        ${o(this.isSlotPick?"promotion.offers.bundleBadge":"promotion.offers.badge")}
389                    </hz-badge>
390                    <span slot="heading">
391                        ${o(this.isSlotPick?"promotion.offers.bundleTitle":"promotion.offers.title")}
392                    </span>
393
394                    <!--
395                        Keine Mindesthoehe mehr: die Flaeche nimmt die Buehne
396                        ganz (full-height) und reicht damit bis zur
397                        Drawer-Kopfzeile. Das loest nebenbei das Springen beim
398                        Wechsel von der Angebotsliste in die Auswahl — beide
399                        Zustaende sind jetzt gleich hoch.
400                    -->
401                    <div class="px-5 py-3.5">
402                        ${this.pickerKey?this.renderPicker():l`<ul
403                                  class="flex flex-col gap-2.5"
404                              >
405                                  ${e.map(t=>this.renderOfferTile(t))}
406                              </ul>`}
407                    </div>
408                </hz-bottom-sheet>
409            </div>
410        `}renderDisclosureIcon(){return l`
411            <span
412                class=${v({"flex transition-transform duration-300":!0,"rotate-180":!this.isExpanded})}
413            >
414                <hz-icon
415                    type="chevron-up"
416                    size="small"
417                ></hz-icon>
418            </span>
419        `}renderHeaderContent(e){const{nextTier:t}=this.model;return l`
420            <span
421                class="bg-campaign-primary text-campaign-primary-foreground grid size-8 flex-none place-items-center rounded-full"
422            >
423                <hz-icon
424                    type=${this.headerIcon}
425                    size="small"
426                ></hz-icon>
427            </span>
428
429            <span class="flex min-w-0 flex-1 flex-col gap-0.5">
430                <span
431                    class="text-10 tracking-2 text-surface-800 uppercase"
432                >
433                    ${o(t?"promotion.timeline.eyebrow":"promotion.timeline.allDone")}
434                </span>
435                <span class="text-14 leading-13">
436                    ${this.renderHeadline()}
437                </span>
438            </span>
439
440            ${e}
441        `}renderRestriction(e){const t=e.includeCollections??[],[i,...n]=t;if(!i)return l`<span
442                class="text-12 text-surface-800 leading-13"
443                >${o("promotion.timeline.restrictedFallback")}</span
444            >`;const s=l`<a
445            class="text-campaign-primary underline underline-offset-2"
446            href="/collections/${i.handle}"
447            >${i.title}</a
448        >`;return l`<span
449            class="text-12 text-surface-800 leading-13"
450            >${o("promotion.timeline.restrictedOnly")}
451            ${s}${n.length>0?o("promotion.timeline.restrictedMore",[n.length]):""}</span
452        >`}renderRestrictedStep(e){const{tier:t,isUnlocked:i}=e;return l`
453            <hz-timeline-step
454                detached
455                ?unlocked=${i}
456                threshold=${t.threshold}
457                icon=${this.iconFor(t)}
458            >
459                ${this.tierLabel(t)}
460                <span slot="note">
461                    ${i?o("promotion.timeline.unlocked"):o("promotion.timeline.remaining",[k(t.remaining)])}
462                </span>
463                <span slot="note">
464                    ${this.renderRestriction(t)}
465                </span>
466                <span slot="threshold"
467                    >${k(t.threshold)}</span
468                >
469            </hz-timeline-step>
470        `}renderHeaderRestriction(){const{nextTier:e}=this.model;return!e||e.hasIncludes!==!0?h:l`
471            <p class="px-5 pb-3 pl-16">
472                ${this.renderRestriction(e)}
473            </p>
474        `}render(){const{tiers:e,restrictedTiers:t,maxThreshold:i,currentValue:n}=this.model,s=[...e,...t];if(s.length===0)return h;const c=s.length>1,d=c&&this.isExpanded,u=s.filter(a=>a.isUnlocked).length,p=c?l`
475                  <span
476                      class="text-surface-800 flex h-8 items-center gap-3"
477                  >
478                      <span class="text-15 tracking-1 uppercase">
479                          ${o("promotion.timeline.unlockedCount",[u,s.length])}
480                      </span>
481                      ${this.renderDisclosureIcon()}
482                  </span>
483              `:h,{nextTier:r}=this.model,x=`flex w-full items-start gap-3 px-5 pt-3.5 text-left ${(r==null?void 0:r.hasIncludes)===!0?"pb-1":"pb-3"}`;return l`
484            <div class="border-border bg-surface-100 border-b">
485                ${c?l`
486                          <button
487                              type="button"
488                              class="${x} cursor-pointer"
489                              aria-expanded=${d}
490                              aria-label=${o(d?"promotion.timeline.collapse":"promotion.timeline.expand")}
491                              @click=${this.handleToggle}
492                          >
493                              ${this.renderHeaderContent(p)}
494                          </button>
495                      `:l`
496                          <div class=${x}>
497                              ${this.renderHeaderContent(p)}
498                          </div>
499                      `}
500                ${this.renderHeaderRestriction()}
501                <!--
502                    Auf- und Zuklappen ueber grid-template-rows: 0fr -> 1fr.
503                    height animiert nicht von auto, und Messen waere hier
504                    unnoetig — das Raster kann es von sich aus.
505
506                    Zugeklappt bleibt der Inhalt im DOM, damit es ueberhaupt
507                    etwas zu bewegen gibt. Er ist dann inert: kein Tabstopp,
508                    nichts fuer den Screenreader. Ohne das laege eine
509                    unsichtbare Liste im Fokuspfad.
510
511                    Die Timeline ist weiterhin dauerhaft aufgeklappt — die
512                    kollabierte Darstellung der Komponente (Punkte auf einem
513                    Fortschrittsbalken) wird hier nicht mehr benutzt, der
514                    Entwurf kennt keinen mehr.
515                -->
516                <div
517                    class="${d?"grid-rows-[1fr]":"grid-rows-[0fr]"} grid transition-[grid-template-rows] duration-300 ease-in-out motion-reduce:transition-none"
518                >
519                    <div
520                        class="overflow-hidden"
521                        ?inert=${!d}
522                    >
523                        <div class="px-5 pb-3.5">
524                            <hz-timeline
525                                expanded
526                                value=${n}
527                                max=${i}
528                                accessibility-label=${o("promotion.timeline.progressLabel")}
529                            >
530                                ${e.map(a=>this.renderStep(a))}
531                                ${t.map(a=>this.renderRestrictedStep(a))}
532                            </hz-timeline>
533                        </div>
534                    </div>
535                </div>
536            </div>
537
538            ${this.renderOfferRow()} ${this.renderSheet()}
539        `}};b([g()],f.prototype,"promotionState",2);b([g()],f.prototype,"isExpanded",2);b([g()],f.prototype,"_isSheetOpen",2);b([g()],f.prototype,"pickerKey",2);b([g()],f.prototype,"pickerProducts",2);b([g()],f.prototype,"activeProduct",2);b([g()],f.prototype,"pickerFailed",2);b([g()],f.prototype,"isLoadingVariant",2);b([g()],f.prototype,"pendingProductId",2);b([g()],f.prototype,"cartBasis",2);b([g()],f.prototype,"isAddingGift",2);f=b([G("cart-promotions")],f);export{f as CartPromotions};

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.