@dropins/storefront-pdp 3.2.0 → 3.3.0-beta.1

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.
@@ -1 +1 @@
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 stale state updates when the URL list changes; the underlying\n * network requests continue in the background and are left to the browser.\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 // \\0 cannot appear in a URL, so this avoids collisions with URLs that\n // contain spaces or other delimiter-like characters.\n const key = urls.join('\\0');\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\n// Total time we hold the pending layer before promoting it. Must exceed the\n// CSS transition duration in ProductGallery.css (currently 180ms). If you\n// change one, update the other.\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 // Mirror collectPreloadUrls: a video preview thumbnail change should\n // also trigger the buffered swap, not the fast path.\n const aPreview = getVideoPreviewUrl(\n aVideos[i].url,\n aVideos[i].preview?.url\n );\n const bPreview = getVideoPreviewUrl(\n bVideos[i].url,\n bVideos[i].preview?.url\n );\n if (aPreview !== bPreview) 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 // videosRef avoids re-subscribing the event listener when a consumer passes\n // an inline object for `videos` (a fresh reference on every parent render).\n const videosRef = useRef(videos);\n videosRef.current = videos;\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, videosRef.current)) {\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]);\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","aPreview","bPreview","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","videosRef","dataEvent","events","newData","pendingUrls","pendingReady","promote","raf","timer","pendingContent","stackClassName","pendingLayerClassName","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 stale state updates when the URL list changes; the underlying\n * network requests continue in the background and are left to the browser.\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 // \\0 cannot appear in a URL, so this avoids collisions with URLs that\n // contain spaces or other delimiter-like characters.\n const key = urls.join('\\0');\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\n// Total time we hold the pending layer before promoting it. Must exceed the\n// CSS transition duration in ProductGallery.css (currently 180ms). If you\n// change one, update the other.\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 // Mirror collectPreloadUrls: a video preview thumbnail change should\n // also trigger the buffered swap, not the fast path.\n const aPreview = getVideoPreviewUrl(\n aVideos[i].url,\n aVideos[i].preview?.url\n );\n const bPreview = getVideoPreviewUrl(\n bVideos[i].url,\n bVideos[i].preview?.url\n );\n if (aPreview !== bPreview) 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 // When the image opens the preview overlay (no inline `zoom`), the\n // main image acts as a control. It must be keyboard-focusable and\n // operable with Enter/Space, not just clickable (WCAG 2.1.1).\n // `onZoom` is only provided for the displayed (interactive) layer;\n // the pending crossfade layer stays non-interactive and unfocusable.\n const previewProps = onZoom\n ? {\n onClick: () => onZoom(key),\n onKeyDown: (event: KeyboardEvent) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n onZoom(key);\n }\n },\n role: 'button',\n tabIndex: 0,\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} {...previewProps} />\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 // videosRef avoids re-subscribing the event listener when a consumer passes\n // an inline object for `videos` (a fresh reference on every parent render).\n const videosRef = useRef(videos);\n videosRef.current = videos;\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, videosRef.current)) {\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]);\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","aPreview","bPreview","prefersReducedMotion","useGalleryContent","imageParams","thumbnailParams","zoom","slots","ImagesLabel","onZoom","defaultSlotContext","useMemo","mediaItems","galleryItems","item","Item","itemProps","defaultProps","jsx","Slot","jsxs","Image","previewProps","event","Zoom","thumbnails","ProductGallery","initialData","scope","controls","loop","peak","gap","arrows","arrowsOnMainImage","children","props","useText","displayedData","setDisplayedData","pendingData","setPendingData","entering","setEntering","zoomTo","setZoomTo","displayedRef","useRef","videosRef","dataEvent","events","newData","pendingUrls","pendingReady","promote","raf","timer","pendingContent","stackClassName","pendingLayerClassName","Carousel","Overlay","getFetchedProductData"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  /*! Copyright 2026 Adobe
2
2
  All Rights Reserved. */
