@dropins/storefront-pdp 3.0.3 → 3.0.4-beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,6 +6,9 @@ import { ProductModel } from '../../data/models/product-model';
6
6
  type DefaultSlotContext = {
7
7
  data: ProductModel | null;
8
8
  };
9
+ type VideosProp = boolean | {
10
+ position: 'first' | 'last';
11
+ };
9
12
  export interface ProductGalleryProps extends Omit<HTMLAttributes<HTMLDivElement>, 'controls'> {
10
13
  scope?: string;
11
14
  controls?: 'thumbnailsRow' | 'thumbnailsColumn' | 'dots' | null;
@@ -19,9 +22,7 @@ export interface ProductGalleryProps extends Omit<HTMLAttributes<HTMLDivElement>
19
22
  zoom?: {
20
23
  closeButton: boolean;
21
24
  } | boolean;
22
- videos?: boolean | {
23
- position: 'first' | 'last';
24
- };
25
+ videos?: VideosProp;
25
26
  slots?: {
26
27
  CarouselThumbnail?: SlotProps<DefaultSlotContext & {
27
28
  defaultImageProps: ImageProps;
@@ -1,4 +1,4 @@
1
1
  /*! Copyright 2026 Adobe
2
2
  All Rights Reserved. */
3
- import{jsx as d,jsxs as x}from"@dropins/tools/preact-jsx-runtime.js";import{useState as D,useEffect as G,useMemo as C}from"@dropins/tools/preact-compat.js";import{Slot as M}from"@dropins/tools/lib.js";import{Image as N}from"@dropins/tools/components.js";import{events as W}from"@dropins/tools/event-bus.js";import{g as H}from"../chunks/getFetchedProductData.js";import{Z as Q,C as s,O as Y}from"../chunks/Zoom.js";/* empty css */import"@dropins/tools/preact-hooks.js";/* empty css *//* empty css */import{useText as q}from"@dropins/tools/i18n.js";import"@dropins/tools/preact.js";const k=[".mp4",".webm",".ogg",".mov",".avi",".mkv"],L={".mp4":"video/mp4",".webm":"video/webm",".ogg":"video/ogg",".mov":"video/quicktime",".avi":"video/x-msvideo",".mkv":"video/x-matroska"};function X(l){if(!l)return"video/mp4";const p=l.toLowerCase(),i=k.find(v=>p.endsWith(v));return i?L[i]:"video/mp4"}function J(l){if(!l)return!1;const p=l.toLowerCase();return k.some(i=>p.endsWith(i))}function O(l,p){if(p)return p;if(l!=null&&l.includes("adobeaemcloud.com")&&l.endsWith("/play"))return l.replace(/\/play$/,"/as/thumbnail.png")}function K(l){if(!l)return"";const p=l.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]+)/);if(p)return`https://www.youtube.com/embed/${p[1]}?rel=0`;const i=l.match(/vimeo\.com\/(?:video\/)?(\d+)/);return i?`https://player.vimeo.com/video/${i[1]}`:l}const U=({initialData:l=null,scope:p,controls:i="dots",loop:v=!0,peak:V=!1,gap:Z=null,arrows:j=!0,arrowsOnMainImage:R=!1,zoom:g=!1,videos:_=!1,imageParams:o,thumbnailParams:t,children:b,slots:n,...$})=>{const h=q("PDP.Product.Image.label").label,[r,z]=D(l),[T,E]=D(null);G(()=>{const e=W.on("pdp/data",z,{scope:p});return()=>{e==null||e.off()}},[p]);const I=C(()=>({data:r}),[r]),c=C(()=>{var f,y;const e=((f=r==null?void 0:r.images)==null?void 0:f.map(u=>({type:"image",url:u.url,label:u.label})))??[];if(!_)return e;const A=((y=r==null?void 0:r.videos)==null?void 0:y.map(u=>({type:"video",url:u.url,label:u.title??"",preview:u.preview})))??[];return(typeof _=="object"?_.position:"last")==="first"?[...A,...e]:[...e,...A]},[r==null?void 0:r.images,r==null?void 0:r.videos,_]),S=C(()=>c.length>0?c.map((e,A)=>{const w=({...f})=>{var B;const y={title:e.label,src:e.url,alt:h==null?void 0:h.replace("{product}",e.label).replace("{key}",String(A+1)).replace("{total}",String(c.length)),width:(o==null?void 0:o.width)??960,height:(o==null?void 0:o.height)??1191,params:o??{width:960},loading:A===0?"eager":"lazy",...f},u=e.type==="video"?O(e.url,(B=e.preview)==null?void 0:B.url):void 0;return d(M,{name:"CarouselMainImage",slot:n==null?void 0:n.CarouselMainImage,context:{...I,defaultImageProps:y,mediaType:e.type,previewUrl:u},children:e.type==="video"?J(e.url)?x("video",{className:"pdp-product-gallery__video",controls:!0,preload:"metadata",poster:u,width:(o==null?void 0:o.width)??960,height:(o==null?void 0:o.height)??1191,children:[d("source",{src:e.url,type:X(e.url)}),"Your browser does not support the video tag."]}):d("iframe",{className:"pdp-product-gallery__video-iframe",src:K(e.url),title:e.label||"Product video",width:(o==null?void 0:o.width)??960,height:(o==null?void 0:o.height)??1191,loading:"lazy",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0}):d(N,{...y})})};return g&&e.type==="image"?d(Q,{closeButton:typeof g=="object"?g.closeButton:!1,children:d(w,{})},e.url):d(w,{onClick:()=>E(A)},e.url)}):d("img",{src:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",className:"pdp-product-gallery__images__placeholder",width:(o==null?void 0:o.width)??960,height:(o==null?void 0:o.height)??1191,"aria-hidden":!0},"pleaceholder"),[h,c,o,g,I,n==null?void 0:n.CarouselMainImage]),F=C(()=>c.length>0?c.map((e,A)=>{var y;const w={title:e.label,src:e.url,alt:h==null?void 0:h.replace("{product}",e.label).replace("{key}",String(A+1)).replace("{total}",String(c.length)),width:(t==null?void 0:t.width)??200,height:(t==null?void 0:t.height)??248,params:t??{width:200},loading:"lazy"},f=e.type==="video"?O(e.url,(y=e.preview)==null?void 0:y.url):void 0;return d(M,{name:"CarouselThumbnail",slotTag:"span",contentTag:"span",slot:n==null?void 0:n.CarouselThumbnail,context:{...I,defaultImageProps:w,mediaType:e.type,previewUrl:f},children:e.type==="video"?x("span",{className:"pdp-product-gallery__video-thumbnail",role:"img","aria-label":`Play video: ${e.label||"Product video"}`,children:[f?d("img",{src:f,alt:"",width:(t==null?void 0:t.width)??200,height:(t==null?void 0:t.height)??248,loading:"lazy","aria-hidden":"true"}):d("span",{className:"pdp-product-gallery__video-thumbnail-placeholder",style:{width:(t==null?void 0:t.width)??200,height:(t==null?void 0:t.height)??248},"aria-hidden":"true"}),d("span",{className:"pdp-product-gallery__video-icon","aria-hidden":"true",children:"▶"})]}):d(N,{...w})},e.url)}):d("img",{src:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",className:"pdp-product-gallery__thumbnail__placeholder",width:(t==null?void 0:t.width)??200,height:(t==null?void 0:t.height)??248,"aria-hidden":!0},"pleaceholder"),[h,c,t,I,n==null?void 0:n.CarouselThumbnail]);return r?x("div",{...$,children:[d(s,{arrows:!!c.length&&j,arrowsOnMainImage:R,controls:c.length?i:void 0,loop:v&&!!c.length,peak:!!c.length&&V,gap:Z,defaultIndex:T||0,thumbnails:F,children:S}),T===null?null:d(Y,{onClose:()=>E(null),centered:!0,children:d(s,{arrows:!!c.length,width:"100%",height:"100%",defaultIndex:T,loop:v&&!!c.length,controls:"dots",isZoomed:!0,children:S})})]}):null};U.getInitialData=H;export{U as ProductGallery,U as default};
3
+ import{jsxs as O,jsx as i}from"@dropins/tools/preact-jsx-runtime.js";import{useState as C,useEffect as B,useRef as Z,useMemo as S}from"@dropins/tools/preact-compat.js";import{Slot as $}from"@dropins/tools/lib.js";import{Image as G}from"@dropins/tools/components.js";import{events as J}from"@dropins/tools/event-bus.js";import{g as L}from"../chunks/getFetchedProductData.js";import{C as k,O as a,Z as b}from"../chunks/Zoom.js";/* empty css */import"@dropins/tools/preact-hooks.js";/* empty css *//* empty css */import{useText as m}from"@dropins/tools/i18n.js";import"@dropins/tools/preact.js";const W=[".mp4",".webm",".ogg",".mov",".avi",".mkv"],P={".mp4":"video/mp4",".webm":"video/webm",".ogg":"video/ogg",".mov":"video/quicktime",".avi":"video/x-msvideo",".mkv":"video/x-matroska"};function ee(o){if(!o)return"video/mp4";const n=o.toLowerCase(),t=W.find(e=>n.endsWith(e));return t?P[t]:"video/mp4"}function te(o){if(!o)return!1;const n=o.toLowerCase();return W.some(t=>n.endsWith(t))}function R(o,n){if(n)return n;if(o!=null&&o.includes("adobeaemcloud.com")&&o.endsWith("/play"))return o.replace(/\/play$/,"/as/thumbnail.png")}function oe(o){if(!o)return"";const n=o.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([a-zA-Z0-9_-]+)/);if(n)return`https://www.youtube.com/embed/${n[1]}?rel=0`;const t=o.match(/vimeo\.com\/(?:video\/)?(\d+)/);return t?`https://player.vimeo.com/video/${t[1]}`:o}const re=3e3;function ne(o,{enabled:n=!0,timeoutMs:t=re}={}){const e=o.join(" "),u=!n||o.length===0,[l,c]=C(null),[d,A]=C(null);return B(()=>{if(u){c(e),A(null);return}let v=!1,r=o.length,h=!1;const y=()=>{v||(v=!0,h&&A(e),c(e))},g=window.setTimeout(()=>{h=!0,y()},t),s=w=>{v||(w&&(h=!0),r-=1,r<=0&&(window.clearTimeout(g),y()))};return o.forEach(w=>{const f=new Image;f.src=w,typeof f.decode=="function"?f.decode().then(()=>s(!1)).catch(()=>s(!0)):(f.onload=()=>s(!1),f.onerror=()=>s(!0))}),()=>{v=!0,window.clearTimeout(g)}},[e,n,t,u]),{ready:u||l===e,error:d===e}}const le=200,de=(o,n)=>{var l,c;const t=((l=o==null?void 0:o.images)==null?void 0:l.map(d=>({type:"image",url:d.url,label:d.label})))??[];if(!n)return t;const e=((c=o==null?void 0:o.videos)==null?void 0:c.map(d=>({type:"video",url:d.url,label:d.title??"",preview:d.preview})))??[];return(typeof n=="object"?n.position:"last")==="first"?[...e,...t]:[...t,...e]},ce=(o,n)=>{if(!o)return[];const t=(o.images??[]).map(e=>e.url);return n&&(o.videos??[]).forEach(e=>{var l;const u=R(e.url,(l=e.preview)==null?void 0:l.url);u&&t.push(u)}),t},ie=(o,n,t)=>{if(o===n)return!0;if(!o||!n)return!1;const e=o.images??[],u=n.images??[];if(e.length!==u.length)return!1;for(let d=0;d<e.length;d+=1)if(e[d].url!==u[d].url)return!1;if(!t)return!0;const l=o.videos??[],c=n.videos??[];if(l.length!==c.length)return!1;for(let d=0;d<l.length;d+=1)if(l[d].url!==c[d].url)return!1;return!0},ue=()=>typeof window<"u"&&typeof window.matchMedia=="function"&&window.matchMedia("(prefers-reduced-motion: reduce)").matches,K=({data:o,videos:n,imageParams:t,thumbnailParams:e,zoom:u,slots:l,ImagesLabel:c,onZoom:d})=>{const A=S(()=>({data:o}),[o]),p=S(()=>de(o,n),[o,n]),M=S(()=>p.length>0?p.map((r,h)=>{const y=({...g})=>{var f;const s={title:r.label,src:r.url,alt:c==null?void 0:c.replace("{product}",r.label).replace("{key}",String(h+1)).replace("{total}",String(p.length)),width:(t==null?void 0:t.width)??960,height:(t==null?void 0:t.height)??1191,params:t??{width:960},loading:h===0?"eager":"lazy",...g},w=r.type==="video"?R(r.url,(f=r.preview)==null?void 0:f.url):void 0;return i($,{name:"CarouselMainImage",slot:l==null?void 0:l.CarouselMainImage,context:{...A,defaultImageProps:s,mediaType:r.type,previewUrl:w},children:r.type==="video"?te(r.url)?O("video",{className:"pdp-product-gallery__video",controls:!0,preload:"metadata",poster:w,width:(t==null?void 0:t.width)??960,height:(t==null?void 0:t.height)??1191,children:[i("source",{src:r.url,type:ee(r.url)}),"Your browser does not support the video tag."]}):i("iframe",{className:"pdp-product-gallery__video-iframe",src:oe(r.url),title:r.label||"Product video",width:(t==null?void 0:t.width)??960,height:(t==null?void 0:t.height)??1191,loading:"lazy",allow:"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture",allowFullScreen:!0}):i(G,{...s})})};return u&&r.type==="image"?i(b,{closeButton:typeof u=="object"?u.closeButton:!1,children:i(y,{})},r.url):i(y,{onClick:()=>d==null?void 0:d(h)},r.url)}):i("img",{src:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",className:"pdp-product-gallery__images__placeholder",width:(t==null?void 0:t.width)??960,height:(t==null?void 0:t.height)??1191,"aria-hidden":!0},"pleaceholder"),[c,p,t,u,A,l==null?void 0:l.CarouselMainImage,d]),v=S(()=>p.length>0?p.map((r,h)=>{var s;const y={title:r.label,src:r.url,alt:c==null?void 0:c.replace("{product}",r.label).replace("{key}",String(h+1)).replace("{total}",String(p.length)),width:(e==null?void 0:e.width)??200,height:(e==null?void 0:e.height)??248,params:e??{width:200},loading:"lazy"},g=r.type==="video"?R(r.url,(s=r.preview)==null?void 0:s.url):void 0;return i($,{name:"CarouselThumbnail",slotTag:"span",contentTag:"span",slot:l==null?void 0:l.CarouselThumbnail,context:{...A,defaultImageProps:y,mediaType:r.type,previewUrl:g},children:r.type==="video"?O("span",{className:"pdp-product-gallery__video-thumbnail",role:"img","aria-label":`Play video: ${r.label||"Product video"}`,children:[g?i("img",{src:g,alt:"",width:(e==null?void 0:e.width)??200,height:(e==null?void 0:e.height)??248,loading:"lazy","aria-hidden":"true"}):i("span",{className:"pdp-product-gallery__video-thumbnail-placeholder",style:{width:(e==null?void 0:e.width)??200,height:(e==null?void 0:e.height)??248},"aria-hidden":"true"}),i("span",{className:"pdp-product-gallery__video-icon","aria-hidden":"true",children:"▶"})]}):i(G,{...y})},r.url)}):i("img",{src:"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",className:"pdp-product-gallery__thumbnail__placeholder",width:(e==null?void 0:e.width)??200,height:(e==null?void 0:e.height)??248,"aria-hidden":!0},"pleaceholder"),[c,p,e,A,l==null?void 0:l.CarouselThumbnail]);return{mediaItems:p,galleryItems:M,thumbnails:v}},pe=({initialData:o=null,scope:n,controls:t="dots",loop:e=!0,peak:u=!1,gap:l=null,arrows:c=!0,arrowsOnMainImage:d=!1,zoom:A=!1,videos:p=!1,imageParams:M,thumbnailParams:v,children:r,slots:h,...y})=>{const g=m("PDP.Product.Image.label").label,[s,w]=C(o),[f,x]=C(null),[q,N]=C(!1),[D,j]=C(null),F=Z(o);B(()=>{F.current=s},[s]),B(()=>{const T=J.on("pdp/data",E=>{if(!E){w(null),x(null),N(!1);return}if(ie(F.current,E,p)){w(E),x(null),N(!1);return}x(E),N(!1)},{scope:n});return()=>{T==null||T.off()}},[n,p]);const z=S(()=>ce(f,p),[f,p]),{ready:V}=ne(z,{enabled:!!f});B(()=>{if(!f||!V)return;const T=()=>{w(f),x(null),N(!1)};if(ue()){T();return}const E=window.requestAnimationFrame(()=>N(!0)),X=window.setTimeout(T,le+50);return()=>{window.cancelAnimationFrame(E),window.clearTimeout(X)}},[V,f]);const{mediaItems:_,galleryItems:U,thumbnails:H}=K({data:s,videos:p,imageParams:M,thumbnailParams:v,zoom:A,slots:h,ImagesLabel:g,onZoom:j}),I=K({data:f,videos:p,imageParams:M,thumbnailParams:v,zoom:A,slots:h,ImagesLabel:g});if(!s)return null;const Q=["pdp-product-gallery__stack",y.className].filter(Boolean).join(" "),Y=["pdp-product-gallery__layer","pdp-product-gallery__layer--pending",q?"pdp-product-gallery__layer--entering":""].filter(Boolean).join(" ");return O("div",{...y,className:Q,children:[i("div",{className:"pdp-product-gallery__layer",children:i(k,{arrows:!!_.length&&c,arrowsOnMainImage:d,controls:_.length?t:void 0,loop:e&&!!_.length,peak:!!_.length&&u,gap:l,defaultIndex:D||0,thumbnails:H,children:U})}),f?i("div",{className:Y,"aria-hidden":"true",children:i(k,{arrows:!!I.mediaItems.length&&c,arrowsOnMainImage:d,controls:I.mediaItems.length?t:void 0,loop:e&&!!I.mediaItems.length,peak:!!I.mediaItems.length&&u,gap:l,defaultIndex:0,thumbnails:I.thumbnails,children:I.galleryItems})}):null,D===null?null:i(a,{onClose:()=>j(null),centered:!0,children:i(k,{arrows:!!_.length,width:"100%",height:"100%",defaultIndex:D,loop:e&&!!_.length,controls:"dots",isZoomed:!0,children:U})})]})};pe.getInitialData=L;export{pe as ProductGallery,pe as default};
4
4
  //# sourceMappingURL=ProductGallery.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductGallery.js","sources":["/@dropins/storefront-pdp/src/lib/videoUtils.ts","/@dropins/storefront-pdp/src/containers/ProductGallery/ProductGallery.tsx"],"sourcesContent":["/**\n * Video utility functions for ProductGallery\n */\n\nconst VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv'];\n\nconst VIDEO_MIME_TYPES: Record<string, string> = {\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.ogg': 'video/ogg',\n '.mov': 'video/quicktime',\n '.avi': 'video/x-msvideo',\n '.mkv': 'video/x-matroska',\n};\n\n/**\n * Gets the MIME type for a video URL based on file extension\n * @param url - The video URL\n * @returns The MIME type string, defaults to 'video/mp4' if unknown\n */\nexport function getVideoMimeType(url: string): string {\n if (!url) return 'video/mp4';\n const lowerUrl = url.toLowerCase();\n const ext = VIDEO_EXTENSIONS.find((e) => lowerUrl.endsWith(e));\n return ext ? VIDEO_MIME_TYPES[ext] : 'video/mp4';\n}\n\n/**\n * Checks if URL is a direct video file based on extension\n * @param url - The video URL\n * @returns true if direct video file\n */\nexport function isDirectVideoFile(url: string): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return VIDEO_EXTENSIONS.some((ext) => lowerUrl.endsWith(ext));\n}\n\n/**\n * Gets the preview image URL for a video.\n * Uses provided preview URL if available, otherwise tries to derive from video URL.\n * @param videoUrl - The video URL\n * @param previewUrl - The preview URL from data (if available)\n * @returns The preview image URL or undefined\n */\nexport function getVideoPreviewUrl(\n videoUrl: string,\n previewUrl?: string\n): string | undefined {\n // Use provided preview URL if available\n if (previewUrl) return previewUrl;\n\n // Fallback: Derive thumbnail for AEM Assets URLs\n // Pattern: .../urn:aaid:aem:.../play -> .../urn:aaid:aem:.../as/thumbnail.png\n if (videoUrl?.includes('adobeaemcloud.com') && videoUrl.endsWith('/play')) {\n return videoUrl.replace(/\\/play$/, '/as/thumbnail.png');\n }\n\n return undefined;\n}\n\n/**\n * Converts YouTube/Vimeo URLs to embeddable URLs.\n * Returns original URL for other video sources.\n * @param url - The original video URL\n * @returns The embed URL\n */\nexport function getEmbedUrl(url: string): string {\n if (!url) return '';\n\n // YouTube: various formats\n // https://www.youtube.com/watch?v=VIDEO_ID\n // https://youtu.be/VIDEO_ID\n // https://www.youtube.com/embed/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]+)/\n );\n if (youtubeMatch) {\n return `https://www.youtube.com/embed/${youtubeMatch[1]}?rel=0`;\n }\n\n // Vimeo: various formats\n // https://vimeo.com/VIDEO_ID\n // https://player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/vimeo\\.com\\/(?:video\\/)?(\\d+)/);\n if (vimeoMatch) {\n return `https://player.vimeo.com/video/${vimeoMatch[1]}`;\n }\n\n // Return original URL for other sources (AEM, etc.)\n return url;\n}\n","import { HTMLAttributes, useEffect, useMemo, useState } from 'preact/compat';\nimport {\n Container,\n ResolveImageUrlOptions,\n Slot,\n SlotProps,\n} from '@adobe-commerce/elsie/lib';\n\nimport { Image, ImageProps } from '@adobe-commerce/elsie/components';\nimport { events } from '@adobe-commerce/event-bus';\nimport { ProductModel } from '@/pdp/data/models/product-model';\nimport { getFetchedProductData } from '@/pdp/api';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { Carousel, Overlay, Zoom } from '@/pdp/components';\nimport {\n isDirectVideoFile,\n getEmbedUrl,\n getVideoPreviewUrl,\n getVideoMimeType,\n} from '@/pdp/lib/videoUtils';\n\ntype DefaultSlotContext = {\n data: ProductModel | null;\n};\n\nexport interface ProductGalleryProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'controls'> {\n scope?: string;\n controls?: 'thumbnailsRow' | 'thumbnailsColumn' | 'dots' | null;\n loop?: boolean;\n peak?: boolean;\n gap?: 'small' | 'medium' | 'large' | null;\n arrows?: boolean;\n arrowsOnMainImage?: boolean;\n imageParams?: ResolveImageUrlOptions;\n thumbnailParams?: ResolveImageUrlOptions;\n zoom?: { closeButton: boolean } | boolean;\n videos?: boolean | { position: 'first' | 'last' };\n slots?: {\n CarouselThumbnail?: SlotProps<\n DefaultSlotContext & {\n defaultImageProps: ImageProps;\n mediaType?: 'image' | 'video';\n previewUrl?: string;\n }\n >;\n CarouselMainImage?: SlotProps<\n DefaultSlotContext & {\n defaultImageProps: ImageProps;\n mediaType?: 'image' | 'video';\n previewUrl?: string;\n }\n >;\n };\n}\n\nexport const ProductGallery: Container<\n ProductGalleryProps,\n ProductModel | null\n> = ({\n initialData = null,\n scope,\n controls = 'dots',\n loop = true,\n peak = false,\n gap = null,\n arrows = true,\n arrowsOnMainImage = false,\n zoom = false,\n videos = false,\n imageParams,\n thumbnailParams,\n children,\n slots,\n ...props\n}) => {\n const ImagesLabel = useText('PDP.Product.Image.label').label;\n\n const [data, setData] = useState<ProductModel | null>(initialData);\n\n const [zoomTo, setZoomTo] = useState<number | null>(null);\n\n useEffect(() => {\n const dataEvent = events.on('pdp/data', setData, { scope });\n return () => {\n dataEvent?.off();\n };\n }, [scope]);\n\n const defaultSlotContext: DefaultSlotContext = useMemo(\n () => ({ data }),\n [data]\n );\n\n // Combine images and videos into a single media array based on videos prop\n const mediaItems = useMemo(() => {\n const imageItems =\n data?.images?.map((img) => ({\n type: 'image' as const,\n url: img.url,\n label: img.label,\n })) ?? [];\n\n if (!videos) {\n return imageItems;\n }\n\n const videoItems =\n data?.videos?.map((video) => ({\n type: 'video' as const,\n url: video.url,\n label: video.title ?? '',\n preview: video.preview,\n })) ?? [];\n\n const position = typeof videos === 'object' ? videos.position : 'last';\n\n return position === 'first'\n ? [...videoItems, ...imageItems]\n : [...imageItems, ...videoItems];\n }, [data?.images, data?.videos, videos]);\n\n const galleryItems = useMemo(\n () =>\n mediaItems.length > 0 ? (\n mediaItems.map((item, key) => {\n const Item = ({ ...itemProps }) => {\n const defaultProps = {\n title: item.label,\n src: item.url,\n alt: ImagesLabel?.replace('{product}', item.label)\n .replace('{key}', String(key + 1))\n .replace('{total}', String(mediaItems.length)),\n\n // TODO: change width/height for image aspect ratio\n // pending https://jira.corp.adobe.com/browse/DATA-5122\n width: imageParams?.width ?? 960,\n height: imageParams?.height ?? 1191,\n params: imageParams ?? { width: 960 },\n loading: key === 0 ? 'eager' : 'lazy',\n ...itemProps,\n } as const;\n\n const previewUrl =\n item.type === 'video'\n ? getVideoPreviewUrl(item.url, item.preview?.url)\n : undefined;\n\n return (\n <Slot\n name=\"CarouselMainImage\"\n slot={slots?.CarouselMainImage}\n context={{\n ...defaultSlotContext,\n defaultImageProps: defaultProps,\n mediaType: item.type,\n previewUrl,\n }}\n >\n {item.type === 'video' ? (\n isDirectVideoFile(item.url) ? (\n <video\n className=\"pdp-product-gallery__video\"\n controls\n preload=\"metadata\"\n poster={previewUrl}\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n >\n <source\n src={item.url}\n type={getVideoMimeType(item.url)}\n />\n Your browser does not support the video tag.\n </video>\n ) : (\n <iframe\n className=\"pdp-product-gallery__video-iframe\"\n src={getEmbedUrl(item.url)}\n title={item.label || 'Product video'}\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )\n ) : (\n <Image {...defaultProps} />\n )}\n </Slot>\n );\n };\n\n return zoom && item.type === 'image' ? (\n <Zoom\n key={item.url}\n closeButton={typeof zoom === 'object' ? zoom.closeButton : false}\n >\n <Item />\n </Zoom>\n ) : (\n <Item key={item.url} onClick={() => setZoomTo(key)} />\n );\n })\n ) : (\n <img\n key=\"pleaceholder\"\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\n className=\"pdp-product-gallery__images__placeholder\"\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n aria-hidden\n />\n ),\n [\n ImagesLabel,\n mediaItems,\n imageParams,\n zoom,\n defaultSlotContext,\n slots?.CarouselMainImage,\n ]\n );\n\n const thumbnails = useMemo(() => {\n return mediaItems.length > 0 ? (\n mediaItems.map((item, key) => {\n const defaultProps = {\n title: item.label,\n src: item.url,\n alt: ImagesLabel?.replace('{product}', item.label)\n .replace('{key}', String(key + 1))\n .replace('{total}', String(mediaItems.length)),\n\n // TODO: change width/height for image aspect ratio\n // pending https://jira.corp.adobe.com/browse/DATA-5122\n width: thumbnailParams?.width ?? 200,\n height: thumbnailParams?.height ?? 248,\n params: thumbnailParams ?? { width: 200 },\n loading: 'lazy',\n } as const;\n\n const previewUrl =\n item.type === 'video'\n ? getVideoPreviewUrl(item.url, item.preview?.url)\n : undefined;\n\n return (\n <Slot\n key={item.url}\n name=\"CarouselThumbnail\"\n slotTag=\"span\"\n contentTag=\"span\"\n slot={slots?.CarouselThumbnail}\n context={{\n ...defaultSlotContext,\n defaultImageProps: defaultProps,\n mediaType: item.type,\n previewUrl,\n }}\n >\n {item.type === 'video' ? (\n <span\n className=\"pdp-product-gallery__video-thumbnail\"\n role=\"img\"\n aria-label={`Play video: ${item.label || 'Product video'}`}\n >\n {previewUrl ? (\n <img\n src={previewUrl}\n alt=\"\"\n width={thumbnailParams?.width ?? 200}\n height={thumbnailParams?.height ?? 248}\n loading=\"lazy\"\n aria-hidden=\"true\"\n />\n ) : (\n <span\n className=\"pdp-product-gallery__video-thumbnail-placeholder\"\n style={{\n width: thumbnailParams?.width ?? 200,\n height: thumbnailParams?.height ?? 248,\n }}\n aria-hidden=\"true\"\n />\n )}\n <span\n className=\"pdp-product-gallery__video-icon\"\n aria-hidden=\"true\"\n >\n ▶\n </span>\n </span>\n ) : (\n <Image {...defaultProps} />\n )}\n </Slot>\n );\n })\n ) : (\n <img\n key=\"pleaceholder\"\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\n className=\"pdp-product-gallery__thumbnail__placeholder\"\n width={thumbnailParams?.width ?? 200}\n height={thumbnailParams?.height ?? 248}\n aria-hidden\n />\n );\n }, [\n ImagesLabel,\n mediaItems,\n thumbnailParams,\n defaultSlotContext,\n slots?.CarouselThumbnail,\n ]);\n\n if (!data) return null;\n\n return (\n <div {...props}>\n {/* Carousel */}\n <Carousel\n arrows={!!mediaItems.length && arrows}\n arrowsOnMainImage={arrowsOnMainImage}\n controls={mediaItems.length ? controls : undefined}\n loop={loop && !!mediaItems.length}\n peak={!!mediaItems.length && peak}\n gap={gap}\n defaultIndex={zoomTo || 0}\n thumbnails={thumbnails}\n >\n {galleryItems}\n </Carousel>\n\n {/* Zoom Overlay */}\n {zoomTo === null ? null : (\n <Overlay onClose={() => setZoomTo(null)} centered>\n <Carousel\n arrows={!!mediaItems.length}\n width=\"100%\"\n height=\"100%\"\n defaultIndex={zoomTo}\n loop={loop && !!mediaItems.length}\n controls=\"dots\"\n isZoomed\n >\n {galleryItems}\n </Carousel>\n </Overlay>\n )}\n </div>\n );\n};\n\nProductGallery.getInitialData = getFetchedProductData;\n"],"names":["VIDEO_EXTENSIONS","VIDEO_MIME_TYPES","getVideoMimeType","url","lowerUrl","ext","e","isDirectVideoFile","getVideoPreviewUrl","videoUrl","previewUrl","getEmbedUrl","youtubeMatch","vimeoMatch","ProductGallery","initialData","scope","controls","loop","peak","gap","arrows","arrowsOnMainImage","zoom","videos","imageParams","thumbnailParams","children","slots","props","ImagesLabel","useText","data","setData","useState","zoomTo","setZoomTo","useEffect","dataEvent","events","defaultSlotContext","useMemo","mediaItems","imageItems","_a","img","videoItems","_b","video","galleryItems","item","key","Item","itemProps","defaultProps","jsx","Slot","jsxs","Image","Zoom","thumbnails","Carousel","Overlay","getFetchedProductData"],"mappings":"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"}
1
+ {"version":3,"file":"ProductGallery.js","sources":["/@dropins/storefront-pdp/src/lib/videoUtils.ts","/@dropins/storefront-pdp/src/lib/usePreloadImages.ts","/@dropins/storefront-pdp/src/containers/ProductGallery/ProductGallery.tsx"],"sourcesContent":["/**\n * Video utility functions for ProductGallery\n */\n\nconst VIDEO_EXTENSIONS = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv'];\n\nconst VIDEO_MIME_TYPES: Record<string, string> = {\n '.mp4': 'video/mp4',\n '.webm': 'video/webm',\n '.ogg': 'video/ogg',\n '.mov': 'video/quicktime',\n '.avi': 'video/x-msvideo',\n '.mkv': 'video/x-matroska',\n};\n\n/**\n * Gets the MIME type for a video URL based on file extension\n * @param url - The video URL\n * @returns The MIME type string, defaults to 'video/mp4' if unknown\n */\nexport function getVideoMimeType(url: string): string {\n if (!url) return 'video/mp4';\n const lowerUrl = url.toLowerCase();\n const ext = VIDEO_EXTENSIONS.find((e) => lowerUrl.endsWith(e));\n return ext ? VIDEO_MIME_TYPES[ext] : 'video/mp4';\n}\n\n/**\n * Checks if URL is a direct video file based on extension\n * @param url - The video URL\n * @returns true if direct video file\n */\nexport function isDirectVideoFile(url: string): boolean {\n if (!url) return false;\n const lowerUrl = url.toLowerCase();\n return VIDEO_EXTENSIONS.some((ext) => lowerUrl.endsWith(ext));\n}\n\n/**\n * Gets the preview image URL for a video.\n * Uses provided preview URL if available, otherwise tries to derive from video URL.\n * @param videoUrl - The video URL\n * @param previewUrl - The preview URL from data (if available)\n * @returns The preview image URL or undefined\n */\nexport function getVideoPreviewUrl(\n videoUrl: string,\n previewUrl?: string\n): string | undefined {\n // Use provided preview URL if available\n if (previewUrl) return previewUrl;\n\n // Fallback: Derive thumbnail for AEM Assets URLs\n // Pattern: .../urn:aaid:aem:.../play -> .../urn:aaid:aem:.../as/thumbnail.png\n if (videoUrl?.includes('adobeaemcloud.com') && videoUrl.endsWith('/play')) {\n return videoUrl.replace(/\\/play$/, '/as/thumbnail.png');\n }\n\n return undefined;\n}\n\n/**\n * Converts YouTube/Vimeo URLs to embeddable URLs.\n * Returns original URL for other video sources.\n * @param url - The original video URL\n * @returns The embed URL\n */\nexport function getEmbedUrl(url: string): string {\n if (!url) return '';\n\n // YouTube: various formats\n // https://www.youtube.com/watch?v=VIDEO_ID\n // https://youtu.be/VIDEO_ID\n // https://www.youtube.com/embed/VIDEO_ID\n const youtubeMatch = url.match(\n /(?:youtube\\.com\\/(?:watch\\?v=|embed\\/)|youtu\\.be\\/)([a-zA-Z0-9_-]+)/\n );\n if (youtubeMatch) {\n return `https://www.youtube.com/embed/${youtubeMatch[1]}?rel=0`;\n }\n\n // Vimeo: various formats\n // https://vimeo.com/VIDEO_ID\n // https://player.vimeo.com/video/VIDEO_ID\n const vimeoMatch = url.match(/vimeo\\.com\\/(?:video\\/)?(\\d+)/);\n if (vimeoMatch) {\n return `https://player.vimeo.com/video/${vimeoMatch[1]}`;\n }\n\n // Return original URL for other sources (AEM, etc.)\n return url;\n}\n","/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2026 Adobe\n * All Rights Reserved.\n * __________________\n * NOTICE: All information contained herein is, and remains\n * the property of Adobe and its suppliers, if any. The intellectual\n * and technical concepts contained herein are proprietary to Adobe\n * and its suppliers and are protected by all applicable intellectual\n * property laws, including trade secret and copyright laws.\n * Dissemination of this information or reproduction of this material\n * is strictly forbidden unless prior written permission is obtained\n * from Adobe.\n */\n\nimport { useEffect, useState } from 'preact/compat';\n\nexport interface UsePreloadImagesOptions {\n /** Skip the preload entirely; ready resolves to true synchronously. */\n enabled?: boolean;\n /**\n * Maximum time to wait for all images before giving up and reporting ready=true\n * with error=true. Prevents a single broken URL from soft-locking consumers.\n */\n timeoutMs?: number;\n}\n\nexport interface UsePreloadImagesResult {\n ready: boolean;\n error: boolean;\n}\n\nconst DEFAULT_TIMEOUT_MS = 3000;\n\n/**\n * Preloads a list of image URLs in the background and reports when all are\n * decoded and ready to paint. Used to avoid visible flicker when a gallery\n * needs to swap to a new set of images.\n *\n * - Calls img.decode() when available (guarantees paint readiness); falls\n * back to onload otherwise.\n * - Cancels in-flight loads if the URL list changes.\n * - Always resolves ready=true after timeoutMs as a safety fallback.\n *\n * `ready` is derived synchronously from the URL list, so a transition to a\n * new non-empty url set immediately reports ready=false in the same render —\n * avoiding races where consumers would otherwise see a stale ready=true.\n */\nexport function usePreloadImages(\n urls: string[],\n {\n enabled = true,\n timeoutMs = DEFAULT_TIMEOUT_MS,\n }: UsePreloadImagesOptions = {}\n): UsePreloadImagesResult {\n const key = urls.join(' ');\n const trivial = !enabled || urls.length === 0;\n\n const [loadedKey, setLoadedKey] = useState<string | null>(null);\n const [errorKey, setErrorKey] = useState<string | null>(null);\n\n useEffect(() => {\n if (trivial) {\n setLoadedKey(key);\n setErrorKey(null);\n return undefined;\n }\n\n let cancelled = false;\n let remaining = urls.length;\n let hadError = false;\n\n const finish = () => {\n if (cancelled) return;\n cancelled = true;\n if (hadError) setErrorKey(key);\n setLoadedKey(key);\n };\n\n const timeoutId = window.setTimeout(() => {\n hadError = true;\n finish();\n }, timeoutMs);\n\n const onOne = (failed: boolean) => {\n if (cancelled) return;\n if (failed) hadError = true;\n remaining -= 1;\n if (remaining <= 0) {\n window.clearTimeout(timeoutId);\n finish();\n }\n };\n\n urls.forEach((url) => {\n const img = new Image();\n img.src = url;\n if (typeof img.decode === 'function') {\n img\n .decode()\n .then(() => onOne(false))\n .catch(() => onOne(true));\n } else {\n img.onload = () => onOne(false);\n img.onerror = () => onOne(true);\n }\n });\n\n return () => {\n cancelled = true;\n window.clearTimeout(timeoutId);\n };\n // key intentionally drives the effect; urls is its source.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [key, enabled, timeoutMs, trivial]);\n\n const ready = trivial || loadedKey === key;\n const error = errorKey === key;\n return { ready, error };\n}\n","import {\n HTMLAttributes,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'preact/compat';\nimport {\n Container,\n ResolveImageUrlOptions,\n Slot,\n SlotProps,\n} from '@adobe-commerce/elsie/lib';\n\nimport { Image, ImageProps } from '@adobe-commerce/elsie/components';\nimport { events } from '@adobe-commerce/event-bus';\nimport { ProductModel } from '@/pdp/data/models/product-model';\nimport { getFetchedProductData } from '@/pdp/api';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { Carousel, Overlay, Zoom } from '@/pdp/components';\nimport {\n isDirectVideoFile,\n getEmbedUrl,\n getVideoPreviewUrl,\n getVideoMimeType,\n} from '@/pdp/lib/videoUtils';\nimport { usePreloadImages } from '@/pdp/lib/usePreloadImages';\nimport '@/pdp/containers/ProductGallery/ProductGallery.css';\n\nconst CROSSFADE_MS = 200;\n\ntype DefaultSlotContext = {\n data: ProductModel | null;\n};\n\ntype VideosProp = boolean | { position: 'first' | 'last' };\n\nexport interface ProductGalleryProps\n extends Omit<HTMLAttributes<HTMLDivElement>, 'controls'> {\n scope?: string;\n controls?: 'thumbnailsRow' | 'thumbnailsColumn' | 'dots' | null;\n loop?: boolean;\n peak?: boolean;\n gap?: 'small' | 'medium' | 'large' | null;\n arrows?: boolean;\n arrowsOnMainImage?: boolean;\n imageParams?: ResolveImageUrlOptions;\n thumbnailParams?: ResolveImageUrlOptions;\n zoom?: { closeButton: boolean } | boolean;\n videos?: VideosProp;\n slots?: {\n CarouselThumbnail?: SlotProps<\n DefaultSlotContext & {\n defaultImageProps: ImageProps;\n mediaType?: 'image' | 'video';\n previewUrl?: string;\n }\n >;\n CarouselMainImage?: SlotProps<\n DefaultSlotContext & {\n defaultImageProps: ImageProps;\n mediaType?: 'image' | 'video';\n previewUrl?: string;\n }\n >;\n };\n}\n\ntype MediaItem =\n | { type: 'image'; url: string; label: string }\n | {\n type: 'video';\n url: string;\n label: string;\n preview?: { url: string; label?: string };\n };\n\nconst buildMediaItems = (\n data: ProductModel | null,\n videos: VideosProp\n): MediaItem[] => {\n const imageItems: MediaItem[] =\n data?.images?.map((img) => ({\n type: 'image' as const,\n url: img.url,\n label: img.label,\n })) ?? [];\n\n if (!videos) return imageItems;\n\n const videoItems: MediaItem[] =\n data?.videos?.map((video) => ({\n type: 'video' as const,\n url: video.url,\n label: video.title ?? '',\n preview: video.preview,\n })) ?? [];\n\n const position = typeof videos === 'object' ? videos.position : 'last';\n return position === 'first'\n ? [...videoItems, ...imageItems]\n : [...imageItems, ...videoItems];\n};\n\nconst collectPreloadUrls = (\n data: ProductModel | null,\n videos: VideosProp\n): string[] => {\n if (!data) return [];\n const urls: string[] = (data.images ?? []).map((img) => img.url);\n if (videos) {\n (data.videos ?? []).forEach((video) => {\n const previewUrl = getVideoPreviewUrl(video.url, video.preview?.url);\n if (previewUrl) urls.push(previewUrl);\n });\n }\n return urls;\n};\n\nconst sameMediaUrls = (\n a: ProductModel | null,\n b: ProductModel | null,\n videos: VideosProp\n): boolean => {\n if (a === b) return true;\n if (!a || !b) return false;\n\n const aImages = a.images ?? [];\n const bImages = b.images ?? [];\n if (aImages.length !== bImages.length) return false;\n for (let i = 0; i < aImages.length; i += 1) {\n if (aImages[i].url !== bImages[i].url) return false;\n }\n\n if (!videos) return true;\n\n const aVideos = a.videos ?? [];\n const bVideos = b.videos ?? [];\n if (aVideos.length !== bVideos.length) return false;\n for (let i = 0; i < aVideos.length; i += 1) {\n if (aVideos[i].url !== bVideos[i].url) return false;\n }\n return true;\n};\n\nconst prefersReducedMotion = (): boolean =>\n typeof window !== 'undefined' &&\n typeof window.matchMedia === 'function' &&\n window.matchMedia('(prefers-reduced-motion: reduce)').matches;\n\ninterface GalleryContentArgs {\n data: ProductModel | null;\n videos: VideosProp;\n imageParams?: ResolveImageUrlOptions;\n thumbnailParams?: ResolveImageUrlOptions;\n zoom: ProductGalleryProps['zoom'];\n slots: ProductGalleryProps['slots'];\n ImagesLabel?: string;\n onZoom?: (key: number) => void;\n}\n\nconst useGalleryContent = ({\n data,\n videos,\n imageParams,\n thumbnailParams,\n zoom,\n slots,\n ImagesLabel,\n onZoom,\n}: GalleryContentArgs) => {\n const defaultSlotContext: DefaultSlotContext = useMemo(\n () => ({ data }),\n [data]\n );\n\n const mediaItems = useMemo(\n () => buildMediaItems(data, videos),\n [data, videos]\n );\n\n const galleryItems = useMemo(\n () =>\n mediaItems.length > 0 ? (\n mediaItems.map((item, key) => {\n const Item = ({ ...itemProps }) => {\n const defaultProps = {\n title: item.label,\n src: item.url,\n alt: ImagesLabel?.replace('{product}', item.label)\n .replace('{key}', String(key + 1))\n .replace('{total}', String(mediaItems.length)),\n\n // TODO: change width/height for image aspect ratio\n // pending https://jira.corp.adobe.com/browse/DATA-5122\n width: imageParams?.width ?? 960,\n height: imageParams?.height ?? 1191,\n params: imageParams ?? { width: 960 },\n loading: key === 0 ? 'eager' : 'lazy',\n ...itemProps,\n } as const;\n\n const previewUrl =\n item.type === 'video'\n ? getVideoPreviewUrl(item.url, item.preview?.url)\n : undefined;\n\n return (\n <Slot\n name=\"CarouselMainImage\"\n slot={slots?.CarouselMainImage}\n context={{\n ...defaultSlotContext,\n defaultImageProps: defaultProps,\n mediaType: item.type,\n previewUrl,\n }}\n >\n {item.type === 'video' ? (\n isDirectVideoFile(item.url) ? (\n <video\n className=\"pdp-product-gallery__video\"\n controls\n preload=\"metadata\"\n poster={previewUrl}\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n >\n <source\n src={item.url}\n type={getVideoMimeType(item.url)}\n />\n Your browser does not support the video tag.\n </video>\n ) : (\n <iframe\n className=\"pdp-product-gallery__video-iframe\"\n src={getEmbedUrl(item.url)}\n title={item.label || 'Product video'}\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n loading=\"lazy\"\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\"\n allowFullScreen\n />\n )\n ) : (\n <Image {...defaultProps} />\n )}\n </Slot>\n );\n };\n\n return zoom && item.type === 'image' ? (\n <Zoom\n key={item.url}\n closeButton={typeof zoom === 'object' ? zoom.closeButton : false}\n >\n <Item />\n </Zoom>\n ) : (\n <Item key={item.url} onClick={() => onZoom?.(key)} />\n );\n })\n ) : (\n <img\n key=\"pleaceholder\"\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\n className=\"pdp-product-gallery__images__placeholder\"\n width={imageParams?.width ?? 960}\n height={imageParams?.height ?? 1191}\n aria-hidden\n />\n ),\n [\n ImagesLabel,\n mediaItems,\n imageParams,\n zoom,\n defaultSlotContext,\n slots?.CarouselMainImage,\n onZoom,\n ]\n );\n\n const thumbnails = useMemo(() => {\n return mediaItems.length > 0 ? (\n mediaItems.map((item, key) => {\n const defaultProps = {\n title: item.label,\n src: item.url,\n alt: ImagesLabel?.replace('{product}', item.label)\n .replace('{key}', String(key + 1))\n .replace('{total}', String(mediaItems.length)),\n\n // TODO: change width/height for image aspect ratio\n // pending https://jira.corp.adobe.com/browse/DATA-5122\n width: thumbnailParams?.width ?? 200,\n height: thumbnailParams?.height ?? 248,\n params: thumbnailParams ?? { width: 200 },\n loading: 'lazy',\n } as const;\n\n const previewUrl =\n item.type === 'video'\n ? getVideoPreviewUrl(item.url, item.preview?.url)\n : undefined;\n\n return (\n <Slot\n key={item.url}\n name=\"CarouselThumbnail\"\n slotTag=\"span\"\n contentTag=\"span\"\n slot={slots?.CarouselThumbnail}\n context={{\n ...defaultSlotContext,\n defaultImageProps: defaultProps,\n mediaType: item.type,\n previewUrl,\n }}\n >\n {item.type === 'video' ? (\n <span\n className=\"pdp-product-gallery__video-thumbnail\"\n role=\"img\"\n aria-label={`Play video: ${item.label || 'Product video'}`}\n >\n {previewUrl ? (\n <img\n src={previewUrl}\n alt=\"\"\n width={thumbnailParams?.width ?? 200}\n height={thumbnailParams?.height ?? 248}\n loading=\"lazy\"\n aria-hidden=\"true\"\n />\n ) : (\n <span\n className=\"pdp-product-gallery__video-thumbnail-placeholder\"\n style={{\n width: thumbnailParams?.width ?? 200,\n height: thumbnailParams?.height ?? 248,\n }}\n aria-hidden=\"true\"\n />\n )}\n <span\n className=\"pdp-product-gallery__video-icon\"\n aria-hidden=\"true\"\n >\n ▶\n </span>\n </span>\n ) : (\n <Image {...defaultProps} />\n )}\n </Slot>\n );\n })\n ) : (\n <img\n key=\"pleaceholder\"\n src=\"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\"\n className=\"pdp-product-gallery__thumbnail__placeholder\"\n width={thumbnailParams?.width ?? 200}\n height={thumbnailParams?.height ?? 248}\n aria-hidden\n />\n );\n }, [\n ImagesLabel,\n mediaItems,\n thumbnailParams,\n defaultSlotContext,\n slots?.CarouselThumbnail,\n ]);\n\n return { mediaItems, galleryItems, thumbnails };\n};\n\nexport const ProductGallery: Container<\n ProductGalleryProps,\n ProductModel | null\n> = ({\n initialData = null,\n scope,\n controls = 'dots',\n loop = true,\n peak = false,\n gap = null,\n arrows = true,\n arrowsOnMainImage = false,\n zoom = false,\n videos = false,\n imageParams,\n thumbnailParams,\n children,\n slots,\n ...props\n}) => {\n const ImagesLabel = useText('PDP.Product.Image.label').label;\n\n const [displayedData, setDisplayedData] = useState<ProductModel | null>(\n initialData\n );\n const [pendingData, setPendingData] = useState<ProductModel | null>(null);\n const [entering, setEntering] = useState(false);\n const [zoomTo, setZoomTo] = useState<number | null>(null);\n\n const displayedRef = useRef<ProductModel | null>(initialData);\n useEffect(() => {\n displayedRef.current = displayedData;\n }, [displayedData]);\n\n useEffect(() => {\n const dataEvent = events.on(\n 'pdp/data',\n (newData: ProductModel | null) => {\n if (!newData) {\n setDisplayedData(null);\n setPendingData(null);\n setEntering(false);\n return;\n }\n\n if (sameMediaUrls(displayedRef.current, newData, videos)) {\n // Same media set — fast path, skip buffering and crossfade.\n setDisplayedData(newData);\n setPendingData(null);\n setEntering(false);\n return;\n }\n\n setPendingData(newData);\n setEntering(false);\n },\n { scope }\n );\n return () => {\n dataEvent?.off();\n };\n }, [scope, videos]);\n\n const pendingUrls = useMemo(\n () => collectPreloadUrls(pendingData, videos),\n [pendingData, videos]\n );\n\n const { ready: pendingReady } = usePreloadImages(pendingUrls, {\n enabled: !!pendingData,\n });\n\n useEffect(() => {\n if (!pendingData || !pendingReady) return undefined;\n\n const promote = () => {\n setDisplayedData(pendingData);\n setPendingData(null);\n setEntering(false);\n };\n\n if (prefersReducedMotion()) {\n promote();\n return undefined;\n }\n\n // Mount pending layer in opacity:0, flip to 1 on the next frame so the\n // browser commits the initial style before the transition starts.\n const raf = window.requestAnimationFrame(() => setEntering(true));\n const timer = window.setTimeout(promote, CROSSFADE_MS + 50);\n\n return () => {\n window.cancelAnimationFrame(raf);\n window.clearTimeout(timer);\n };\n }, [pendingReady, pendingData]);\n\n const { mediaItems, galleryItems, thumbnails } = useGalleryContent({\n data: displayedData,\n videos,\n imageParams,\n thumbnailParams,\n zoom,\n slots,\n ImagesLabel,\n onZoom: setZoomTo,\n });\n\n const pendingContent = useGalleryContent({\n data: pendingData,\n videos,\n imageParams,\n thumbnailParams,\n zoom,\n slots,\n ImagesLabel,\n // No onZoom: pending layer is non-interactive.\n });\n\n if (!displayedData) return null;\n\n const stackClassName = ['pdp-product-gallery__stack', props.className]\n .filter(Boolean)\n .join(' ');\n\n const pendingLayerClassName = [\n 'pdp-product-gallery__layer',\n 'pdp-product-gallery__layer--pending',\n entering ? 'pdp-product-gallery__layer--entering' : '',\n ]\n .filter(Boolean)\n .join(' ');\n\n return (\n <div {...props} className={stackClassName}>\n <div className=\"pdp-product-gallery__layer\">\n <Carousel\n arrows={!!mediaItems.length && arrows}\n arrowsOnMainImage={arrowsOnMainImage}\n controls={mediaItems.length ? controls : undefined}\n loop={loop && !!mediaItems.length}\n peak={!!mediaItems.length && peak}\n gap={gap}\n defaultIndex={zoomTo || 0}\n thumbnails={thumbnails}\n >\n {galleryItems}\n </Carousel>\n </div>\n\n {pendingData ? (\n <div className={pendingLayerClassName} aria-hidden=\"true\">\n <Carousel\n arrows={!!pendingContent.mediaItems.length && arrows}\n arrowsOnMainImage={arrowsOnMainImage}\n controls={pendingContent.mediaItems.length ? controls : undefined}\n loop={loop && !!pendingContent.mediaItems.length}\n peak={!!pendingContent.mediaItems.length && peak}\n gap={gap}\n defaultIndex={0}\n thumbnails={pendingContent.thumbnails}\n >\n {pendingContent.galleryItems}\n </Carousel>\n </div>\n ) : null}\n\n {/* Zoom Overlay (always uses displayed data) */}\n {zoomTo === null ? null : (\n <Overlay onClose={() => setZoomTo(null)} centered>\n <Carousel\n arrows={!!mediaItems.length}\n width=\"100%\"\n height=\"100%\"\n defaultIndex={zoomTo}\n loop={loop && !!mediaItems.length}\n controls=\"dots\"\n isZoomed\n >\n {galleryItems}\n </Carousel>\n </Overlay>\n )}\n </div>\n );\n};\n\nProductGallery.getInitialData = getFetchedProductData;\n"],"names":["VIDEO_EXTENSIONS","VIDEO_MIME_TYPES","getVideoMimeType","url","lowerUrl","ext","isDirectVideoFile","getVideoPreviewUrl","videoUrl","previewUrl","getEmbedUrl","youtubeMatch","vimeoMatch","DEFAULT_TIMEOUT_MS","usePreloadImages","urls","enabled","timeoutMs","key","trivial","loadedKey","setLoadedKey","useState","errorKey","setErrorKey","useEffect","cancelled","remaining","hadError","finish","timeoutId","onOne","failed","img","CROSSFADE_MS","buildMediaItems","data","videos","imageItems","_a","videoItems","_b","video","collectPreloadUrls","sameMediaUrls","a","b","aImages","bImages","i","aVideos","bVideos","prefersReducedMotion","useGalleryContent","imageParams","thumbnailParams","zoom","slots","ImagesLabel","onZoom","defaultSlotContext","useMemo","mediaItems","galleryItems","item","Item","itemProps","defaultProps","jsx","Slot","jsxs","Image","Zoom","thumbnails","ProductGallery","initialData","scope","controls","loop","peak","gap","arrows","arrowsOnMainImage","children","props","useText","displayedData","setDisplayedData","pendingData","setPendingData","entering","setEntering","zoomTo","setZoomTo","displayedRef","useRef","dataEvent","events","newData","pendingUrls","pendingReady","promote","raf","timer","pendingContent","stackClassName","pendingLayerClassName","Carousel","Overlay","getFetchedProductData"],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ADOBE CONFIDENTIAL
3
+ * __________________
4
+ * Copyright 2026 Adobe
5
+ * All Rights Reserved.
6
+ * __________________
7
+ * NOTICE: All information contained herein is, and remains
8
+ * the property of Adobe and its suppliers, if any. The intellectual
9
+ * and technical concepts contained herein are proprietary to Adobe
10
+ * and its suppliers and are protected by all applicable intellectual
11
+ * property laws, including trade secret and copyright laws.
12
+ * Dissemination of this information or reproduction of this material
13
+ * is strictly forbidden unless prior written permission is obtained
14
+ * from Adobe.
15
+ */
16
+ export interface UsePreloadImagesOptions {
17
+ /** Skip the preload entirely; ready resolves to true synchronously. */
18
+ enabled?: boolean;
19
+ /**
20
+ * Maximum time to wait for all images before giving up and reporting ready=true
21
+ * with error=true. Prevents a single broken URL from soft-locking consumers.
22
+ */
23
+ timeoutMs?: number;
24
+ }
25
+ export interface UsePreloadImagesResult {
26
+ ready: boolean;
27
+ error: boolean;
28
+ }
29
+ /**
30
+ * Preloads a list of image URLs in the background and reports when all are
31
+ * decoded and ready to paint. Used to avoid visible flicker when a gallery
32
+ * needs to swap to a new set of images.
33
+ *
34
+ * - Calls img.decode() when available (guarantees paint readiness); falls
35
+ * back to onload otherwise.
36
+ * - Cancels in-flight loads if the URL list changes.
37
+ * - Always resolves ready=true after timeoutMs as a safety fallback.
38
+ *
39
+ * `ready` is derived synchronously from the URL list, so a transition to a
40
+ * new non-empty url set immediately reports ready=false in the same render —
41
+ * avoiding races where consumers would otherwise see a stale ready=true.
42
+ */
43
+ export declare function usePreloadImages(urls: string[], { enabled, timeoutMs, }?: UsePreloadImagesOptions): UsePreloadImagesResult;
44
+ //# sourceMappingURL=usePreloadImages.d.ts.map
package/package.json CHANGED
@@ -1 +1 @@
1
- {"name": "@dropins/storefront-pdp", "version": "3.0.3", "@dropins/tools": "~1.5.0", "license": "SEE LICENSE IN LICENSE.md"}
1
+ {"name": "@dropins/storefront-pdp", "version": "3.0.4-beta1", "@dropins/tools": "~1.5.0", "license": "SEE LICENSE IN LICENSE.md"}
package/render.js CHANGED
@@ -1,6 +1,7 @@
1
1
  /*! Copyright 2026 Adobe
2
2
  All Rights Reserved. */
3
- (function(e,a){try{if(typeof document<"u"){const o=document.createElement("style"),p=a.styleId;for(const r in a.attributes)o.setAttribute(r,a.attributes[r]);o.setAttribute("data-dropin",p),o.appendChild(document.createTextNode(e));const t=document.querySelector('style[data-dropin="sdk"]');if(t)t.after(o);else{const r=document.querySelector('link[rel="stylesheet"], style');r?r.before(o):document.head.append(o)}}}catch(o){console.error("dropin-styles (injectCodeFunction)",o)}})(`.pdp-carousel{--icon-size: 32px;--icon-extra-padding: 8px;--thumb-peak: 32px;--thumbnails-col-width: 18.6%;position:relative;display:flex;flex-direction:var(--flex-carousel);align-items:center;gap:var(--gap);width:var(--width)}.pdp-carousel .dropin-button--tertiary--disabled:hover{color:var(--color-neutral-500)}.pdp-carousel--thumbnailsColumn{justify-content:flex-end;align-items:start}.pdp-carousel--thumbnailsRow{align-items:center}.pdp-carousel--arrows{padding-left:calc(var(--icon-size) + var(--icon-extra-padding));padding-right:calc(var(--icon-size) + var(--icon-extra-padding));width:calc(var(--width) - 2 * var(--icon-size) - 2 * var(--icon-extra-padding))}.pdp-carousel__button{position:absolute;top:50%;transform:translateY(-50%);padding:0}.pdp-carousel__button--prev{left:0}[dir=rtl] .pdp-carousel__button--prev{left:auto;right:0}[dir=rtl] .pdp-carousel__button--next{right:auto;left:0}.pdp-carousel__button__icon--next{transform:rotate(-90deg)}.pdp-carousel__button--next{right:0}.pdp-carousel__button__icon--prev{transform:rotate(90deg)}[dir=rtl] .pdp-carousel__button__icon--prev{transform:rotate(-90deg)}[dir=rtl] .pdp-carousel__button__icon--next{transform:rotate(90deg)}.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsColumn .pdp-carousel__button--prev{left:var(--thumbnails-col-width)}.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsRow .pdp-carousel__button--prev,.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsRow .pdp-carousel__button--next{top:calc(var(--height) / 2)}.pdp-carousel__button--thumbnailsColumn{margin:auto;padding:0}.pdp-carousel__button--thumbnailsRow{padding:0}.pdp-carousel__button__icon--thumbnailsColumn--prev{transform:rotate(180deg)}.pdp-carousel__button__icon--thumbnailsRow--prev{transform:rotate(90deg)}[dir=rtl] .pdp-carousel__button__icon--thumbnailsRow--prev,.pdp-carousel__button__icon--thumbnailsRow--next{transform:rotate(-90deg)}[dir=rtl] .pdp-carousel__button__icon--thumbnailsRow--next{transform:rotate(90deg)}.pdp-carousel__wrapper{--peak: 0px;scroll-padding:var(--peak);width:var(--total-width);height:var(--height);display:flex;flex-wrap:nowrap;gap:var(--gap);-webkit-overflow-scrolling:touch}.pdp-carousel__wrapper--horizontal{overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory}.pdp-carousel__slide{display:grid;grid-gap:var(--gap);grid-auto-columns:1fr;grid-auto-rows:100%;scroll-snap-align:end;scroll-snap-stop:always;flex-shrink:0}.pdp-carousel__slide--horizontal{grid-auto-flow:column;width:calc(100% - var(--peak))}.pdp-carousel__slide--horizontal.pdp-carousel__slide--orphan{width:calc((100% / var(--per-page) * var(--length)) - var(--peak) / var(--per-page) * var(--length) - var(--gap) / var(--per-page) * (var(--per-page) - var(--length)))}.pdp-carousel__slide *{vertical-align:middle;max-width:100%;max-height:100%;width:100%;height:100%}.pdp-carousel__slide img{object-fit:contain;width:100%;height:100%}.pdp-carousel__wrapper--peak{--peak: calc(var(--spacing-medium) + var(--gap))}.pdp-carousel__wrapper--scrollbar{--scroll-foreground: var(--color-neutral-600);--scroll-background: var(--color-neutral-400);scrollbar-color:var(--scroll-foreground) var(--scroll-background);scrollbar-width:thin}.pdp-carousel__wrapper:not(.pdp-carousel__wrapper--scrollbar){scrollbar-width:none}.pdp-carousel__wrapper:not(.pdp-carousel__wrapper--scrollbar)::-webkit-scrollbar{display:none}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar{width:4px;height:4px}.pdp-carousel__slide--hidden{opacity:0;pointer-events:none}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar-thumb{background:var(--scroll-foreground)}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar-track{background:var(--scroll-background)}.pdp-carousel__controls__container--thumbnailsRow{--thumbnails-spacing: var(--spacing-small);--thumbnails-box-width: var(--spacing-xxsmall);--thumbnails-rest: calc(var(--thumbnails-spacing) - var(--thumbnails-box-width));--thumbnails-width: calc((var(--width) - 2 * var(--icon-size) - 4 * var(--thumbnails-spacing) - var(--thumb-peak) - 2 * var(--thumbnails-box-width)) / 4);margin-top:var(--thumbnails-rest);width:var(--width);display:flex}.pdp-carousel__controls__container--thumbnailsColumn{--thumbnails-spacing: var(--spacing-small);--thumbnails-box-width: var(--spacing-xxsmall);--thumbnails-rest: calc(var(--thumbnails-spacing) - var(--thumbnails-box-width));--thumbnails-width: 100%;margin-right:var(--thumbnails-rest);width:calc(var(--thumbnails-col-width) - var(--thumbnails-rest))}.pdp-carousel__controls__container--thumbnailsColumn:not(.pdp-carousel__controls__container--no-arrows){height:calc(max(var(--height),4 * var(--thumbnails-spacing)) - 4 * var(--thumbnails-spacing))}.pdp-carousel__controls__container--thumbnailsColumn.pdp-carousel__controls__container--no-arrows{height:var(--height)}.pdp-carousel__controls__wrapper--thumbnailsColumn{max-height:100%;overflow-y:scroll}.pdp-carousel__controls__wrapper--thumbnailsRow{max-width:100%;overflow-x:scroll}.pdp-carousel__controls--thumbnailsRow>label>span:nth-last-child(1){margin-left:0;margin-right:var(--thumbnails-box-width)}[dir=rtl] .pdp-carousel__controls--thumbnailsRow>label>span:nth-last-child(1){margin-left:var(--thumbnails-box-width);margin-right:0}.pdp-carousel__controls__wrapper::-webkit-scrollbar{display:none}.pdp-carousel__controls__wrapper{-ms-overflow-style:none;scrollbar-width:none}.pdp-carousel__controls{display:flex;flex-direction:row;gap:var(--spacing-xsmall)}.pdp-carousel__controls--thumbnailsColumn{display:grid;grid-template-columns:repeat(1,1fr);grid-auto-rows:1fr;grid-row-gap:var(--thumbnails-spacing);padding:var(--thumbnails-box-width)}.pdp-carousel__controls--thumbnailsRow{grid-column-gap:var(--thumbnails-spacing);padding:var(--thumbnails-box-width)}.pdp-carousel__controls__button{border-radius:50%;width:10px;height:10px;padding:0;border:none;background:var(--color-neutral-400);cursor:pointer}.pdp-carousel__controls__button:hover{background:var(--color-neutral-500)}.pdp-carousel__controls__button:active,.pdp-carousel__controls__button--active{background:var(--color-neutral-600)}.pdp-carousel__slide--last{width:100%}.pdp-carousel__thumbnail__container input[type=radio]{opacity:0;position:absolute;width:0;height:0}.pdp-carousel__thumbnail__container .pdp-carousel__thumbnail__span{--bg-color: var(--color-neutral-300);display:inline-block;box-shadow:0 0 0 var(--shape-border-width-1) var(--color-neutral-400);position:relative;cursor:pointer;background-color:var(--bg-color);background-image:var(--background-image);background-repeat:no-repeat;background-size:cover;height:100%;width:100%;overflow:hidden}.pdp-carousel__thumbnail__content{width:inherit;position:absolute;top:-9999px;bottom:-9999px;left:-9999px;right:-9999px;margin:auto;overflow:hidden}.pdp-carousel__thumbnail__span:hover{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-500);outline:none}.pdp-carousel__controls__wrapper--thumbnailsRow .pdp-carousel__thumbnail__container{min-width:100px}.pdp-carousel__thumbnail__container input[type=radio]:not(:checked):active~.pdp-carousel-thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-1) var(--color-neutral-500);outline:none}.pdp-carousel__thumbnail__container input[type=radio]:checked~.pdp-carousel__thumbnail__span,.pdp-carousel__thumbnail--selected{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-800)}.pdp-carousel__thumbnail__container input[type=radio]:checked:active~.pdp-carousel__thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-600)}.pdp-carousel__thumbnail__container input[type=radio]:focus-visible+.pdp-carousel__thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-900),0 0 0 6px var(--color-neutral-400)}.pdp-carousel__thumbnail__span img{object-fit:cover;width:100%;height:100%}.pdp-product-gallery__video,.pdp-product-gallery__video-iframe{width:100%;height:100%;object-fit:contain}.pdp-product-gallery__video-iframe{border:none}.pdp-product-gallery__video-thumbnail{position:relative;display:inline-block;width:100%;height:100%}.pdp-product-gallery__video-thumbnail-placeholder{display:block;background-color:var(--color-neutral-300)}.pdp-product-gallery__video-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;width:40px;height:40px;background-color:#0009;border-radius:50%;color:var(--color-neutral-50);font-size:16px;pointer-events:none;transition:background-color .2s ease}.pdp-product-gallery__video-thumbnail:hover .pdp-product-gallery__video-icon,.pdp-product-gallery__video-thumbnail:focus-visible .pdp-product-gallery__video-icon{background-color:#000c}.pdp-product-gallery__video-thumbnail img{width:100%;height:100%;object-fit:cover}.pdp-product{display:grid;grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);margin:0 var(--grid-1-margins)}.pdp-product__gallery-column{display:none}.pdp-product__content-column{grid-column:1 / span 4;color:var(--color-neutral-800)}.pdp-product__header{display:grid;grid-auto-rows:max-content;margin-bottom:var(--spacing-medium)}.pdp-product__breadcrumbs{grid-column:1 / -1;padding:var(--spacing-medium) 0 var(--spacing-xxbig)}.pdp-product__title{font:var(--type-display-3-font);letter-spacing:var(--type-display-3-letter-spacing);margin:0}.pdp-product__sku{font:var(--type-body-1-default-font);max-width:600px}.pdp-product__prices{display:flex;gap:var(--spacing-xsmall);margin-top:var(--spacing-medium)}.pdp-product__out-of-stock__text{font:var(--type-headline-2-strong-font)}.pdp-product__options{grid-column:1 / span 4}.pdp-product__price,.pdp-product__price-special{font:var(--type-headline-2-strong-font)}.pdp-product__price,.pdp-product__sku,.pdp-product__price-special,.pdp-product__out-of-stock__text{letter-spacing:var(--type-headline-2-strong-letter-spacing)}.pdp-product__price--grey{color:var(--color-neutral-500)}.pdp-product__actions,.pdp-product__actions--out-of-stock{display:grid;grid-auto-flow:row;gap:var(--grid-1-gutters);grid-auto-rows:max-content;margin-bottom:var(--spacing-xxbig);margin-top:var(--spacing-big)}.pdp-product__short_description,.pdp-product__attributes{font:var(--type-body-1-strong-font);letter-spacing:var(--type-body-1-strong-letter-spacing);margin-bottom:var(--spacing-small)}.pdp-product__description,.pdp-product__attributes li{font:var(--type-body-1-default-font);letter-spacing:var(--type-body-1-default-letter-spacing)}.pdp-product__attributes{margin-top:var(--spacing-xsmall)}.pdp-product__short_description p,.pdp-product__description p,.pdp-product__attributes li p{margin:0}.pdp-product__short_description figure,.pdp-product__description figure{margin:0}.pdp-product__short_description img,.pdp-product__description img{max-width:100%}.pdp-product__images--carousel{max-height:60vh}.pdp-product__images--carousel--thumbnails{max-height:90vh}.pdp-product__images__placeholder{background-color:var(--color-neutral-200)}.pdp-product__buttons{display:grid;grid-auto-rows:max-content;grid-gap:var(--grid-1-gutters)}.pdp-product__buttons a{text-align:center}.pdp-product__buttons a:hover{text-decoration:none}.pdp-product__overlay .pdp-overlay__content{width:100%;height:100%}.pdp-product__overlay__carousel{width:100%;height:100%;box-sizing:border-box}.pdp-product__overlay__carousel img{width:100%;height:100%;object-fit:contain}.pdp-carousel__controls__container--thumbnailsRow img,.pdp-carousel__controls__container--thumbnailsColumn img{object-fit:cover}.pdp-product__quantity,.pdp-product__buttons{grid-column:1 / span 4}@media only screen and (min-width: 1024px){.pdp-product__breadcrumbs{grid-column:2 / -2}.pdp-product{grid-template-columns:repeat(var(--grid-3-columns),1fr);grid-column-gap:var(--grid-3-gutters);margin:0 var(--grid-3-margins);position:relative}.pdp-product__column-container{position:relative;display:flex;flex-direction:column}.pdp-product__column-body{position:sticky;bottom:30%;align-self:flex-end}.pdp-product__gallery-column{display:block;grid-column:2 / span 6}.pdp-product__content-column{grid-column:8 / span 4;padding-top:var(--spacing-medium)}.pdp-product__images--carousel{display:none}.pdp-product__header{margin-bottom:var(--spacing-small)}.pdp-product__actions{grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);grid-row-gap:var(--spacing-xxbig);margin-top:var(--spacing-xxbig)}.pdp-product__quantity{grid-column:1 / span 2;width:fit-content}.pdp-product__actions--out-of-stock{grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);grid-row-gap:var(--spacing-xxbig);margin-top:var(--spacing-small)}.pdp-product__buttons{grid-column:1 / span 4}.pdp-product__out-of-stock__text{grid-column:1 / span 2}.pdp-product__gallery-content,.pdp-product__content{grid-column:2 / span 6}}.pdp-gallery-grid{display:grid;grid-gap:var(--gap);grid-template-columns:repeat(1,1fr);grid-auto-rows:max-content}.pdp-gallery-grid__item>*{height:100%;max-width:100%;min-width:100%;vertical-align:middle;width:100%;object-fit:contain}.pdp-gallery-grid__item:only-child{grid-column:1 / span 2}.pdp-gallery-grid__item:first-child:nth-last-child(2){grid-column:1 / span 2;grid-row:1}.pdp-gallery-grid__item:nth-child(2):last-child{grid-column:1 / 3;grid-row:2}@media only screen and (min-width: 1024px){.pdp-gallery-grid{grid-template-columns:repeat(2,1fr)}}.pdp-price-range .dropin-price{letter-spacing:var(--type-headline-2-strong-letter-spacing);font:var(--type-headline-2-strong-font)}.pdp-price-range__label{font:var(--type-headline-2-strong-font);letter-spacing:var(--type-body-1-strong-letter-spacing);margin-left:5px;margin-right:5px}.pdp-overlay{position:fixed;top:0;left:0;z-index:1000;right:0;bottom:0;background-color:var(--color-neutral-50);display:flex;padding:var(--spacing-medium)}.pdp-overlay--centered{align-items:center;justify-content:center}.pdp-overlay__content{display:flex;height:100%;width:100%}.pdp-overlay__close-button{position:absolute;top:var(--spacing-medium);right:var(--spacing-medium);z-index:1001}.pdp-swatches{display:grid;grid-auto-flow:row;grid-gap:var(--grid-1-gutters);grid-auto-rows:max-content;position:relative;z-index:0}.pdp-swatches__field{display:grid;font:var(--type-body-2-strong-font);grid-auto-rows:max-content;gap:var(--spacing-xsmall)}.pdp-swatches__field__label{font:var(--type-details-caption-1-font);letter-spacing:var(--type-details-caption-1-letter-spacing);color:var(--color-neutral-800)}.pdp-swatches__options{display:flex;flex-wrap:wrap;gap:var(--spacing-xsmall)}.pdp-zoom{cursor:zoom-in;overflow:hidden;margin:0;position:relative}.pdp-zoom img{height:100%;max-width:100%;min-width:100%;object-fit:contain;position:relative;transition:transform .2s ease-out;transform:var(--transform);vertical-align:middle;width:100%;will-change:transform;backface-visibility:hidden;transform-style:preserve-3d}.pdp-zoom .pdp-zoom__close-button{background-color:var(--color-neutral-50);border-radius:50%;position:absolute;right:var(--spacing-small);top:var(--spacing-small);width:10px;height:10px}.pdp-zoom.pdp-zoom--zoomed{cursor:zoom-out}.pdp-zoom.pdp-zoom--no-effects{cursor:default}@media only screen and (max-width: 768px){.pdp-zoom img{display:block;width:100%;height:100%;transition:transform .2s ease-out;transform:var(--transform);will-change:transform;backface-visibility:hidden}}.pdp-header{display:grid;grid-auto-rows:max-content}.pdp-header__title{font:var(--type-display-3-font);letter-spacing:var(--type-display-3-letter-spacing);margin:0}.pdp-header__sku,.pdp-header__variant{font:var(--type-body-1-default-font);max-width:600px}.pdp-header__variant-separator{margin-inline:.25em}.pdp-header__variant-sku{font:var(--type-body-1-default-font)}.pdp-price{display:flex;flex-wrap:wrap;gap:var(--spacing-xsmall);margin-top:var(--spacing-medium)}.pdp-price__amount,.pdp-price__amount-special{font:var(--type-headline-2-strong-font);letter-spacing:var(--type-headline-2-strong-letter-spacing)}.pdp-price__amount--grey{color:var(--color-neutral-500)}.pdp-price__tiers{flex:0 0 100%}.pdp-price-tiers{display:flex;flex-direction:column;gap:8px;margin-top:8px}.pdp-price-tiers__tier{font:var(--type-body-2-font);color:var(--color-text-primary);line-height:1.4}.pdp-price-tiers__text{display:inline}.pdp-price-tiers__price{font-weight:600;color:var(--color-text-primary)}.pdp-price-tiers__savings{font-weight:600;color:var(--color-success)}
3
+ (function(e,a){try{if(typeof document<"u"){const o=document.createElement("style"),p=a.styleId;for(const r in a.attributes)o.setAttribute(r,a.attributes[r]);o.setAttribute("data-dropin",p),o.appendChild(document.createTextNode(e));const t=document.querySelector('style[data-dropin="sdk"]');if(t)t.after(o);else{const r=document.querySelector('link[rel="stylesheet"], style');r?r.before(o):document.head.append(o)}}}catch(o){console.error("dropin-styles (injectCodeFunction)",o)}})(`.pdp-product-gallery__stack{position:relative}.pdp-product-gallery__layer{transition:opacity .18s ease-out}.pdp-product-gallery__layer--pending{position:absolute;top:0;right:0;bottom:0;left:0;opacity:0;pointer-events:none}.pdp-product-gallery__layer--pending.pdp-product-gallery__layer--entering{opacity:1}@media (prefers-reduced-motion: reduce){.pdp-product-gallery__layer{transition:none}}
4
+ .pdp-carousel{--icon-size: 32px;--icon-extra-padding: 8px;--thumb-peak: 32px;--thumbnails-col-width: 18.6%;position:relative;display:flex;flex-direction:var(--flex-carousel);align-items:center;gap:var(--gap);width:var(--width)}.pdp-carousel .dropin-button--tertiary--disabled:hover{color:var(--color-neutral-500)}.pdp-carousel--thumbnailsColumn{justify-content:flex-end;align-items:start}.pdp-carousel--thumbnailsRow{align-items:center}.pdp-carousel--arrows{padding-left:calc(var(--icon-size) + var(--icon-extra-padding));padding-right:calc(var(--icon-size) + var(--icon-extra-padding));width:calc(var(--width) - 2 * var(--icon-size) - 2 * var(--icon-extra-padding))}.pdp-carousel__button{position:absolute;top:50%;transform:translateY(-50%);padding:0}.pdp-carousel__button--prev{left:0}[dir=rtl] .pdp-carousel__button--prev{left:auto;right:0}[dir=rtl] .pdp-carousel__button--next{right:auto;left:0}.pdp-carousel__button__icon--next{transform:rotate(-90deg)}.pdp-carousel__button--next{right:0}.pdp-carousel__button__icon--prev{transform:rotate(90deg)}[dir=rtl] .pdp-carousel__button__icon--prev{transform:rotate(-90deg)}[dir=rtl] .pdp-carousel__button__icon--next{transform:rotate(90deg)}.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsColumn .pdp-carousel__button--prev{left:var(--thumbnails-col-width)}.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsRow .pdp-carousel__button--prev,.pdp-carousel--main-image-controls.pdp-carousel--thumbnailsRow .pdp-carousel__button--next{top:calc(var(--height) / 2)}.pdp-carousel__button--thumbnailsColumn{margin:auto;padding:0}.pdp-carousel__button--thumbnailsRow{padding:0}.pdp-carousel__button__icon--thumbnailsColumn--prev{transform:rotate(180deg)}.pdp-carousel__button__icon--thumbnailsRow--prev{transform:rotate(90deg)}[dir=rtl] .pdp-carousel__button__icon--thumbnailsRow--prev,.pdp-carousel__button__icon--thumbnailsRow--next{transform:rotate(-90deg)}[dir=rtl] .pdp-carousel__button__icon--thumbnailsRow--next{transform:rotate(90deg)}.pdp-carousel__wrapper{--peak: 0px;scroll-padding:var(--peak);width:var(--total-width);height:var(--height);display:flex;flex-wrap:nowrap;gap:var(--gap);-webkit-overflow-scrolling:touch}.pdp-carousel__wrapper--horizontal{overflow-x:auto;overflow-y:hidden;scroll-snap-type:x mandatory}.pdp-carousel__slide{display:grid;grid-gap:var(--gap);grid-auto-columns:1fr;grid-auto-rows:100%;scroll-snap-align:end;scroll-snap-stop:always;flex-shrink:0}.pdp-carousel__slide--horizontal{grid-auto-flow:column;width:calc(100% - var(--peak))}.pdp-carousel__slide--horizontal.pdp-carousel__slide--orphan{width:calc((100% / var(--per-page) * var(--length)) - var(--peak) / var(--per-page) * var(--length) - var(--gap) / var(--per-page) * (var(--per-page) - var(--length)))}.pdp-carousel__slide *{vertical-align:middle;max-width:100%;max-height:100%;width:100%;height:100%}.pdp-carousel__slide img{object-fit:contain;width:100%;height:100%}.pdp-carousel__wrapper--peak{--peak: calc(var(--spacing-medium) + var(--gap))}.pdp-carousel__wrapper--scrollbar{--scroll-foreground: var(--color-neutral-600);--scroll-background: var(--color-neutral-400);scrollbar-color:var(--scroll-foreground) var(--scroll-background);scrollbar-width:thin}.pdp-carousel__wrapper:not(.pdp-carousel__wrapper--scrollbar){scrollbar-width:none}.pdp-carousel__wrapper:not(.pdp-carousel__wrapper--scrollbar)::-webkit-scrollbar{display:none}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar{width:4px;height:4px}.pdp-carousel__slide--hidden{opacity:0;pointer-events:none}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar-thumb{background:var(--scroll-foreground)}.pdp-carousel__wrapper--scrollbar::-webkit-scrollbar-track{background:var(--scroll-background)}.pdp-carousel__controls__container--thumbnailsRow{--thumbnails-spacing: var(--spacing-small);--thumbnails-box-width: var(--spacing-xxsmall);--thumbnails-rest: calc(var(--thumbnails-spacing) - var(--thumbnails-box-width));--thumbnails-width: calc((var(--width) - 2 * var(--icon-size) - 4 * var(--thumbnails-spacing) - var(--thumb-peak) - 2 * var(--thumbnails-box-width)) / 4);margin-top:var(--thumbnails-rest);width:var(--width);display:flex}.pdp-carousel__controls__container--thumbnailsColumn{--thumbnails-spacing: var(--spacing-small);--thumbnails-box-width: var(--spacing-xxsmall);--thumbnails-rest: calc(var(--thumbnails-spacing) - var(--thumbnails-box-width));--thumbnails-width: 100%;margin-right:var(--thumbnails-rest);width:calc(var(--thumbnails-col-width) - var(--thumbnails-rest))}.pdp-carousel__controls__container--thumbnailsColumn:not(.pdp-carousel__controls__container--no-arrows){height:calc(max(var(--height),4 * var(--thumbnails-spacing)) - 4 * var(--thumbnails-spacing))}.pdp-carousel__controls__container--thumbnailsColumn.pdp-carousel__controls__container--no-arrows{height:var(--height)}.pdp-carousel__controls__wrapper--thumbnailsColumn{max-height:100%;overflow-y:scroll}.pdp-carousel__controls__wrapper--thumbnailsRow{max-width:100%;overflow-x:scroll}.pdp-carousel__controls--thumbnailsRow>label>span:nth-last-child(1){margin-left:0;margin-right:var(--thumbnails-box-width)}[dir=rtl] .pdp-carousel__controls--thumbnailsRow>label>span:nth-last-child(1){margin-left:var(--thumbnails-box-width);margin-right:0}.pdp-carousel__controls__wrapper::-webkit-scrollbar{display:none}.pdp-carousel__controls__wrapper{-ms-overflow-style:none;scrollbar-width:none}.pdp-carousel__controls{display:flex;flex-direction:row;gap:var(--spacing-xsmall)}.pdp-carousel__controls--thumbnailsColumn{display:grid;grid-template-columns:repeat(1,1fr);grid-auto-rows:1fr;grid-row-gap:var(--thumbnails-spacing);padding:var(--thumbnails-box-width)}.pdp-carousel__controls--thumbnailsRow{grid-column-gap:var(--thumbnails-spacing);padding:var(--thumbnails-box-width)}.pdp-carousel__controls__button{border-radius:50%;width:10px;height:10px;padding:0;border:none;background:var(--color-neutral-400);cursor:pointer}.pdp-carousel__controls__button:hover{background:var(--color-neutral-500)}.pdp-carousel__controls__button:active,.pdp-carousel__controls__button--active{background:var(--color-neutral-600)}.pdp-carousel__slide--last{width:100%}.pdp-carousel__thumbnail__container input[type=radio]{opacity:0;position:absolute;width:0;height:0}.pdp-carousel__thumbnail__container .pdp-carousel__thumbnail__span{--bg-color: var(--color-neutral-300);display:inline-block;box-shadow:0 0 0 var(--shape-border-width-1) var(--color-neutral-400);position:relative;cursor:pointer;background-color:var(--bg-color);background-image:var(--background-image);background-repeat:no-repeat;background-size:cover;height:100%;width:100%;overflow:hidden}.pdp-carousel__thumbnail__content{width:inherit;position:absolute;top:-9999px;bottom:-9999px;left:-9999px;right:-9999px;margin:auto;overflow:hidden}.pdp-carousel__thumbnail__span:hover{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-500);outline:none}.pdp-carousel__controls__wrapper--thumbnailsRow .pdp-carousel__thumbnail__container{min-width:100px}.pdp-carousel__thumbnail__container input[type=radio]:not(:checked):active~.pdp-carousel-thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-1) var(--color-neutral-500);outline:none}.pdp-carousel__thumbnail__container input[type=radio]:checked~.pdp-carousel__thumbnail__span,.pdp-carousel__thumbnail--selected{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-800)}.pdp-carousel__thumbnail__container input[type=radio]:checked:active~.pdp-carousel__thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-50),0 0 0 var(--shape-border-width-4) var(--color-neutral-600)}.pdp-carousel__thumbnail__container input[type=radio]:focus-visible+.pdp-carousel__thumbnail__span{box-shadow:0 0 0 var(--shape-border-width-3) var(--color-neutral-900),0 0 0 6px var(--color-neutral-400)}.pdp-carousel__thumbnail__span img{object-fit:cover;width:100%;height:100%}.pdp-product-gallery__video,.pdp-product-gallery__video-iframe{width:100%;height:100%;object-fit:contain}.pdp-product-gallery__video-iframe{border:none}.pdp-product-gallery__video-thumbnail{position:relative;display:inline-block;width:100%;height:100%}.pdp-product-gallery__video-thumbnail-placeholder{display:block;background-color:var(--color-neutral-300)}.pdp-product-gallery__video-icon{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);display:flex;align-items:center;justify-content:center;width:40px;height:40px;background-color:#0009;border-radius:50%;color:var(--color-neutral-50);font-size:16px;pointer-events:none;transition:background-color .2s ease}.pdp-product-gallery__video-thumbnail:hover .pdp-product-gallery__video-icon,.pdp-product-gallery__video-thumbnail:focus-visible .pdp-product-gallery__video-icon{background-color:#000c}.pdp-product-gallery__video-thumbnail img{width:100%;height:100%;object-fit:cover}.pdp-product{display:grid;grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);margin:0 var(--grid-1-margins)}.pdp-product__gallery-column{display:none}.pdp-product__content-column{grid-column:1 / span 4;color:var(--color-neutral-800)}.pdp-product__header{display:grid;grid-auto-rows:max-content;margin-bottom:var(--spacing-medium)}.pdp-product__breadcrumbs{grid-column:1 / -1;padding:var(--spacing-medium) 0 var(--spacing-xxbig)}.pdp-product__title{font:var(--type-display-3-font);letter-spacing:var(--type-display-3-letter-spacing);margin:0}.pdp-product__sku{font:var(--type-body-1-default-font);max-width:600px}.pdp-product__prices{display:flex;gap:var(--spacing-xsmall);margin-top:var(--spacing-medium)}.pdp-product__out-of-stock__text{font:var(--type-headline-2-strong-font)}.pdp-product__options{grid-column:1 / span 4}.pdp-product__price,.pdp-product__price-special{font:var(--type-headline-2-strong-font)}.pdp-product__price,.pdp-product__sku,.pdp-product__price-special,.pdp-product__out-of-stock__text{letter-spacing:var(--type-headline-2-strong-letter-spacing)}.pdp-product__price--grey{color:var(--color-neutral-500)}.pdp-product__actions,.pdp-product__actions--out-of-stock{display:grid;grid-auto-flow:row;gap:var(--grid-1-gutters);grid-auto-rows:max-content;margin-bottom:var(--spacing-xxbig);margin-top:var(--spacing-big)}.pdp-product__short_description,.pdp-product__attributes{font:var(--type-body-1-strong-font);letter-spacing:var(--type-body-1-strong-letter-spacing);margin-bottom:var(--spacing-small)}.pdp-product__description,.pdp-product__attributes li{font:var(--type-body-1-default-font);letter-spacing:var(--type-body-1-default-letter-spacing)}.pdp-product__attributes{margin-top:var(--spacing-xsmall)}.pdp-product__short_description p,.pdp-product__description p,.pdp-product__attributes li p{margin:0}.pdp-product__short_description figure,.pdp-product__description figure{margin:0}.pdp-product__short_description img,.pdp-product__description img{max-width:100%}.pdp-product__images--carousel{max-height:60vh}.pdp-product__images--carousel--thumbnails{max-height:90vh}.pdp-product__images__placeholder{background-color:var(--color-neutral-200)}.pdp-product__buttons{display:grid;grid-auto-rows:max-content;grid-gap:var(--grid-1-gutters)}.pdp-product__buttons a{text-align:center}.pdp-product__buttons a:hover{text-decoration:none}.pdp-product__overlay .pdp-overlay__content{width:100%;height:100%}.pdp-product__overlay__carousel{width:100%;height:100%;box-sizing:border-box}.pdp-product__overlay__carousel img{width:100%;height:100%;object-fit:contain}.pdp-carousel__controls__container--thumbnailsRow img,.pdp-carousel__controls__container--thumbnailsColumn img{object-fit:cover}.pdp-product__quantity,.pdp-product__buttons{grid-column:1 / span 4}@media only screen and (min-width: 1024px){.pdp-product__breadcrumbs{grid-column:2 / -2}.pdp-product{grid-template-columns:repeat(var(--grid-3-columns),1fr);grid-column-gap:var(--grid-3-gutters);margin:0 var(--grid-3-margins);position:relative}.pdp-product__column-container{position:relative;display:flex;flex-direction:column}.pdp-product__column-body{position:sticky;bottom:30%;align-self:flex-end}.pdp-product__gallery-column{display:block;grid-column:2 / span 6}.pdp-product__content-column{grid-column:8 / span 4;padding-top:var(--spacing-medium)}.pdp-product__images--carousel{display:none}.pdp-product__header{margin-bottom:var(--spacing-small)}.pdp-product__actions{grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);grid-row-gap:var(--spacing-xxbig);margin-top:var(--spacing-xxbig)}.pdp-product__quantity{grid-column:1 / span 2;width:fit-content}.pdp-product__actions--out-of-stock{grid-template-columns:repeat(var(--grid-1-columns),1fr);grid-column-gap:var(--grid-1-gutters);grid-row-gap:var(--spacing-xxbig);margin-top:var(--spacing-small)}.pdp-product__buttons{grid-column:1 / span 4}.pdp-product__out-of-stock__text{grid-column:1 / span 2}.pdp-product__gallery-content,.pdp-product__content{grid-column:2 / span 6}}.pdp-gallery-grid{display:grid;grid-gap:var(--gap);grid-template-columns:repeat(1,1fr);grid-auto-rows:max-content}.pdp-gallery-grid__item>*{height:100%;max-width:100%;min-width:100%;vertical-align:middle;width:100%;object-fit:contain}.pdp-gallery-grid__item:only-child{grid-column:1 / span 2}.pdp-gallery-grid__item:first-child:nth-last-child(2){grid-column:1 / span 2;grid-row:1}.pdp-gallery-grid__item:nth-child(2):last-child{grid-column:1 / 3;grid-row:2}@media only screen and (min-width: 1024px){.pdp-gallery-grid{grid-template-columns:repeat(2,1fr)}}.pdp-price-range .dropin-price{letter-spacing:var(--type-headline-2-strong-letter-spacing);font:var(--type-headline-2-strong-font)}.pdp-price-range__label{font:var(--type-headline-2-strong-font);letter-spacing:var(--type-body-1-strong-letter-spacing);margin-left:5px;margin-right:5px}.pdp-overlay{position:fixed;top:0;left:0;z-index:1000;right:0;bottom:0;background-color:var(--color-neutral-50);display:flex;padding:var(--spacing-medium)}.pdp-overlay--centered{align-items:center;justify-content:center}.pdp-overlay__content{display:flex;height:100%;width:100%}.pdp-overlay__close-button{position:absolute;top:var(--spacing-medium);right:var(--spacing-medium);z-index:1001}.pdp-swatches{display:grid;grid-auto-flow:row;grid-gap:var(--grid-1-gutters);grid-auto-rows:max-content;position:relative;z-index:0}.pdp-swatches__field{display:grid;font:var(--type-body-2-strong-font);grid-auto-rows:max-content;gap:var(--spacing-xsmall)}.pdp-swatches__field__label{font:var(--type-details-caption-1-font);letter-spacing:var(--type-details-caption-1-letter-spacing);color:var(--color-neutral-800)}.pdp-swatches__options{display:flex;flex-wrap:wrap;gap:var(--spacing-xsmall)}.pdp-zoom{cursor:zoom-in;overflow:hidden;margin:0;position:relative}.pdp-zoom img{height:100%;max-width:100%;min-width:100%;object-fit:contain;position:relative;transition:transform .2s ease-out;transform:var(--transform);vertical-align:middle;width:100%;will-change:transform;backface-visibility:hidden;transform-style:preserve-3d}.pdp-zoom .pdp-zoom__close-button{background-color:var(--color-neutral-50);border-radius:50%;position:absolute;right:var(--spacing-small);top:var(--spacing-small);width:10px;height:10px}.pdp-zoom.pdp-zoom--zoomed{cursor:zoom-out}.pdp-zoom.pdp-zoom--no-effects{cursor:default}@media only screen and (max-width: 768px){.pdp-zoom img{display:block;width:100%;height:100%;transition:transform .2s ease-out;transform:var(--transform);will-change:transform;backface-visibility:hidden}}.pdp-header{display:grid;grid-auto-rows:max-content}.pdp-header__title{font:var(--type-display-3-font);letter-spacing:var(--type-display-3-letter-spacing);margin:0}.pdp-header__sku,.pdp-header__variant{font:var(--type-body-1-default-font);max-width:600px}.pdp-header__variant-separator{margin-inline:.25em}.pdp-header__variant-sku{font:var(--type-body-1-default-font)}.pdp-price{display:flex;flex-wrap:wrap;gap:var(--spacing-xsmall);margin-top:var(--spacing-medium)}.pdp-price__amount,.pdp-price__amount-special{font:var(--type-headline-2-strong-font);letter-spacing:var(--type-headline-2-strong-letter-spacing)}.pdp-price__amount--grey{color:var(--color-neutral-500)}.pdp-price__tiers{flex:0 0 100%}.pdp-price-tiers{display:flex;flex-direction:column;gap:8px;margin-top:8px}.pdp-price-tiers__tier{font:var(--type-body-2-font);color:var(--color-text-primary);line-height:1.4}.pdp-price-tiers__text{display:inline}.pdp-price-tiers__price{font-weight:600;color:var(--color-text-primary)}.pdp-price-tiers__savings{font-weight:600;color:var(--color-success)}
4
5
  .pdp-gift-card-options{display:flex;flex-direction:column;gap:var(--spacing-xsmall)}.pdp-gift-card-options__grid{display:flex;flex-direction:column;gap:var(--spacing-small)}.pdp-gift-card-options__row{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--spacing-small)}.pdp-product__gift-card-options-container{grid-column:1 / -1}.pdp-gift-card-options__option-field{min-width:0;box-sizing:border-box}.pdp-gift-card-options__option-field--full-width{grid-column:1 / -1}.pdp-gift-card-options__label{font-weight:700;margin-bottom:var(--spacing-xsmall)}