3
- import{jsx as I}from"@dropins/tools/preact-jsx-runtime.js";import{useState as D,useMemo as G,useEffect as H,useCallback as C}from"@dropins/tools/preact-compat.js";import{Slot as j}from"@dropins/tools/lib.js";import{events as U}from"@dropins/tools/event-bus.js";import"@dropins/tools/components.js";import{S as J}from"../chunks/components.js";import"@dropins/tools/preact.js";import"@dropins/tools/preact-hooks.js";import{config as k,s as K,a as Q,getProductConfigurationValues as y,setProductConfigurationValues as B,fetchProductData as V,i as T,setProductConfigurationValid as v,p as W,getFetchedProductData as X}from"../api.js";import{useText as Y}from"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const Z=({initialData:n={data:null,optionsUIDs:[]},scope:i,hideSelectedValue:g=!1,transformer:u,slots:s,onValues:f,onErrors:l,children:_,...M})=>{var S;const[t,F]=D(n.data),[d,L]=D(n.optionsUIDs),[z,N]=D(),P=Y("PDP.Swatches.ChooseOption.label").label,m=G(()=>{if(t!=null&&t.options){if(!u)return t.options;try{return u(t.options)??t.options}catch(o){return console.error("ProductOptions: transformer threw an error, using original options",o),t.options}}},[t==null?void 0:t.options,u]);H(()=>{const o=U.on("pdp/data",F,{scope:i}),c=U.on("pdp/values",({optionsUIDs:a})=>{a&&L(a)},{scope:i});return()=>{o==null||o.off(),c==null||c.off()}},[]);const R=C(async o=>{var w,b;if(!(t!=null&&t.sku))return;const{persistURLParams:c,anchors:a,acdl:A}=k.getConfig()??{},E=Object.fromEntries(Object.entries(o).filter(([,p])=>p.label!==P));let r=K(E,m);c&&!(t!=null&&t.isBundle)&&Q({optionsUIDs:r.join(",")}),((b=(w=y({scope:i}))==null?void 0:w.optionsUIDs)==null?void 0:b.join("|"))!==(r==null?void 0:r.join("|"))&&B(p=>({...p,optionsUIDs:r}),{scope:i}),f==null||f(r);const e=await V(t.sku,{optionsUIDs:r??[],isBundle:t==null?void 0:t.isBundle,anchors:a});U.emit("pdp/data",e,{scope:i});let O=T(e==null?void 0:e.options,(e==null?void 0:e.optionUIDs)??r,e==null?void 0:e.isBundle);if(a!=null&&a.length){const p=(e==null?void 0:e.optionUIDs)??[];r.every(h=>p.includes(h))||(N(e==null?void 0:e.options),B(h=>({...h,optionsUIDs:p}),{scope:i}),O=T(e==null?void 0:e.options,p,e==null?void 0:e.isBundle))}v(()=>O,{scope:i}),A&&e&&(e==null?void 0:e.variantSku)!==t.sku&&W(e)},[P,t==null?void 0:t.isBundle,t==null?void 0:t.sku,f,i,m]),q=C(o=>{Object.keys(o).length!==0&&(v(()=>!1,{scope:i}),l==null||l(o))},[l,i]);return!t||!m?null:I(j,{name:"Swatches",slot:s==null?void 0:s.Swatches,context:{data:t,optionsUIDs:d},children:I(J,{...M,options:m,selectedUIDs:d,defaultOptions:d,selectionsToUpdate:z,hideSelectedValue:g,disablePreselections:!((S=k.getConfig())!=null&&S.preselectFirstOption),onValues:R,onErrors:q,imageSwatchNode:s!=null&&s.SwatchImage?({imageSwatchContext:o,...c})=>I(j,{name:"SwatchImage",slot:s==null?void 0:s.SwatchImage,slotTag:"span",contentTag:"span",context:{data:t,optionsUIDs:d,defaultImageProps:c,imageSwatchContext:o}}):void 0})})};Z.getInitialData=async function(n){var u;const i=await X({scope:n==null?void 0:n.scope}),g=((u=y({scope:n==null?void 0:n.scope}))==null?void 0:u.optionsUIDs)??[];return{data:i,optionsUIDs:g}};export{Z as ProductOptions,Z as default};
3
+ import{jsx as S}from"@dropins/tools/preact-jsx-runtime.js";import{useState as I,useMemo as Y,useEffect as Z,useCallback as B}from"@dropins/tools/preact-compat.js";import{Slot as T}from"@dropins/tools/lib.js";import{events as C}from"@dropins/tools/event-bus.js";import"@dropins/tools/components.js";import{S as _}from"../chunks/components.js";import"@dropins/tools/preact.js";import"@dropins/tools/preact-hooks.js";import{config as M,s as $,c as V,getProductConfigurationValues as L,setProductConfigurationValues as U,fetchProductData as x,i as v,setProductConfigurationValid as E,p as tt,a as F,m as nt,getFetchedProductData as it}from"../api.js";import{useText as et}from"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const ot=({initialData:c={data:null,optionsUIDs:[]},scope:e,hideSelectedValue:b=!1,transformer:d,slots:a,onValues:m,onErrors:g,children:st,...z})=>{var w;const[t,N]=I(c.data),[h,R]=I(c.optionsUIDs),[q,A]=I({}),[G,H]=I(),Q=et("PDP.Swatches.ChooseOption.label").label,p=Y(()=>{if(t!=null&&t.options){if(!d)return t.options;try{return d(t.options)??t.options}catch(i){return console.error("ProductOptions: transformer threw an error, using original options",i),t.options}}},[t==null?void 0:t.options,d]);Z(()=>{const i=C.on("pdp/data",N,{scope:e}),u=C.on("pdp/values",({optionsUIDs:o,bundleOptionQuantities:s})=>{o&&R(o),s&&A(s)},{scope:e});return()=>{i==null||i.off(),u==null||u.off()}},[]);const J=B(async i=>{var k;if(!(t!=null&&t.sku))return;const{persistURLParams:u,anchors:o,acdl:s}=M.getConfig()??{},O=Object.fromEntries(Object.entries(i).filter(([,r])=>r.label!==Q));let l=$(O,p);u&&!(t!=null&&t.isBundle)&&V({optionsUIDs:l.join(",")});const j=((k=L({scope:e}))==null?void 0:k.optionsUIDs)??[];j.join("|")!==l.join("|")&&!(t!=null&&t.isBundle)&&U(r=>({...r,optionsUIDs:l}),{scope:e}),m==null||m(l);const n=await x(t.sku,{optionsUIDs:l??[],isBundle:t==null?void 0:t.isBundle,anchors:o});C.emit("pdp/data",n,{scope:e});const X=r=>{!(t!=null&&t.isBundle)||!(n!=null&&n.options)||U(P=>{const f=nt(P.bundleOptionQuantities,n.options,r);return{...P,optionsUIDs:r,bundleOptionQuantities:f,enteredOptions:F(f)}},{scope:e})};let D=(n==null?void 0:n.optionUIDs)??l,y=v(n==null?void 0:n.options,D,n==null?void 0:n.isBundle);if(o!=null&&o.length){const r=(n==null?void 0:n.optionUIDs)??[];l.every(f=>r.includes(f))||(H(n==null?void 0:n.options),D=r,t!=null&&t.isBundle||U(f=>({...f,optionsUIDs:r}),{scope:e}),y=v(n==null?void 0:n.options,r,n==null?void 0:n.isBundle))}j.join("|")!==D.join("|")&&X(D),E(()=>y,{scope:e}),s&&n&&(n==null?void 0:n.variantSku)!==t.sku&&tt(n)},[Q,t==null?void 0:t.isBundle,t==null?void 0:t.sku,m,e,p]),K=B((i,u)=>{const o=p==null?void 0:p.flatMap(s=>s.items).find(s=>s.id===i);o!=null&&o.canEditQuantity&&U(s=>{const O={...s.bundleOptionQuantities??{},[i]:u};return{...s,bundleOptionQuantities:O,enteredOptions:F(O)}},{scope:e})},[e,p]),W=B(i=>{Object.keys(i).length!==0&&(E(()=>!1,{scope:e}),g==null||g(i))},[g,e]);return!t||!p?null:S(T,{name:"Swatches",slot:a==null?void 0:a.Swatches,context:{data:t,optionsUIDs:h},children:S(_,{...z,options:p,selectedUIDs:h,defaultOptions:h,selectionsToUpdate:G,hideSelectedValue:b,disablePreselections:!((w=M.getConfig())!=null&&w.preselectFirstOption),onValues:J,onErrors:W,bundleOptionQuantities:q,onBundleOptionQuantityChange:K,imageSwatchNode:a!=null&&a.SwatchImage?({imageSwatchContext:i,...u})=>S(T,{name:"SwatchImage",slot:a==null?void 0:a.SwatchImage,slotTag:"span",contentTag:"span",context:{data:t,optionsUIDs:h,defaultImageProps:u,imageSwatchContext:i}}):void 0})})};ot.getInitialData=async function(c){var d;const e=await it({scope:c==null?void 0:c.scope}),b=((d=L({scope:c==null?void 0:c.scope}))==null?void 0:d.optionsUIDs)??[];return{data:e,optionsUIDs:b}};export{ot as ProductOptions,ot as default};
4
4
  //# sourceMappingURL=ProductOptions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductOptions.js","sources":["/@dropins/storefront-pdp/src/containers/ProductOptions/ProductOptions.tsx"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2024 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 {\n HTMLAttributes,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'preact/compat';\nimport { Container, Slot, SlotProps } from '@adobe-commerce/elsie/lib';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { ProductModel } from '@/pdp/data/models';\nimport { events } from '@adobe-commerce/event-bus';\nimport { Swatches } from '@/pdp/components';\nimport {\n setProductConfigurationValues,\n getProductConfigurationValues,\n setProductConfigurationValid,\n fetchProductData,\n getFetchedProductData,\n config,\n OptionsTransformer,\n} from '@/pdp/api';\nimport { setUrlParams } from '@/pdp/lib/url-params';\nimport { publishProductPageViewEvent } from '@/pdp/lib/acdl';\nimport {\n ImageNodeRenderProps,\n ImageProps,\n} from '@adobe-commerce/elsie/components';\nimport {\n type ProductOptionSelectionMap,\n isProductOptionsSelectionComplete,\n selectionMapToOptionUIDs,\n} from '@/pdp/lib/product-option-selection';\n\n/** @deprecated Use OptionsTransformer from @/pdp/api instead */\nexport type ProductOptionsTransformer = OptionsTransformer;\n\nexport interface ProductOptionsProps extends HTMLAttributes<HTMLDivElement> {\n scope?: string;\n hideSelectedValue?: boolean;\n onValues?: (optionsUIDs: string[]) => void;\n onErrors?: (errors: { [id: string]: string }) => void;\n transformer?: ProductOptionsTransformer;\n slots?: {\n Swatches?: SlotProps<{ data: ProductModel | null; optionsUIDs: string[] }>;\n SwatchImage?: SlotProps<{\n data: ProductModel | null;\n optionsUIDs: string[];\n imageSwatchContext: ImageNodeRenderProps['imageSwatchContext'];\n defaultImageProps: ImageProps;\n }>;\n };\n}\n\nexport const ProductOptions: Container<\n ProductOptionsProps,\n { data: ProductModel | null; optionsUIDs: string[] }\n> = ({\n initialData = { data: null, optionsUIDs: [] },\n scope,\n hideSelectedValue = false,\n transformer,\n slots,\n onValues,\n onErrors,\n children,\n ...props\n}) => {\n const [data, setData] = useState<ProductModel | null>(initialData.data);\n\n const [current, setCurrent] = useState<string[]>(initialData.optionsUIDs);\n\n const [selectionsToUpdate, setSelectionsToUpdate] =\n useState<ProductModel['options']>();\n\n const ChooseOptionLabel = useText('PDP.Swatches.ChooseOption.label').label;\n\n // Apply transformer to options if provided (must run before selection handlers)\n const transformedOptions = useMemo(() => {\n if (!data?.options) return undefined;\n\n if (!transformer) {\n return data.options;\n }\n\n try {\n const transformed = transformer(data.options);\n // Fallback to original options if transformer returns null/undefined\n return transformed ?? data.options;\n } catch (error) {\n console.error(\n 'ProductOptions: transformer threw an error, using original options',\n error\n );\n return data.options;\n }\n }, [data?.options, transformer]);\n\n useEffect(() => {\n const dataEvent = events.on('pdp/data', setData, { scope });\n\n const valuesEvent = events.on(\n 'pdp/values',\n ({ optionsUIDs }) => {\n if (optionsUIDs) {\n setCurrent(optionsUIDs);\n }\n },\n { scope }\n );\n\n return () => {\n dataEvent?.off();\n valuesEvent?.off();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleOptionSelected = useCallback(\n async (selections: ProductOptionSelectionMap) => {\n if (!data?.sku) return;\n\n const { persistURLParams, anchors, acdl } = config.getConfig() ?? {};\n\n const sanitizedSelections = Object.fromEntries(\n Object.entries(selections).filter(\n ([, value]) => value.label !== ChooseOptionLabel\n )\n ) as ProductOptionSelectionMap;\n\n // options selected\n let optionsUIDs = selectionMapToOptionUIDs(\n sanitizedSelections,\n transformedOptions\n );\n\n // set optionUIDs to URL params\n // ?optionsUIDs=1,2,3\n // only set the URL params if the \"persistURLParams\" is set to true and the product is not a bundle type\n if (persistURLParams && !data?.isBundle) {\n setUrlParams({ optionsUIDs: optionsUIDs.join(',') });\n }\n\n // check if the options have changed and update the values only if they have changed\n if (\n getProductConfigurationValues({ scope })?.optionsUIDs?.join('|') !==\n optionsUIDs?.join('|')\n ) {\n // update values\n setProductConfigurationValues((prev) => ({ ...prev, optionsUIDs }), {\n scope,\n });\n }\n\n // pass the selected options to callback\n onValues?.(optionsUIDs);\n\n // refetch data\n const next = await fetchProductData(data.sku, {\n optionsUIDs: optionsUIDs ?? [],\n isBundle: data?.isBundle,\n anchors,\n });\n\n // emit the new data\n events.emit('pdp/data', next, { scope });\n\n let valid = isProductOptionsSelectionComplete(\n next?.options,\n next?.optionUIDs ?? optionsUIDs,\n next?.isBundle\n );\n\n // special processing for anchor options (options that should not be refined when other options are selected)\n if (anchors?.length) {\n const updatedOptionsUIDs = next?.optionUIDs ?? [];\n\n const isMatching = optionsUIDs.every((item) =>\n updatedOptionsUIDs.includes(item)\n );\n\n if (!isMatching) {\n setSelectionsToUpdate(next?.options);\n setProductConfigurationValues(\n (prev) => ({\n ...prev,\n optionsUIDs: updatedOptionsUIDs,\n }),\n { scope }\n );\n\n valid = isProductOptionsSelectionComplete(\n next?.options,\n updatedOptionsUIDs,\n next?.isBundle\n );\n }\n }\n\n // is the product valid\n setProductConfigurationValid(() => valid, { scope });\n\n // ACDL\n if (acdl && next && next?.variantSku !== data.sku) {\n publishProductPageViewEvent(next);\n }\n },\n [\n ChooseOptionLabel,\n data?.isBundle,\n data?.sku,\n onValues,\n scope,\n transformedOptions,\n ]\n );\n\n const handleErrors = useCallback(\n (errors: { [id: string]: string }) => {\n const hasErrors = Object.keys(errors).length !== 0;\n\n if (hasErrors) {\n setProductConfigurationValid(() => false, { scope });\n onErrors?.(errors);\n }\n },\n [onErrors, scope]\n );\n\n if (!data) return null;\n\n // Only show if there are options in the data\n if (!transformedOptions) return null;\n\n return (\n <Slot\n name=\"Swatches\"\n slot={slots?.Swatches}\n context={{ data, optionsUIDs: current }}\n >\n <Swatches\n {...props}\n options={transformedOptions}\n selectedUIDs={current}\n defaultOptions={current}\n selectionsToUpdate={selectionsToUpdate}\n hideSelectedValue={hideSelectedValue}\n disablePreselections={!config.getConfig()?.preselectFirstOption}\n onValues={handleOptionSelected}\n onErrors={handleErrors}\n imageSwatchNode={\n slots?.SwatchImage\n ? ({ imageSwatchContext, ...imageProps }) => (\n <Slot\n name=\"SwatchImage\"\n slot={slots?.SwatchImage}\n slotTag=\"span\"\n contentTag=\"span\"\n context={{\n data,\n optionsUIDs: current,\n defaultImageProps: imageProps,\n imageSwatchContext,\n }}\n />\n )\n : undefined\n }\n />\n </Slot>\n );\n};\n\nProductOptions.getInitialData = async function (options) {\n const data = await getFetchedProductData({ scope: options?.scope });\n const optionsUIDs =\n getProductConfigurationValues({ scope: options?.scope })?.optionsUIDs ?? [];\n return { data, optionsUIDs };\n};\n"],"names":["ProductOptions","initialData","scope","hideSelectedValue","transformer","slots","onValues","onErrors","children","props","data","setData","useState","current","setCurrent","selectionsToUpdate","setSelectionsToUpdate","ChooseOptionLabel","useText","transformedOptions","useMemo","error","useEffect","dataEvent","events","valuesEvent","optionsUIDs","handleOptionSelected","useCallback","selections","persistURLParams","anchors","acdl","config","sanitizedSelections","value","selectionMapToOptionUIDs","setUrlParams","_b","_a","getProductConfigurationValues","setProductConfigurationValues","prev","next","fetchProductData","valid","isProductOptionsSelectionComplete","updatedOptionsUIDs","item","setProductConfigurationValid","publishProductPageViewEvent","handleErrors","errors","jsx","Slot","Swatches","imageSwatchContext","imageProps","options","getFetchedProductData"],"mappings":"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"}