5
6
  .pdp-downloadable-options{display:grid;grid-auto-flow:row;grid-gap:var(--grid-1-gutters);grid-auto-rows:max-content;position:relative;z-index:0}.pdp-downloadable-options__field{display:grid;font:var(--type-body-2-strong-font);grid-auto-rows:max-content;gap:var(--spacing-small)}.pdp-downloadable-options__field__label{font:var(--type-details-caption-1-font);letter-spacing:var(--type-details-caption-1-letter-spacing);color:var(--color-neutral-800);margin-bottom:var(--spacing-xxsmall)}.pdp-downloadable-options__list{display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:var(--spacing-small)}.pdp-downloadable-options__item{position:relative;min-width:0;display:flex;flex-direction:row;align-items:stretch}.pdp-downloadable-options__item--disabled{cursor:not-allowed;opacity:.6}.pdp-downloadable-options__item--disabled .pdp-downloadable-options__clickable{cursor:not-allowed}.pdp-downloadable-options__item--disabled:hover{border-color:var(--color-neutral-300);background-color:var(--color-neutral-50)}.pdp-downloadable-options__item--sample{cursor:default}.pdp-downloadable-options__item--sample:hover{border-color:var(--color-neutral-300);background-color:var(--color-neutral-50)}.pdp-downloadable-options__item--sample .pdp-downloadable-options__clickable{cursor:default;pointer-events:none}.pdp-downloadable-options__clickable{padding:var(--spacing-xsmall);outline:none;border-radius:var(--shape-border-radius-2) 0 0 var(--shape-border-radius-2);border:2px solid var(--color-neutral-300);background-color:var(--color-neutral-50);font:inherit;color:inherit;text-align:left;width:100%;cursor:pointer;display:flex;flex-direction:column;flex:1;transition:all .15s ease}.pdp-downloadable-options__clickable:hover:not(:disabled){border-color:var(--color-neutral-500);background-color:var(--color-neutral-100)}.pdp-downloadable-options__item--selected .pdp-downloadable-options__clickable{border-color:var(--color-brand-500);background-color:var(--color-brand-50)}.pdp-downloadable-options__item--selected .pdp-downloadable-options__clickable:hover:not(:disabled){border-color:var(--color-brand-600);background-color:var(--color-brand-100)}.pdp-downloadable-options__clickable:focus-visible{outline:2px solid var(--color-brand-500);outline-offset:2px}.pdp-downloadable-options__item:has(.pdp-downloadable-options__clickable:focus-visible){outline:2px solid var(--color-brand-500);outline-offset:2px}.pdp-downloadable-options__content{display:flex;flex-direction:column;gap:var(--spacing-xxsmall)}.pdp-downloadable-options__header{width:100%;display:flex;justify-content:space-between;align-items:baseline;gap:var(--spacing-small)}.pdp-downloadable-options__title{font:var(--type-body-2-font);color:var(--color-neutral-900);line-height:1.4;flex:1;min-width:0}.pdp-downloadable-options__sample-button{padding:var(--spacing-xsmall);font:var(--type-details-caption-2-font);color:var(--color-brand-600);background-color:var(--color-neutral-50);border:2px solid var(--color-neutral-300);border-left:none;border-radius:0 var(--shape-border-radius-2) var(--shape-border-radius-2) 0;cursor:pointer;flex-shrink:0;white-space:nowrap;text-decoration:none;display:flex;align-items:center}.pdp-downloadable-options__price{font:var(--type-body-2-strong-font);color:var(--color-neutral-900);white-space:nowrap;flex-shrink:0}.pdp-downloadable-options__sample{display:inline-block;padding:0 var(--spacing-xsmall) var(--spacing-xsmall) var(--spacing-xsmall);font:var(--type-details-caption-2-font);color:var(--color-brand-600);text-decoration:underline;text-underline-offset:2px;transition:color .15s ease;cursor:pointer}.pdp-downloadable-options__sample:hover{color:var(--color-brand-700);text-decoration-thickness:2px}`,{styleId:"PDP"});
6
7
  import{jsx as o}from"@dropins/tools/preact-jsx-runtime.js";import{deepmerge as m,Render as u}from"@dropins/tools/lib.js";import{useState as c,useEffect as b}from"@dropins/tools/preact-hooks.js";import{UIProvider as d}from"@dropins/tools/components.js";import{events as f}from"@dropins/tools/event-bus.js";import{c as p}from"./chunks/fetchProductData.js";import"./fragments.js";import"@dropins/tools/fetch-graphql.js";import"./chunks/getProductConfigurationValues.js";import"./chunks/isProductConfigurationValid.js";const g={Product:{Incrementer:{label:"Item Quantity"},OutOfStock:{label:"Out of Stock"},AddToCart:{label:"Add to Cart"},Details:{label:"Details"},RegularPrice:{label:"Regular Price"},SpecialPrice:{label:"Special Price"},TierPrice:{label:"Tier Price"},TierPricing:{label:"Buy {quantity} for {price} each and save {percent}%"},Image:{label:"{product} Image {key} of {total}"},GiftCardOptions:{ChooseAmount:{label:"Choose amount"},OtherAmount:{label:"Other amount"},RequiredFieldError:{label:"This field is required"},NumberError:{label:"Please enter a valid number"},MinError:{label:"Minimum value is {min}"},MaxError:{label:"Maximum value is {max}"},MinLengthError:{label:"Minimum length is {min_length} characters"},MaxLengthError:{label:"Maximum length is {max_length} characters"},EmailError:{label:"Please enter a valid email address"},InvalidValueError:{label:"Please select one of the allowed values: {values}"}}},Swatches:{Required:{label:"Required"},ChooseOption:{label:"Choose an option"}},DownloadableOptions:{Sample:{label:"Sample"},ViewSample:{label:"View sample for {label}"}},Carousel:{label:"Carousel",Next:{label:"Next"},Previous:{label:"Previous"},Slide:{label:"Slide"},Controls:{label:"Carousel Controls",Button:{label:"Show slide {key} of {total}"}}},Overlay:{Close:{label:"Close"}},Zoom:{Close:{label:"Close"}}},h={PDP:g},P={default:h},C=({children:i})=>{var r;const[e,t]=c(),n=(r=p.getConfig())==null?void 0:r.langDefinitions;b(()=>{const l=f.on("locale",a=>{a!==e&&t(a)},{eager:!0});return()=>{l==null||l.off()}},[e]);const s=m(P,n??{});return o(d,{lang:e,langDefinitions:s,children:i})},q=new u(o(C,{}));export{q as render};