1
+ {"version":3,"file":"ProductOptions.js","sources":["/@dropins/storefront-pdp/src/containers/ProductOptions/ProductOptions.tsx"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2024 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 {\n HTMLAttributes,\n useCallback,\n useEffect,\n useMemo,\n useState,\n} from 'preact/compat';\nimport { Container, Slot, SlotProps } from '@adobe-commerce/elsie/lib';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { ProductModel } from '@/pdp/data/models';\nimport { events } from '@adobe-commerce/event-bus';\nimport { Swatches } from '@/pdp/components';\nimport {\n setProductConfigurationValues,\n getProductConfigurationValues,\n setProductConfigurationValid,\n fetchProductData,\n getFetchedProductData,\n config,\n OptionsTransformer,\n} from '@/pdp/api';\nimport { setUrlParams } from '@/pdp/lib/url-params';\nimport { publishProductPageViewEvent } from '@/pdp/lib/acdl';\nimport {\n ImageNodeRenderProps,\n ImageProps,\n} from '@adobe-commerce/elsie/components';\nimport {\n type ProductOptionSelectionMap,\n isProductOptionsSelectionComplete,\n selectionMapToOptionUIDs,\n} from '@/pdp/lib/product-option-selection';\nimport {\n bundleOptionQuantitiesToEnteredOptions,\n mergeBundleOptionQuantities,\n} from '@/pdp/lib/bundle-option-quantities';\n\n/** @deprecated Use OptionsTransformer from @/pdp/api instead */\nexport type ProductOptionsTransformer = OptionsTransformer;\n\nexport interface ProductOptionsProps extends HTMLAttributes<HTMLDivElement> {\n scope?: string;\n hideSelectedValue?: boolean;\n onValues?: (optionsUIDs: string[]) => void;\n onErrors?: (errors: { [id: string]: string }) => void;\n transformer?: ProductOptionsTransformer;\n slots?: {\n Swatches?: SlotProps<{ data: ProductModel | null; optionsUIDs: string[] }>;\n SwatchImage?: SlotProps<{\n data: ProductModel | null;\n optionsUIDs: string[];\n imageSwatchContext: ImageNodeRenderProps['imageSwatchContext'];\n defaultImageProps: ImageProps;\n }>;\n };\n}\n\nexport const ProductOptions: Container<\n ProductOptionsProps,\n { data: ProductModel | null; optionsUIDs: string[] }\n> = ({\n initialData = { data: null, optionsUIDs: [] },\n scope,\n hideSelectedValue = false,\n transformer,\n slots,\n onValues,\n onErrors,\n children,\n ...props\n}) => {\n const [data, setData] = useState<ProductModel | null>(initialData.data);\n\n const [current, setCurrent] = useState<string[]>(initialData.optionsUIDs);\n const [bundleOptionQuantities, setBundleOptionQuantities] = useState<\n Record<string, number>\n >({});\n\n const [selectionsToUpdate, setSelectionsToUpdate] =\n useState<ProductModel['options']>();\n\n const ChooseOptionLabel = useText('PDP.Swatches.ChooseOption.label').label;\n\n // Apply transformer to options if provided (must run before selection handlers)\n const transformedOptions = useMemo(() => {\n if (!data?.options) return undefined;\n\n if (!transformer) {\n return data.options;\n }\n\n try {\n const transformed = transformer(data.options);\n // Fallback to original options if transformer returns null/undefined\n return transformed ?? data.options;\n } catch (error) {\n console.error(\n 'ProductOptions: transformer threw an error, using original options',\n error\n );\n return data.options;\n }\n }, [data?.options, transformer]);\n\n useEffect(() => {\n const dataEvent = events.on('pdp/data', setData, { scope });\n\n const valuesEvent = events.on(\n 'pdp/values',\n ({ optionsUIDs, bundleOptionQuantities }) => {\n if (optionsUIDs) {\n setCurrent(optionsUIDs);\n }\n if (bundleOptionQuantities) {\n setBundleOptionQuantities(bundleOptionQuantities);\n }\n },\n { scope }\n );\n\n return () => {\n dataEvent?.off();\n valuesEvent?.off();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n const handleOptionSelected = useCallback(\n async (selections: ProductOptionSelectionMap) => {\n if (!data?.sku) return;\n\n const { persistURLParams, anchors, acdl } = config.getConfig() ?? {};\n\n const sanitizedSelections = Object.fromEntries(\n Object.entries(selections).filter(\n ([, value]) => value.label !== ChooseOptionLabel\n )\n ) as ProductOptionSelectionMap;\n\n // options selected\n let optionsUIDs = selectionMapToOptionUIDs(\n sanitizedSelections,\n transformedOptions\n );\n\n // set optionUIDs to URL params\n // ?optionsUIDs=1,2,3\n // only set the URL params if the \"persistURLParams\" is set to true and the product is not a bundle type\n if (persistURLParams && !data?.isBundle) {\n setUrlParams({ optionsUIDs: optionsUIDs.join(',') });\n }\n\n const storedOptionsUIDs =\n getProductConfigurationValues({ scope })?.optionsUIDs ?? [];\n\n const optionsChanged =\n storedOptionsUIDs.join('|') !== optionsUIDs.join('|');\n\n // check if the options have changed and update the values only if they have changed\n if (optionsChanged && !data?.isBundle) {\n setProductConfigurationValues((prev) => ({ ...prev, optionsUIDs }), {\n scope,\n });\n }\n\n // pass the selected options to callback\n onValues?.(optionsUIDs);\n\n // refetch data\n const next = await fetchProductData(data.sku, {\n optionsUIDs: optionsUIDs ?? [],\n isBundle: data?.isBundle,\n anchors,\n });\n\n // emit the new data\n events.emit('pdp/data', next, { scope });\n\n const syncBundleOptionQuantities = (finalOptionsUIDs: string[]) => {\n if (!data?.isBundle || !next?.options) {\n return;\n }\n\n setProductConfigurationValues(\n (prev) => {\n const bundleOptionQuantities = mergeBundleOptionQuantities(\n prev.bundleOptionQuantities,\n next.options,\n finalOptionsUIDs\n );\n\n return {\n ...prev,\n optionsUIDs: finalOptionsUIDs,\n bundleOptionQuantities,\n enteredOptions: bundleOptionQuantitiesToEnteredOptions(\n bundleOptionQuantities\n ),\n };\n },\n { scope }\n );\n };\n\n let finalOptionsUIDs = next?.optionUIDs ?? optionsUIDs;\n\n let valid = isProductOptionsSelectionComplete(\n next?.options,\n finalOptionsUIDs,\n next?.isBundle\n );\n\n // special processing for anchor options (options that should not be refined when other options are selected)\n if (anchors?.length) {\n const updatedOptionsUIDs = next?.optionUIDs ?? [];\n\n const isMatching = optionsUIDs.every((item) =>\n updatedOptionsUIDs.includes(item)\n );\n\n if (!isMatching) {\n setSelectionsToUpdate(next?.options);\n finalOptionsUIDs = updatedOptionsUIDs;\n\n if (!data?.isBundle) {\n setProductConfigurationValues(\n (prev) => ({\n ...prev,\n optionsUIDs: updatedOptionsUIDs,\n }),\n { scope }\n );\n }\n\n valid = isProductOptionsSelectionComplete(\n next?.options,\n updatedOptionsUIDs,\n next?.isBundle\n );\n }\n }\n\n if (storedOptionsUIDs.join('|') !== finalOptionsUIDs.join('|')) {\n syncBundleOptionQuantities(finalOptionsUIDs);\n }\n\n // is the product valid\n setProductConfigurationValid(() => valid, { scope });\n\n // ACDL\n if (acdl && next && next?.variantSku !== data.sku) {\n publishProductPageViewEvent(next);\n }\n },\n [\n ChooseOptionLabel,\n data?.isBundle,\n data?.sku,\n onValues,\n scope,\n transformedOptions,\n ]\n );\n\n const handleBundleOptionQuantityChange = useCallback(\n (optionValueId: string, quantity: number) => {\n const optionValue = transformedOptions\n ?.flatMap((option) => option.items)\n .find((item) => item.id === optionValueId);\n\n if (!optionValue?.canEditQuantity) {\n return;\n }\n\n setProductConfigurationValues(\n (prev) => {\n const bundleOptionQuantities = {\n ...(prev.bundleOptionQuantities ?? {}),\n [optionValueId]: quantity,\n };\n return {\n ...prev,\n bundleOptionQuantities,\n enteredOptions: bundleOptionQuantitiesToEnteredOptions(\n bundleOptionQuantities\n ),\n };\n },\n { scope }\n );\n },\n [scope, transformedOptions]\n );\n\n const handleErrors = useCallback(\n (errors: { [id: string]: string }) => {\n const hasErrors = Object.keys(errors).length !== 0;\n\n if (hasErrors) {\n setProductConfigurationValid(() => false, { scope });\n onErrors?.(errors);\n }\n },\n [onErrors, scope]\n );\n\n if (!data) return null;\n\n // Only show if there are options in the data\n if (!transformedOptions) return null;\n\n return (\n <Slot\n name=\"Swatches\"\n slot={slots?.Swatches}\n context={{ data, optionsUIDs: current }}\n >\n <Swatches\n {...props}\n options={transformedOptions}\n selectedUIDs={current}\n defaultOptions={current}\n selectionsToUpdate={selectionsToUpdate}\n hideSelectedValue={hideSelectedValue}\n disablePreselections={!config.getConfig()?.preselectFirstOption}\n onValues={handleOptionSelected}\n onErrors={handleErrors}\n bundleOptionQuantities={bundleOptionQuantities}\n onBundleOptionQuantityChange={handleBundleOptionQuantityChange}\n imageSwatchNode={\n slots?.SwatchImage\n ? ({ imageSwatchContext, ...imageProps }) => (\n <Slot\n name=\"SwatchImage\"\n slot={slots?.SwatchImage}\n slotTag=\"span\"\n contentTag=\"span\"\n context={{\n data,\n optionsUIDs: current,\n defaultImageProps: imageProps,\n imageSwatchContext,\n }}\n />\n )\n : undefined\n }\n />\n </Slot>\n );\n};\n\nProductOptions.getInitialData = async function (options) {\n const data = await getFetchedProductData({ scope: options?.scope });\n const optionsUIDs =\n getProductConfigurationValues({ scope: options?.scope })?.optionsUIDs ?? [];\n return { data, optionsUIDs };\n};\n"],"names":["ProductOptions","initialData","scope","hideSelectedValue","transformer","slots","onValues","onErrors","children","props","data","setData","useState","current","setCurrent","bundleOptionQuantities","setBundleOptionQuantities","selectionsToUpdate","setSelectionsToUpdate","ChooseOptionLabel","useText","transformedOptions","useMemo","error","useEffect","dataEvent","events","valuesEvent","optionsUIDs","handleOptionSelected","useCallback","selections","persistURLParams","anchors","acdl","config","sanitizedSelections","value","selectionMapToOptionUIDs","setUrlParams","storedOptionsUIDs","_a","getProductConfigurationValues","setProductConfigurationValues","prev","next","fetchProductData","syncBundleOptionQuantities","finalOptionsUIDs","mergeBundleOptionQuantities","bundleOptionQuantitiesToEnteredOptions","valid","isProductOptionsSelectionComplete","updatedOptionsUIDs","item","setProductConfigurationValid","publishProductPageViewEvent","handleBundleOptionQuantityChange","optionValueId","quantity","optionValue","option","handleErrors","errors","jsx","Slot","Swatches","imageSwatchContext","imageProps","options","getFetchedProductData"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  /*! Copyright 2026 Adobe
2
2
  All Rights Reserved. */
3
- import{jsx as e}from"@dropins/tools/preact-jsx-runtime.js";import{useState as v,useEffect as C}from"@dropins/tools/preact-compat.js";import{toLanguageTag as a}from"@dropins/tools/lib.js";import"@dropins/tools/components.js";import{P as F,b as I,a as h}from"../chunks/components.js";import"@dropins/tools/preact.js";import"@dropins/tools/preact-hooks.js";import{events as D}from"@dropins/tools/event-bus.js";import{config as R,getFetchedProductData as U}from"../api.js";import"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const k=({initialData:L=null,scope:c,children:q,...x})=>{var s,l,p,n,m,u,f,g,d,P,b;const{globalLocale:S="en-US"}=R.getConfig(),[r,T]=v(L),[i,j]=v(S);return C(()=>{const t=D.on("pdp/data",T,{scope:c}),o=D.on("locale",j);return()=>{t==null||t.off(),o==null||o.off()}},[c]),!r||!r.prices?null:e(F,{...x,specialPrice:(s=r.prices)!=null&&s.visible?e(h,{...(l=r.prices)==null?void 0:l.final,locale:a(i)}):void 0,price:(p=r.prices)!=null&&p.visible&&((n=r.prices)!=null&&n.regular)&&((m=r.prices)==null?void 0:m.final.amount)!==((u=r.prices)==null?void 0:u.regular.amount)?e(h,{...(f=r.prices)==null?void 0:f.regular,locale:a(i)}):void 0,tiers:(g=r.prices)!=null&&g.visible&&((P=(d=r.prices)==null?void 0:d.tiers)==null?void 0:P.length)>0?e(I,{tiers:r.prices.tiers,locale:a(i),regularPrice:(b=r.prices.regular)==null?void 0:b.amount}):void 0})};k.getInitialData=U;export{k as ProductPrice,k as default};
3
+ import{jsx as e}from"@dropins/tools/preact-jsx-runtime.js";import{useState as v,useEffect as C}from"@dropins/tools/preact-compat.js";import{toLanguageTag as a}from"@dropins/tools/lib.js";import"@dropins/tools/components.js";import{b as F,c as I,a as h}from"../chunks/components.js";import"@dropins/tools/preact.js";import"@dropins/tools/preact-hooks.js";import{events as D}from"@dropins/tools/event-bus.js";import{config as R,getFetchedProductData as U}from"../api.js";import"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const k=({initialData:L=null,scope:c,children:q,...x})=>{var s,l,p,n,m,u,f,g,d,P,b;const{globalLocale:S="en-US"}=R.getConfig(),[r,T]=v(L),[i,j]=v(S);return C(()=>{const t=D.on("pdp/data",T,{scope:c}),o=D.on("locale",j);return()=>{t==null||t.off(),o==null||o.off()}},[c]),!r||!r.prices?null:e(F,{...x,specialPrice:(s=r.prices)!=null&&s.visible?e(h,{...(l=r.prices)==null?void 0:l.final,locale:a(i)}):void 0,price:(p=r.prices)!=null&&p.visible&&((n=r.prices)!=null&&n.regular)&&((m=r.prices)==null?void 0:m.final.amount)!==((u=r.prices)==null?void 0:u.regular.amount)?e(h,{...(f=r.prices)==null?void 0:f.regular,locale:a(i)}):void 0,tiers:(g=r.prices)!=null&&g.visible&&((P=(d=r.prices)==null?void 0:d.tiers)==null?void 0:P.length)>0?e(I,{tiers:r.prices.tiers,locale:a(i),regularPrice:(b=r.prices.regular)==null?void 0:b.amount}):void 0})};k.getInitialData=U;export{k as ProductPrice,k as default};
4
4
  //# sourceMappingURL=ProductPrice.js.map
@@ -1,4 +1,4 @@
1
1
  /*! Copyright 2026 Adobe
2
2
  All Rights Reserved. */
3
- import{jsx as P}from"@dropins/tools/preact-jsx-runtime.js";import{useState as s,useEffect as y}from"@dropins/tools/preact-compat.js";import{classes as I}from"@dropins/tools/lib.js";import{Incrementer as b}from"@dropins/tools/components.js";import{events as m}from"@dropins/tools/event-bus.js";import{g as q,setProductConfigurationValues as D,config as h,a as x,getFetchedProductData as C}from"../api.js";import{useText as S}from"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const U=({initialData:u=null,scope:e,onValue:a,children:F,...o})=>{const l=S("PDP.Product.Incrementer.label").label,[c,f]=s(u),[i,d]=s(()=>q(1));y(()=>{const r=m.on("pdp/data",f,{scope:e}),t=m.on("pdp/values",({quantity:n})=>{d(n)},{eager:!0,scope:e});return()=>{r==null||r.off(),t==null||t.off()}},[e]);const p=r=>{const t=parseInt(r,10);D(g=>({...g,quantity:t}),{scope:e});const{persistURLParams:n}=h.getConfig()??{};n&&x({quantity:t}),a==null||a(t)};return c?P(b,{name:"quantity",defaultValue:String(i),value:String(i),min:1,"aria-label":l,onValue:p,...o,className:I(["pdp-quantity",o.className])},"quantity"):null};U.getInitialData=C;export{U as ProductQuantity,U as default};
3
+ import{jsx as P}from"@dropins/tools/preact-jsx-runtime.js";import{useState as u,useEffect as y}from"@dropins/tools/preact-compat.js";import{classes as I}from"@dropins/tools/lib.js";import{Incrementer as b}from"@dropins/tools/components.js";import{events as l}from"@dropins/tools/event-bus.js";import{g as q,setProductConfigurationValues as D,config as h,c as x,getFetchedProductData as C}from"../api.js";import{useText as S}from"@dropins/tools/i18n.js";import"../fragments.js";import"@dropins/tools/fetch-graphql.js";const U=({initialData:m=null,scope:e,onValue:a,children:F,...i})=>{const c=S("PDP.Product.Incrementer.label").label,[o,f]=u(m),[s,d]=u(()=>q(1));y(()=>{const r=l.on("pdp/data",f,{scope:e}),t=l.on("pdp/values",({quantity:n})=>{d(n)},{eager:!0,scope:e});return()=>{r==null||r.off(),t==null||t.off()}},[e]);const p=r=>{const t=parseInt(r,10);D(g=>({...g,quantity:t}),{scope:e});const{persistURLParams:n}=h.getConfig()??{};n&&x({quantity:t}),a==null||a(t)};return!o||o.isBundle?null:P(b,{name:"quantity",defaultValue:String(s),value:String(s),min:1,"aria-label":c,onValue:p,...i,className:I(["pdp-quantity",i.className])},"quantity")};U.getInitialData=C;export{U as ProductQuantity,U as default};
4
4
  //# sourceMappingURL=ProductQuantity.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductQuantity.js","sources":["/@dropins/storefront-pdp/src/containers/ProductQuantity/ProductQuantity.tsx"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2024 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 { HTMLAttributes, useEffect, useState } from 'preact/compat';\nimport { classes, Container } from '@adobe-commerce/elsie/lib';\nimport { Incrementer } from '@adobe-commerce/elsie/components';\nimport { events } from '@adobe-commerce/event-bus';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { ProductModel } from '@/pdp/data/models';\nimport {\n setProductConfigurationValues,\n getFetchedProductData,\n} from '@/pdp/api';\nimport {\n setUrlParams,\n getValidatedQuantityFromUrl,\n} from '@/pdp/lib/url-params';\nimport { config } from '@/pdp/api/initialize';\n\nexport interface ProductQuantityProps extends HTMLAttributes<HTMLDivElement> {\n scope?: string;\n onValue?: (quantity: number) => void;\n}\n\nexport const ProductQuantity: Container<\n ProductQuantityProps,\n ProductModel | null\n> = ({ initialData = null, scope, onValue, children, ...props }) => {\n const IncrementerLabel = useText('PDP.Product.Incrementer.label').label;\n\n const [data, setData] = useState<ProductModel | null>(initialData);\n\n const [current, setCurrent] = useState(() => getValidatedQuantityFromUrl(1)); // Default quantity to 1\n\n useEffect(() => {\n const dataEvent = events.on('pdp/data', setData, { scope });\n\n const valuesEvent = events.on(\n 'pdp/values',\n ({ quantity }) => {\n setCurrent(quantity);\n },\n { eager: true, scope }\n );\n\n return () => {\n dataEvent?.off();\n valuesEvent?.off();\n };\n }, [scope]);\n\n const handleOnValue = (value: string) => {\n const quantity = parseInt(value, 10);\n setProductConfigurationValues((prev) => ({ ...prev, quantity }), { scope });\n\n // Update URL parameters if persistURLParams is enabled\n const { persistURLParams } = config.getConfig() ?? {};\n if (persistURLParams) {\n setUrlParams({ quantity });\n }\n\n onValue?.(quantity);\n };\n\n if (!data) return null;\n\n return (\n <Incrementer\n key=\"quantity\"\n name=\"quantity\"\n defaultValue={String(current)}\n value={String(current)}\n min={1}\n aria-label={IncrementerLabel}\n onValue={handleOnValue}\n {...props}\n className={classes(['pdp-quantity', props.className])}\n />\n );\n};\n\nProductQuantity.getInitialData = getFetchedProductData;\n"],"names":["ProductQuantity","initialData","scope","onValue","children","props","IncrementerLabel","useText","data","setData","useState","current","setCurrent","getValidatedQuantityFromUrl","useEffect","dataEvent","events","valuesEvent","quantity","handleOnValue","value","setProductConfigurationValues","prev","persistURLParams","config","setUrlParams","jsx","Incrementer","classes","getFetchedProductData"],"mappings":"qgBAqCO,MAAMA,EAGT,CAAC,CAAE,YAAAC,EAAc,KAAM,MAAAC,EAAO,QAAAC,EAAS,SAAAC,EAAU,GAAGC,KAAY,CAClE,MAAMC,EAAmBC,EAAQ,+BAA+B,EAAE,MAE5D,CAACC,EAAMC,CAAO,EAAIC,EAA8BT,CAAW,EAE3D,CAACU,EAASC,CAAU,EAAIF,EAAS,IAAMG,EAA4B,CAAC,CAAC,EAE3EC,EAAU,IAAM,CACd,MAAMC,EAAYC,EAAO,GAAG,WAAYP,EAAS,CAAE,MAAAP,EAAO,EAEpDe,EAAcD,EAAO,GACzB,aACA,CAAC,CAAE,SAAAE,CAAA,IAAe,CAChBN,EAAWM,CAAQ,CACrB,EACA,CAAE,MAAO,GAAM,MAAAhB,CAAA,CAAM,EAGvB,MAAO,IAAM,CACXa,GAAA,MAAAA,EAAW,MACXE,GAAA,MAAAA,EAAa,KACf,CACF,EAAG,CAACf,CAAK,CAAC,EAEV,MAAMiB,EAAiBC,GAAkB,CACvC,MAAMF,EAAW,SAASE,EAAO,EAAE,EACnCC,EAA+BC,IAAU,CAAE,GAAGA,EAAM,SAAAJ,IAAa,CAAE,MAAAhB,EAAO,EAG1E,KAAM,CAAE,iBAAAqB,CAAA,EAAqBC,EAAO,UAAA,GAAe,CAAA,EAC/CD,GACFE,EAAa,CAAE,SAAAP,EAAU,EAG3Bf,GAAA,MAAAA,EAAUe,EACZ,EAEA,OAAKV,EAGHkB,EAACC,EAAA,CAEC,KAAK,WACL,aAAc,OAAOhB,CAAO,EAC5B,MAAO,OAAOA,CAAO,EACrB,IAAK,EACL,aAAYL,EACZ,QAASa,EACR,GAAGd,EACJ,UAAWuB,EAAQ,CAAC,eAAgBvB,EAAM,SAAS,CAAC,CAAA,EARhD,UAAA,EAJU,IAepB,EAEAL,EAAgB,eAAiB6B"}
1
+ {"version":3,"file":"ProductQuantity.js","sources":["/@dropins/storefront-pdp/src/containers/ProductQuantity/ProductQuantity.tsx"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2024 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 { HTMLAttributes, useEffect, useState } from 'preact/compat';\nimport { classes, Container } from '@adobe-commerce/elsie/lib';\nimport { Incrementer } from '@adobe-commerce/elsie/components';\nimport { events } from '@adobe-commerce/event-bus';\nimport { useText } from '@adobe-commerce/elsie/i18n';\nimport { ProductModel } from '@/pdp/data/models';\nimport {\n setProductConfigurationValues,\n getFetchedProductData,\n} from '@/pdp/api';\nimport {\n setUrlParams,\n getValidatedQuantityFromUrl,\n} from '@/pdp/lib/url-params';\nimport { config } from '@/pdp/api/initialize';\n\nexport interface ProductQuantityProps extends HTMLAttributes<HTMLDivElement> {\n scope?: string;\n onValue?: (quantity: number) => void;\n}\n\nexport const ProductQuantity: Container<\n ProductQuantityProps,\n ProductModel | null\n> = ({ initialData = null, scope, onValue, children, ...props }) => {\n const IncrementerLabel = useText('PDP.Product.Incrementer.label').label;\n\n const [data, setData] = useState<ProductModel | null>(initialData);\n\n const [current, setCurrent] = useState(() => getValidatedQuantityFromUrl(1)); // Default quantity to 1\n\n useEffect(() => {\n const dataEvent = events.on('pdp/data', setData, { scope });\n\n const valuesEvent = events.on(\n 'pdp/values',\n ({ quantity }) => {\n setCurrent(quantity);\n },\n { eager: true, scope }\n );\n\n return () => {\n dataEvent?.off();\n valuesEvent?.off();\n };\n }, [scope]);\n\n const handleOnValue = (value: string) => {\n const quantity = parseInt(value, 10);\n setProductConfigurationValues((prev) => ({ ...prev, quantity }), { scope });\n\n // Update URL parameters if persistURLParams is enabled\n const { persistURLParams } = config.getConfig() ?? {};\n if (persistURLParams) {\n setUrlParams({ quantity });\n }\n\n onValue?.(quantity);\n };\n\n if (!data) return null;\n\n // Bundle child quantities are configured per option value in ProductOptions/Swatches.\n if (data.isBundle) return null;\n\n return (\n <Incrementer\n key=\"quantity\"\n name=\"quantity\"\n defaultValue={String(current)}\n value={String(current)}\n min={1}\n aria-label={IncrementerLabel}\n onValue={handleOnValue}\n {...props}\n className={classes(['pdp-quantity', props.className])}\n />\n );\n};\n\nProductQuantity.getInitialData = getFetchedProductData;\n"],"names":["ProductQuantity","initialData","scope","onValue","children","props","IncrementerLabel","useText","data","setData","useState","current","setCurrent","getValidatedQuantityFromUrl","useEffect","dataEvent","events","valuesEvent","quantity","handleOnValue","value","setProductConfigurationValues","prev","persistURLParams","config","setUrlParams","jsx","Incrementer","classes","getFetchedProductData"],"mappings":"qgBAqCO,MAAMA,EAGT,CAAC,CAAE,YAAAC,EAAc,KAAM,MAAAC,EAAO,QAAAC,EAAS,SAAAC,EAAU,GAAGC,KAAY,CAClE,MAAMC,EAAmBC,EAAQ,+BAA+B,EAAE,MAE5D,CAACC,EAAMC,CAAO,EAAIC,EAA8BT,CAAW,EAE3D,CAACU,EAASC,CAAU,EAAIF,EAAS,IAAMG,EAA4B,CAAC,CAAC,EAE3EC,EAAU,IAAM,CACd,MAAMC,EAAYC,EAAO,GAAG,WAAYP,EAAS,CAAE,MAAAP,EAAO,EAEpDe,EAAcD,EAAO,GACzB,aACA,CAAC,CAAE,SAAAE,CAAA,IAAe,CAChBN,EAAWM,CAAQ,CACrB,EACA,CAAE,MAAO,GAAM,MAAAhB,CAAA,CAAM,EAGvB,MAAO,IAAM,CACXa,GAAA,MAAAA,EAAW,MACXE,GAAA,MAAAA,EAAa,KACf,CACF,EAAG,CAACf,CAAK,CAAC,EAEV,MAAMiB,EAAiBC,GAAkB,CACvC,MAAMF,EAAW,SAASE,EAAO,EAAE,EACnCC,EAA+BC,IAAU,CAAE,GAAGA,EAAM,SAAAJ,IAAa,CAAE,MAAAhB,EAAO,EAG1E,KAAM,CAAE,iBAAAqB,CAAA,EAAqBC,EAAO,UAAA,GAAe,CAAA,EAC/CD,GACFE,EAAa,CAAE,SAAAP,EAAU,EAG3Bf,GAAA,MAAAA,EAAUe,EACZ,EAKA,MAHI,CAACV,GAGDA,EAAK,SAAiB,KAGxBkB,EAACC,EAAA,CAEC,KAAK,WACL,aAAc,OAAOhB,CAAO,EAC5B,MAAO,OAAOA,CAAO,EACrB,IAAK,EACL,aAAYL,EACZ,QAASa,EACR,GAAGd,EACJ,UAAWuB,EAAQ,CAAC,eAAgBvB,EAAM,SAAS,CAAC,CAAA,EARhD,UAAA,CAWV,EAEAL,EAAgB,eAAiB6B"}
@@ -68,12 +68,16 @@ export interface Option {
68
68
  multiple: boolean;
69
69
  items: OptionValue[];
70
70
  }
71
- interface OptionValue {
71
+ export interface OptionValue {
72
72
  id: string;
73
73
  label: string;
74
74
  inStock: boolean;
75
75
  value: string;
76
76
  selected: boolean;
77
+ /** Default quantity from Catalog Service for bundle option values. */
78
+ quantity?: number;
79
+ /** When true, shoppers may change this bundle option value quantity. */
80
+ canEditQuantity?: boolean;
77
81
  product?: any;
78
82
  }
79
83
  interface Attribute {
@@ -21,6 +21,8 @@ export interface ValuesModel {
21
21
  sku: string;
22
22
  quantity: number;
23
23
  optionsUIDs?: string[];
24
+ /** Per bundle option value UID quantities (cart `entered_options`). */
25
+ bundleOptionQuantities?: Record<string, number>;
24
26
  enteredOptions?: Array<{
25
27
  uid: string;
26
28
  value: string;
package/fragments.js CHANGED
@@ -14,6 +14,7 @@ fragment PRODUCT_OPTION_FRAGMENT on ProductViewOption {
14
14
  ... on ProductViewOptionValueProduct {
15
15
  title
16
16
  quantity
17
+ canEditQuantity
17
18
  isDefault
18
19
  __typename
19
20
  product {
package/fragments.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"fragments.js","sources":["/@dropins/storefront-pdp/src/api/graphql/ProductOptionFragment.graphql.ts","/@dropins/storefront-pdp/src/api/graphql/PriceRangeFragment.graphql.ts","/@dropins/storefront-pdp/src/api/graphql/ProductFragment.graphql.ts"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2023 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\nexport const PRODUCT_OPTION_FRAGMENT = `\nfragment PRODUCT_OPTION_FRAGMENT on ProductViewOption {\n id\n title\n required\n multi\n values {\n id\n title\n inStock\n __typename\n ... on ProductViewOptionValueProduct {\n title\n quantity\n isDefault\n __typename\n product {\n sku\n shortDescription\n metaDescription\n metaKeyword\n metaTitle\n name\n price {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n }\n }\n ... on ProductViewOptionValueSwatch {\n id\n title\n type\n value\n inStock\n }\n }\n }\n`;\n","/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2025 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\nexport const PRICE_RANGE_FRAGMENT = `\n fragment PRICE_RANGE_FRAGMENT on ComplexProductView {\n priceRange {\n maximum {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n minimum {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n }\n }\n`;\n","/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2023 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 { PRODUCT_OPTION_FRAGMENT } from '@/pdp/api/graphql/ProductOptionFragment.graphql';\nimport { PRICE_RANGE_FRAGMENT } from './PriceRangeFragment.graphql';\n\nexport const PRODUCT_FRAGMENT = `\nfragment PRODUCT_FRAGMENT on ProductView {\n __typename\n id\n sku\n name\n shortDescription\n metaDescription\n metaKeyword\n metaTitle\n description\n inStock\n addToCartAllowed\n url\n urlKey\n externalId\n\n images(roles: []) {\n url\n label\n roles\n }\n\n videos {\n\t\tdescription\n\t\turl\n\t\ttitle\n\t\tpreview {\n\t\t\tlabel\n\t\t\troles\n\t\t\turl\n\t\t}\n\t}\n\n attributes(roles: []) {\n name\n label\n value\n roles\n }\n\n... on SimpleProductView {\n price {\n roles\n\n regular {\n amount {\n value\n currency\n }\n }\n\n final {\n amount {\n value\n currency\n }\n }\n tiers {\n tier {\n amount {\n value\n currency\n }\n }\n quantity {\n ... on ProductViewTierRangeCondition {\n gte\n lt\n }\n }\n }\n }\n }\n\n ... on ComplexProductView {\n options {\n ...PRODUCT_OPTION_FRAGMENT\n }\n\n ...PRICE_RANGE_FRAGMENT\n }\n}\n\n${PRODUCT_OPTION_FRAGMENT}\n${PRICE_RANGE_FRAGMENT}\n`;\n"],"names":["PRODUCT_OPTION_FRAGMENT","PRICE_RANGE_FRAGMENT","PRODUCT_FRAGMENT"],"mappings":"AAgBO,MAAMA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECA1BC,EAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECGvBC,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoF9BF,CAAuB;AAAA,EACvBC,CAAoB;"}
1
+ {"version":3,"file":"fragments.js","sources":["/@dropins/storefront-pdp/src/api/graphql/ProductOptionFragment.graphql.ts","/@dropins/storefront-pdp/src/api/graphql/PriceRangeFragment.graphql.ts","/@dropins/storefront-pdp/src/api/graphql/ProductFragment.graphql.ts"],"sourcesContent":["/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2023 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\nexport const PRODUCT_OPTION_FRAGMENT = `\nfragment PRODUCT_OPTION_FRAGMENT on ProductViewOption {\n id\n title\n required\n multi\n values {\n id\n title\n inStock\n __typename\n ... on ProductViewOptionValueProduct {\n title\n quantity\n canEditQuantity\n isDefault\n __typename\n product {\n sku\n shortDescription\n metaDescription\n metaKeyword\n metaTitle\n name\n price {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n }\n }\n ... on ProductViewOptionValueSwatch {\n id\n title\n type\n value\n inStock\n }\n }\n }\n`;\n","/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2025 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\nexport const PRICE_RANGE_FRAGMENT = `\n fragment PRICE_RANGE_FRAGMENT on ComplexProductView {\n priceRange {\n maximum {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n minimum {\n final {\n amount {\n value\n currency\n }\n }\n regular {\n amount {\n value\n currency\n }\n }\n roles\n }\n }\n }\n`;\n","/**\n * ADOBE CONFIDENTIAL\n * __________________\n * Copyright 2023 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 { PRODUCT_OPTION_FRAGMENT } from '@/pdp/api/graphql/ProductOptionFragment.graphql';\nimport { PRICE_RANGE_FRAGMENT } from './PriceRangeFragment.graphql';\n\nexport const PRODUCT_FRAGMENT = `\nfragment PRODUCT_FRAGMENT on ProductView {\n __typename\n id\n sku\n name\n shortDescription\n metaDescription\n metaKeyword\n metaTitle\n description\n inStock\n addToCartAllowed\n url\n urlKey\n externalId\n\n images(roles: []) {\n url\n label\n roles\n }\n\n videos {\n\t\tdescription\n\t\turl\n\t\ttitle\n\t\tpreview {\n\t\t\tlabel\n\t\t\troles\n\t\t\turl\n\t\t}\n\t}\n\n attributes(roles: []) {\n name\n label\n value\n roles\n }\n\n... on SimpleProductView {\n price {\n roles\n\n regular {\n amount {\n value\n currency\n }\n }\n\n final {\n amount {\n value\n currency\n }\n }\n tiers {\n tier {\n amount {\n value\n currency\n }\n }\n quantity {\n ... on ProductViewTierRangeCondition {\n gte\n lt\n }\n }\n }\n }\n }\n\n ... on ComplexProductView {\n options {\n ...PRODUCT_OPTION_FRAGMENT\n }\n\n ...PRICE_RANGE_FRAGMENT\n }\n}\n\n${PRODUCT_OPTION_FRAGMENT}\n${PRICE_RANGE_FRAGMENT}\n`;\n"],"names":["PRODUCT_OPTION_FRAGMENT","PRICE_RANGE_FRAGMENT","PRODUCT_FRAGMENT"],"mappings":"AAgBO,MAAMA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECA1BC,EAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,ECGvBC,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoF9BF,CAAuB;AAAA,EACvBC,CAAoB;"}
@@ -67,6 +67,9 @@ declare const _default: {
67
67
  },
68
68
  "ChooseOption": {
69
69
  "label": "Choose an option"
70
+ },
71
+ "BundleOptionQuantity": {
72
+ "label": "{option} quantity"
70
73
  }
71
74
  },
72
75
  "DownloadableOptions": {
@@ -0,0 +1,16 @@
1
+ import { Option } from '../data/models';
2
+
3
+ export type BundleOptionQuantities = Record<string, number>;
4
+ export declare function readCanEditQuantity(value: Record<string, unknown>): boolean;
5
+ export declare function readDefaultOptionQuantity(value: Record<string, unknown>): number;
6
+ export declare function buildBundleOptionQuantitiesFromOptions(options: Option[] | undefined, optionUIDs: string[] | undefined): BundleOptionQuantities;
7
+ export declare function mergeBundleOptionQuantities(previous: BundleOptionQuantities | undefined, options: Option[] | undefined, optionUIDs: string[] | undefined): BundleOptionQuantities;
8
+ export declare function bundleOptionQuantitiesToEnteredOptions(quantities: BundleOptionQuantities | undefined): Array<{
9
+ uid: string;
10
+ value: string;
11
+ }>;
12
+ export declare function bundleOptionQuantitiesFromEnteredOptions(enteredOptions: Array<{
13
+ uid: string;
14
+ value: string;
15
+ }> | undefined): BundleOptionQuantities;
16
+ //# sourceMappingURL=bundle-option-quantities.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dropins/storefront-pdp",
3
- "version": "3.2.0",
3
+ "version": "3.3.0-beta.1",
4
4
  "@dropins/tools": "~1.9.0",
5
5
  "license": "SEE LICENSE IN LICENSE.md"
6
6
  }
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-swatches__options--checkboxes{flex-direction:column;align-items:flex-start;gap:var(--spacing-small)}.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)}.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)}.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}
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-swatches__options--checkboxes{flex-direction:column;align-items:flex-start;gap:var(--spacing-small)}.pdp-swatches__options--bundle{flex-direction:column;align-items:flex-start}.pdp-swatches__bundle-option{display:flex;flex-direction:column;align-items:flex-start;gap:var(--spacing-xsmall);width:100%}.pdp-swatches__bundle-option__quantity{align-self:flex-start;width:fit-content;max-width:100%}.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)}.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)}.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}
4
+ .pdp-attributes__title{margin:0;font:inherit;font-weight:inherit}
4
5
  .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}}`,{styleId:"PDP"});
5
- 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 b,useEffect as c}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{config as g}from"./api.js";import"./fragments.js";import"@dropins/tools/fetch-graphql.js";const p={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:p},P={default:h},C=({children:i})=>{var r;const[e,t]=b(),n=(r=g.getConfig())==null?void 0:r.langDefinitions;c(()=>{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})},I=new u(o(C,{}));export{I as render};
6
+ 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 b,useEffect as c}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{config as p}from"./api.js";import"./fragments.js";import"@dropins/tools/fetch-graphql.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"},BundleOptionQuantity:{label:"{option} quantity"}},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:t})=>{var a;const[e,i]=b(),n=(a=p.getConfig())==null?void 0:a.langDefinitions;c(()=>{const l=f.on("locale",r=>{r!==e&&i(r)},{eager:!0});return()=>{l==null||l.off()}},[e]);const s=m(P,n??{});return o(d,{lang:e,langDefinitions:s,children:t})},y=new u(o(C,{}));export{y as render};
6
7
  //# sourceMappingURL=render.js.map