@shopify/shop-minis-react 0.3.1 → 0.3.4

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.
Files changed (42) hide show
  1. package/dist/components/atoms/content-wrapper.js.map +1 -1
  2. package/dist/components/atoms/video-player.js +28 -26
  3. package/dist/components/atoms/video-player.js.map +1 -1
  4. package/dist/components/commerce/product-card.js +106 -79
  5. package/dist/components/commerce/product-card.js.map +1 -1
  6. package/dist/components/commerce/product-link.js +124 -137
  7. package/dist/components/commerce/product-link.js.map +1 -1
  8. package/dist/components/commerce/search.js +20 -20
  9. package/dist/components/commerce/search.js.map +1 -1
  10. package/dist/components/ui/sonner.js +3 -1
  11. package/dist/components/ui/sonner.js.map +1 -1
  12. package/dist/hooks/navigation/useNavigateWithTransition.js +10 -11
  13. package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -1
  14. package/dist/index.js +269 -264
  15. package/dist/index.js.map +1 -1
  16. package/dist/internal/components/product-review-stars.js +78 -0
  17. package/dist/internal/components/product-review-stars.js.map +1 -0
  18. package/dist/mocks.js +178 -107
  19. package/dist/mocks.js.map +1 -1
  20. package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/star-half.js +21 -0
  21. package/dist/shop-minis-react/node_modules/.pnpm/lucide-react@0.513.0_react@19.1.0/node_modules/lucide-react/dist/esm/icons/star-half.js.map +1 -0
  22. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +1 -1
  23. package/dist/shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js +4 -4
  24. package/dist/shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js.map +1 -1
  25. package/dist/shop-minis-react/node_modules/.pnpm/use-sync-external-store@1.5.0_react@19.1.0/node_modules/use-sync-external-store/shim/index.js +1 -1
  26. package/eslint/rules/prefer-sdk-hooks.cjs +9 -11
  27. package/package.json +1 -1
  28. package/src/components/atoms/content-wrapper.tsx +1 -1
  29. package/src/components/atoms/video-player.tsx +7 -0
  30. package/src/components/commerce/product-card.test.tsx +135 -0
  31. package/src/components/commerce/product-card.tsx +39 -5
  32. package/src/components/commerce/product-link.test.tsx +15 -3
  33. package/src/components/commerce/product-link.tsx +9 -25
  34. package/src/components/commerce/search.tsx +2 -2
  35. package/src/components/index.ts +1 -0
  36. package/src/components/ui/sonner.tsx +2 -2
  37. package/src/hooks/navigation/useNavigateWithTransition.test.ts +46 -7
  38. package/src/hooks/navigation/useNavigateWithTransition.ts +4 -1
  39. package/src/internal/components/product-review-stars.test.tsx +90 -0
  40. package/src/internal/components/product-review-stars.tsx +113 -0
  41. package/src/mocks.ts +10 -2
  42. package/src/providers/ImagePickerProvider.test.tsx +3 -9
@@ -1 +1 @@
1
- {"version":3,"file":"content-wrapper.js","sources":["../../../src/components/atoms/content-wrapper.tsx"],"sourcesContent":["import {useContent} from '../../hooks/content/useContent'\nimport {Content} from '../../types'\n\nimport {ContentMonitor} from './content-monitor'\n\ninterface BaseContentWrapperProps {\n children: ({\n content,\n loading,\n }: {\n content?: Content\n loading: boolean\n }) => JSX.Element | null\n}\n\ninterface PublicIdContentWrapperProps extends BaseContentWrapperProps {\n publicId: string\n externalId?: never\n}\n\ninterface ExternalIdContentWrapperProps extends BaseContentWrapperProps {\n externalId: string\n publicId?: never\n}\n\ntype ContentWrapperProps =\n | PublicIdContentWrapperProps\n | ExternalIdContentWrapperProps\n\n// It's too messy in the docs to show the complete types here so we show a simplified version\nexport interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {\n publicId?: string\n externalId?: string\n}\n\nexport function ContentWrapper({\n publicId,\n externalId,\n children,\n}: ContentWrapperProps) {\n const {content, loading} = useContent({\n identifiers: [{publicId, externalId}],\n skip: !publicId && !externalId,\n })\n\n const contentItem = content?.[0]\n\n if (loading || !contentItem) {\n return children({loading})\n }\n\n return (\n <ContentMonitor publicId={contentItem.publicId}>\n {children({content: contentItem, loading})}\n </ContentMonitor>\n )\n}\n"],"names":["ContentWrapper","publicId","externalId","children","content","loading","useContent","contentItem","jsx","ContentMonitor"],"mappings":";;;AAmCO,SAASA,EAAe;AAAA,EAC7B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AACF,GAAwB;AACtB,QAAM,EAAC,SAAAC,GAAS,SAAAC,EAAO,IAAIC,EAAW;AAAA,IACpC,aAAa,CAAC,EAAC,UAAAL,GAAU,YAAAC,GAAW;AAAA,IACpC,MAAM,CAACD,KAAY,CAACC;AAAA,EAAA,CACrB,GAEKK,IAAcH,IAAU,CAAC;AAE3B,SAAAC,KAAW,CAACE,IACPJ,EAAS,EAAC,SAAAE,GAAQ,IAIzB,gBAAAG,EAACC,GAAe,EAAA,UAAUF,EAAY,UACnC,UAASJ,EAAA,EAAC,SAASI,GAAa,SAAAF,EAAO,CAAC,EAC3C,CAAA;AAEJ;"}
1
+ {"version":3,"file":"content-wrapper.js","sources":["../../../src/components/atoms/content-wrapper.tsx"],"sourcesContent":["import {useContent} from '../../hooks/content/useContent'\nimport {Content} from '../../types'\n\nimport {ContentMonitor} from './content-monitor'\n\ninterface BaseContentWrapperProps {\n children: ({\n content,\n loading,\n }: {\n content?: Content\n loading: boolean\n }) => React.JSX.Element | null\n}\n\ninterface PublicIdContentWrapperProps extends BaseContentWrapperProps {\n publicId: string\n externalId?: never\n}\n\ninterface ExternalIdContentWrapperProps extends BaseContentWrapperProps {\n externalId: string\n publicId?: never\n}\n\ntype ContentWrapperProps =\n | PublicIdContentWrapperProps\n | ExternalIdContentWrapperProps\n\n// It's too messy in the docs to show the complete types here so we show a simplified version\nexport interface ContentWrapperPropsForDocs extends BaseContentWrapperProps {\n publicId?: string\n externalId?: string\n}\n\nexport function ContentWrapper({\n publicId,\n externalId,\n children,\n}: ContentWrapperProps) {\n const {content, loading} = useContent({\n identifiers: [{publicId, externalId}],\n skip: !publicId && !externalId,\n })\n\n const contentItem = content?.[0]\n\n if (loading || !contentItem) {\n return children({loading})\n }\n\n return (\n <ContentMonitor publicId={contentItem.publicId}>\n {children({content: contentItem, loading})}\n </ContentMonitor>\n )\n}\n"],"names":["ContentWrapper","publicId","externalId","children","content","loading","useContent","contentItem","jsx","ContentMonitor"],"mappings":";;;AAmCO,SAASA,EAAe;AAAA,EAC7B,UAAAC;AAAA,EACA,YAAAC;AAAA,EACA,UAAAC;AACF,GAAwB;AACtB,QAAM,EAAC,SAAAC,GAAS,SAAAC,EAAO,IAAIC,EAAW;AAAA,IACpC,aAAa,CAAC,EAAC,UAAAL,GAAU,YAAAC,GAAW;AAAA,IACpC,MAAM,CAACD,KAAY,CAACC;AAAA,EAAA,CACrB,GAEKK,IAAcH,IAAU,CAAC;AAE3B,SAAAC,KAAW,CAACE,IACPJ,EAAS,EAAC,SAAAE,GAAQ,IAIzB,gBAAAG,EAACC,GAAe,EAAA,UAAUF,EAAY,UACnC,UAASJ,EAAA,EAAC,SAASI,GAAa,SAAAF,EAAO,CAAC,EAC3C,CAAA;AAEJ;"}
@@ -1,24 +1,24 @@
1
1
  import { jsx as t, jsxs as E } from "react/jsx-runtime";
2
- import { forwardRef as k, useRef as C, useState as I, useImperativeHandle as B, useMemo as D, useEffect as i, useCallback as M } from "react";
2
+ import { forwardRef as k, useRef as C, useState as D, useImperativeHandle as I, useMemo as B, useEffect as a, useCallback as M } from "react";
3
3
  import R from "../../shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js";
4
4
  /* empty css */
5
5
  const W = k(
6
6
  ({
7
- src: u,
8
- format: f = "video/mp4",
9
- poster: d,
10
- muted: p,
7
+ src: f,
8
+ format: d = "video/mp4",
9
+ poster: p,
10
+ muted: l,
11
11
  autoplay: m,
12
- width: l,
12
+ width: s,
13
13
  height: v,
14
14
  playButtonComponent: N,
15
15
  onPlay: y,
16
16
  onPause: h,
17
17
  onEnded: j,
18
- onReady: s
18
+ onReady: c
19
19
  }, b) => {
20
- const a = C(null), e = C(null), [o, n] = I(!1);
21
- B(
20
+ const i = C(null), e = C(null), [o, n] = D(!1);
21
+ I(
22
22
  b,
23
23
  () => ({
24
24
  play: async () => {
@@ -35,7 +35,7 @@ const W = k(
35
35
  }),
36
36
  []
37
37
  );
38
- const x = D(
38
+ const x = B(
39
39
  () => ({
40
40
  controls: !1,
41
41
  controlBar: {
@@ -44,28 +44,28 @@ const W = k(
44
44
  }
45
45
  },
46
46
  preload: "auto",
47
- muted: p,
47
+ muted: l,
48
48
  // This makes sure that the video player does not take over the whole screen
49
49
  preferFullWindow: !1,
50
50
  playsinline: !0,
51
- poster: d,
51
+ poster: p,
52
52
  height: v,
53
- width: l,
53
+ width: s,
54
54
  sources: [
55
55
  {
56
- src: u,
57
- type: f
56
+ src: f,
57
+ type: d
58
58
  }
59
59
  ]
60
60
  }),
61
- [p, d, v, l, u, f]
61
+ [l, p, v, s, f, d]
62
62
  );
63
- i(() => {
63
+ a(() => {
64
64
  const r = e.current;
65
65
  return () => {
66
66
  r && !r.isDisposed() && (r.dispose(), e.current = null);
67
67
  };
68
- }, [e]), i(() => {
68
+ }, [e]), a(() => {
69
69
  if (!e.current) return;
70
70
  const r = () => {
71
71
  y?.(), n(!0);
@@ -78,28 +78,30 @@ const W = k(
78
78
  e.current && (e.current.off("play", r), e.current.off("pause", P), e.current.off("ended", g));
79
79
  };
80
80
  }, [j, h, y]);
81
- const c = M(() => {
81
+ const u = M(() => {
82
82
  o ? (e.current?.pause(), n(!1)) : (e.current?.play(), n(!0));
83
83
  }, [o]);
84
- i(() => {
84
+ a(() => {
85
85
  if (!e.current) {
86
86
  const r = document.createElement("video-js");
87
- a.current?.appendChild(r), e.current = R(r, x, () => {
88
- s && s(), m && c();
87
+ i.current?.appendChild(r), e.current = R(r, x, () => {
88
+ c && c(), m && u();
89
89
  });
90
90
  }
91
- }, [x, a, m, s, c]);
91
+ }, [x, i, m, c, u]), a(() => {
92
+ e.current && !e.current.isDisposed() && e.current.muted(l ?? !1);
93
+ }, [l]);
92
94
  const w = [
93
- l ? `w-${l}` : void 0,
95
+ s ? `w-${s}` : void 0,
94
96
  "relative"
95
97
  ].join(" ");
96
98
  return /* @__PURE__ */ t("div", { className: "flex", children: /* @__PURE__ */ E("div", { "data-vjs-player": !0, className: w, children: [
97
- /* @__PURE__ */ t("div", { ref: a }),
99
+ /* @__PURE__ */ t("div", { ref: i }),
98
100
  /* @__PURE__ */ t(
99
101
  "div",
100
102
  {
101
103
  className: "absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer",
102
- onClick: c,
104
+ onClick: u,
103
105
  "aria-hidden": !0,
104
106
  children: o ? null : N ?? /* @__PURE__ */ t(z, {})
105
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"video-player.js","sources":["../../../src/components/atoms/video-player.tsx"],"sourcesContent":["import {\n useRef,\n useEffect,\n useState,\n useCallback,\n forwardRef,\n useImperativeHandle,\n useMemo,\n} from 'react'\n\nimport videojs from 'video.js'\nimport Player from 'video.js/dist/types/player'\nimport 'video.js/dist/video-js.css'\n\nexport interface VideoPlayerRef {\n play: () => Promise<void>\n pause: () => void\n}\n\ninterface VideoPlayerProps {\n src: string\n /**\n * The format/MIME type of the video.\n * @default 'video/mp4'\n */\n format?: string\n muted?: boolean\n poster?: string\n autoplay?: boolean\n width?: number\n height?: number\n playButtonComponent?: React.ReactNode\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onReady?: () => void\n}\n\nexport const VideoPlayer: React.ForwardRefExoticComponent<\n VideoPlayerProps & React.RefAttributes<VideoPlayerRef>\n> = forwardRef<VideoPlayerRef, VideoPlayerProps>(\n (\n {\n src,\n format = 'video/mp4',\n poster,\n muted,\n autoplay,\n width,\n height,\n playButtonComponent,\n onPlay,\n onPause,\n onEnded,\n onReady,\n },\n ref\n ) => {\n const videoContainerRef = useRef<HTMLDivElement>(null)\n const playerRef = useRef<Player | null>(null)\n\n const [isPlaying, setIsPlaying] = useState(false)\n\n // Expose imperative methods to parent components\n useImperativeHandle(\n ref,\n () => ({\n play: async () => {\n if (playerRef.current) {\n try {\n await playerRef.current.play()\n setIsPlaying(true)\n } catch (error) {\n console.error('Error playing video:', error)\n }\n }\n },\n pause: () => {\n if (playerRef.current) {\n playerRef.current.pause()\n setIsPlaying(false)\n }\n },\n }),\n []\n )\n\n const options = useMemo(\n () => ({\n controls: false,\n controlBar: {\n volumePanel: {\n inline: false,\n },\n },\n preload: 'auto',\n muted,\n // This makes sure that the video player does not take over the whole screen\n preferFullWindow: false,\n playsinline: true,\n poster,\n height,\n width,\n sources: [\n {\n src,\n type: format,\n },\n ],\n }),\n [muted, poster, height, width, src, format]\n )\n\n useEffect(() => {\n const player = playerRef.current\n\n return () => {\n if (player && !player.isDisposed()) {\n player.dispose()\n playerRef.current = null\n }\n }\n }, [playerRef])\n\n useEffect(() => {\n if (!playerRef.current) return\n\n const handlePlay = () => {\n onPlay?.()\n setIsPlaying(true)\n }\n const handlePause = () => {\n onPause?.()\n setIsPlaying(false)\n }\n const handleEnded = () => {\n onEnded?.()\n setIsPlaying(false)\n }\n\n playerRef.current.on('play', handlePlay)\n playerRef.current.on('pause', handlePause)\n playerRef.current.on('ended', handleEnded)\n\n return () => {\n if (playerRef.current) {\n playerRef.current.off('play', handlePlay)\n playerRef.current.off('pause', handlePause)\n playerRef.current.off('ended', handleEnded)\n }\n }\n }, [onEnded, onPause, onPlay])\n\n const togglePlayPause = useCallback(() => {\n if (isPlaying) {\n playerRef.current?.pause()\n setIsPlaying(false)\n } else {\n playerRef.current?.play()\n setIsPlaying(true)\n }\n }, [isPlaying])\n\n useEffect(() => {\n if (!playerRef.current) {\n const videoElement = document.createElement('video-js')\n\n // The Video.js player needs to be _inside_ the component element for React 18 Strict Mode.\n videoContainerRef.current?.appendChild(videoElement)\n\n playerRef.current = videojs(videoElement, options, () => {\n onReady && onReady()\n\n if (autoplay) {\n togglePlayPause()\n }\n })\n }\n }, [options, videoContainerRef, autoplay, onReady, togglePlayPause])\n\n const containerClassName = [\n width ? `w-${width}` : undefined,\n 'relative',\n ].join(' ')\n\n return (\n <div className=\"flex\">\n <div data-vjs-player className={containerClassName}>\n <div ref={videoContainerRef} />\n <div\n className=\"absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer\"\n onClick={togglePlayPause}\n aria-hidden\n >\n {isPlaying ? null : (playButtonComponent ?? <DefaultPlayIcon />)}\n </div>\n </div>\n </div>\n )\n }\n)\n\nconst DefaultPlayIcon = () => (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/20 cursor-pointer animate-in fade-in duration-200\">\n <svg\n viewBox=\"0 0 24 24\"\n className=\"w-10 h-10 text-white\"\n fill=\"currentColor\"\n >\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n </div>\n)\n"],"names":["VideoPlayer","forwardRef","src","format","poster","muted","autoplay","width","height","playButtonComponent","onPlay","onPause","onEnded","onReady","ref","videoContainerRef","useRef","playerRef","isPlaying","setIsPlaying","useState","useImperativeHandle","error","options","useMemo","useEffect","player","handlePlay","handlePause","handleEnded","togglePlayPause","useCallback","videoElement","videojs","containerClassName","jsx","DefaultPlayIcon"],"mappings":"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"}
1
+ {"version":3,"file":"video-player.js","sources":["../../../src/components/atoms/video-player.tsx"],"sourcesContent":["import {\n useRef,\n useEffect,\n useState,\n useCallback,\n forwardRef,\n useImperativeHandle,\n useMemo,\n} from 'react'\n\nimport videojs from 'video.js'\nimport Player from 'video.js/dist/types/player'\nimport 'video.js/dist/video-js.css'\n\nexport interface VideoPlayerRef {\n play: () => Promise<void>\n pause: () => void\n}\n\ninterface VideoPlayerProps {\n src: string\n /**\n * The format/MIME type of the video.\n * @default 'video/mp4'\n */\n format?: string\n muted?: boolean\n poster?: string\n autoplay?: boolean\n width?: number\n height?: number\n playButtonComponent?: React.ReactNode\n onPlay?: () => void\n onPause?: () => void\n onEnded?: () => void\n onReady?: () => void\n}\n\nexport const VideoPlayer: React.ForwardRefExoticComponent<\n VideoPlayerProps & React.RefAttributes<VideoPlayerRef>\n> = forwardRef<VideoPlayerRef, VideoPlayerProps>(\n (\n {\n src,\n format = 'video/mp4',\n poster,\n muted,\n autoplay,\n width,\n height,\n playButtonComponent,\n onPlay,\n onPause,\n onEnded,\n onReady,\n },\n ref\n ) => {\n const videoContainerRef = useRef<HTMLDivElement>(null)\n const playerRef = useRef<Player | null>(null)\n\n const [isPlaying, setIsPlaying] = useState(false)\n\n // Expose imperative methods to parent components\n useImperativeHandle(\n ref,\n () => ({\n play: async () => {\n if (playerRef.current) {\n try {\n await playerRef.current.play()\n setIsPlaying(true)\n } catch (error) {\n console.error('Error playing video:', error)\n }\n }\n },\n pause: () => {\n if (playerRef.current) {\n playerRef.current.pause()\n setIsPlaying(false)\n }\n },\n }),\n []\n )\n\n const options = useMemo(\n () => ({\n controls: false,\n controlBar: {\n volumePanel: {\n inline: false,\n },\n },\n preload: 'auto',\n muted,\n // This makes sure that the video player does not take over the whole screen\n preferFullWindow: false,\n playsinline: true,\n poster,\n height,\n width,\n sources: [\n {\n src,\n type: format,\n },\n ],\n }),\n [muted, poster, height, width, src, format]\n )\n\n useEffect(() => {\n const player = playerRef.current\n\n return () => {\n if (player && !player.isDisposed()) {\n player.dispose()\n playerRef.current = null\n }\n }\n }, [playerRef])\n\n useEffect(() => {\n if (!playerRef.current) return\n\n const handlePlay = () => {\n onPlay?.()\n setIsPlaying(true)\n }\n const handlePause = () => {\n onPause?.()\n setIsPlaying(false)\n }\n const handleEnded = () => {\n onEnded?.()\n setIsPlaying(false)\n }\n\n playerRef.current.on('play', handlePlay)\n playerRef.current.on('pause', handlePause)\n playerRef.current.on('ended', handleEnded)\n\n return () => {\n if (playerRef.current) {\n playerRef.current.off('play', handlePlay)\n playerRef.current.off('pause', handlePause)\n playerRef.current.off('ended', handleEnded)\n }\n }\n }, [onEnded, onPause, onPlay])\n\n const togglePlayPause = useCallback(() => {\n if (isPlaying) {\n playerRef.current?.pause()\n setIsPlaying(false)\n } else {\n playerRef.current?.play()\n setIsPlaying(true)\n }\n }, [isPlaying])\n\n useEffect(() => {\n if (!playerRef.current) {\n const videoElement = document.createElement('video-js')\n\n // The Video.js player needs to be _inside_ the component element for React 18 Strict Mode.\n videoContainerRef.current?.appendChild(videoElement)\n\n playerRef.current = videojs(videoElement, options, () => {\n onReady && onReady()\n\n if (autoplay) {\n togglePlayPause()\n }\n })\n }\n }, [options, videoContainerRef, autoplay, onReady, togglePlayPause])\n\n // Update muted state when prop changes after initial render\n useEffect(() => {\n if (playerRef.current && !playerRef.current.isDisposed()) {\n playerRef.current.muted(muted ?? false)\n }\n }, [muted])\n\n const containerClassName = [\n width ? `w-${width}` : undefined,\n 'relative',\n ].join(' ')\n\n return (\n <div className=\"flex\">\n <div data-vjs-player className={containerClassName}>\n <div ref={videoContainerRef} />\n <div\n className=\"absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer\"\n onClick={togglePlayPause}\n aria-hidden\n >\n {isPlaying ? null : (playButtonComponent ?? <DefaultPlayIcon />)}\n </div>\n </div>\n </div>\n )\n }\n)\n\nconst DefaultPlayIcon = () => (\n <div className=\"absolute inset-0 flex items-center justify-center bg-black/20 cursor-pointer animate-in fade-in duration-200\">\n <svg\n viewBox=\"0 0 24 24\"\n className=\"w-10 h-10 text-white\"\n fill=\"currentColor\"\n >\n <path d=\"M8 5v14l11-7z\" />\n </svg>\n </div>\n)\n"],"names":["VideoPlayer","forwardRef","src","format","poster","muted","autoplay","width","height","playButtonComponent","onPlay","onPause","onEnded","onReady","ref","videoContainerRef","useRef","playerRef","isPlaying","setIsPlaying","useState","useImperativeHandle","error","options","useMemo","useEffect","player","handlePlay","handlePause","handleEnded","togglePlayPause","useCallback","videoElement","videojs","containerClassName","jsx","DefaultPlayIcon"],"mappings":"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"}
@@ -1,17 +1,18 @@
1
- import { jsx as o, jsxs as v } from "react/jsx-runtime";
2
- import * as N from "react";
3
- import { useState as A, useCallback as C, useMemo as k, useContext as z } from "react";
4
- import { useShopNavigation as B } from "../../hooks/navigation/useShopNavigation.js";
1
+ import { jsx as o, jsxs as h } from "react/jsx-runtime";
2
+ import * as F from "react";
3
+ import { useState as R, useCallback as C, useMemo as k, useContext as z } from "react";
4
+ import { useShopNavigation as S } from "../../hooks/navigation/useShopNavigation.js";
5
5
  import { useSavedProductsActions as V } from "../../hooks/user/useSavedProductsActions.js";
6
- import { formatMoney as j } from "../../lib/formatMoney.js";
7
- import { cn as s } from "../../lib/utils.js";
6
+ import { ProductReviewStars as j } from "../../internal/components/product-review-stars.js";
7
+ import { formatMoney as B } from "../../lib/formatMoney.js";
8
+ import { cn as d } from "../../lib/utils.js";
8
9
  import { Image as T } from "../atoms/image.js";
9
10
  import { ProductVariantPrice as E } from "../atoms/product-variant-price.js";
10
11
  import { Touchable as O } from "../atoms/touchable.js";
11
- import { Badge as S } from "../ui/badge.js";
12
- import { FavoriteButton as L } from "./favorite-button.js";
13
- const b = N.createContext(void 0);
14
- function u() {
12
+ import { Badge as L } from "../ui/badge.js";
13
+ import { FavoriteButton as M } from "./favorite-button.js";
14
+ const b = F.createContext(void 0);
15
+ function s() {
15
16
  const t = z(b);
16
17
  if (!t)
17
18
  throw new Error(
@@ -19,14 +20,14 @@ function u() {
19
20
  );
20
21
  return t;
21
22
  }
22
- function M({
23
+ function q({
23
24
  className: t,
24
25
  ...e
25
26
  }) {
26
- const { touchable: r, onClick: a } = u(), n = /* @__PURE__ */ o(
27
+ const { touchable: r, onClick: a } = s(), n = /* @__PURE__ */ o(
27
28
  "div",
28
29
  {
29
- className: s(
30
+ className: d(
30
31
  "relative size-full overflow-hidden rounded-xl border-0",
31
32
  t
32
33
  ),
@@ -45,16 +46,16 @@ function M({
45
46
  }
46
47
  ) : n;
47
48
  }
48
- function R({
49
+ function D({
49
50
  className: t,
50
51
  ...e
51
52
  }) {
52
- const { variant: r } = u();
53
+ const { variant: r } = s();
53
54
  return /* @__PURE__ */ o(
54
55
  "div",
55
56
  {
56
57
  "data-slot": "product-card-image-container",
57
- className: s(
58
+ className: d(
58
59
  "relative overflow-hidden rounded-xl border border-gray-200",
59
60
  "aspect-square",
60
61
  r === "compact" ? "min-h-[104px]" : "min-h-[134px]",
@@ -64,18 +65,18 @@ function R({
64
65
  }
65
66
  );
66
67
  }
67
- function q({ className: t, ...e }) {
68
- const { product: r, selectedProductVariant: a } = u(), n = a?.image || r.featuredImage, i = n?.url, d = n?.altText || r.title, c = r.featuredImage?.thumbhash, p = C(
68
+ function G({ className: t, ...e }) {
69
+ const { product: r, selectedProductVariant: a } = s(), n = a?.image || r.featuredImage, i = n?.url, u = n?.altText || r.title, c = r.featuredImage?.thumbhash, p = C(
69
70
  (l) => c ? /* @__PURE__ */ o(
70
71
  T,
71
72
  {
72
73
  "data-slot": "product-card-image",
73
74
  src: l,
74
- alt: d,
75
+ alt: u,
75
76
  aspectRatio: 1,
76
77
  thumbhash: c,
77
78
  objectFit: "cover",
78
- className: s("size-full", t),
79
+ className: d("size-full", t),
79
80
  ...e
80
81
  }
81
82
  ) : /* @__PURE__ */ o(
@@ -83,36 +84,36 @@ function q({ className: t, ...e }) {
83
84
  {
84
85
  "data-slot": "product-card-image",
85
86
  src: l,
86
- alt: d,
87
- className: s("size-full object-cover", t),
87
+ alt: u,
88
+ className: d("size-full object-cover", t),
88
89
  ...e
89
90
  }
90
91
  ),
91
- [d, t, e, c]
92
+ [u, t, e, c]
92
93
  );
93
94
  return /* @__PURE__ */ o("div", { className: "bg-gray-100 flex items-center justify-center size-full", children: i ? p(i) : /* @__PURE__ */ o("div", { className: "text-gray-400 text-sm w-full text-center", children: "No Image" }) });
94
95
  }
95
- function w({
96
+ function N({
96
97
  className: t,
97
98
  position: e = "bottom-left",
98
99
  variant: r,
99
100
  children: a,
100
101
  ...n
101
102
  }) {
102
- const { badgeText: i, badgeVariant: d } = u(), c = a || i;
103
+ const { badgeText: i, badgeVariant: u } = s(), c = a || i;
103
104
  return c ? /* @__PURE__ */ o(
104
105
  "div",
105
106
  {
106
- className: s(
107
+ className: d(
107
108
  "absolute z-10",
108
109
  e === "top-left" ? "top-3 left-3" : "bottom-2 left-2"
109
110
  ),
110
111
  children: /* @__PURE__ */ o(
111
- S,
112
+ L,
112
113
  {
113
- variant: r ?? d ?? "none",
114
- className: s(
115
- !d && !r && "bg-black/50 text-white border-transparent",
114
+ variant: r ?? u ?? "none",
115
+ className: d(
116
+ !u && !r && "bg-black/50 text-white border-transparent",
116
117
  "rounded",
117
118
  t
118
119
  ),
@@ -123,35 +124,35 @@ function w({
123
124
  }
124
125
  ) : null;
125
126
  }
126
- function D({
127
+ function H({
127
128
  className: t,
128
129
  ...e
129
130
  }) {
130
- const { isFavorited: r, isFavoriteButtonDisabled: a, onFavoriteToggle: n } = u();
131
- return a ? null : /* @__PURE__ */ o("div", { className: s("absolute bottom-3 right-3 z-10", t), ...e, children: /* @__PURE__ */ o(L, { onClick: n, filled: r }) });
131
+ const { isFavorited: r, favoriteButtonDisabled: a, onFavoriteToggle: n } = s();
132
+ return a ? null : /* @__PURE__ */ o("div", { className: d("absolute bottom-3 right-3 z-10", t), ...e, children: /* @__PURE__ */ o(M, { onClick: n, filled: r }) });
132
133
  }
133
- function G({ className: t, ...e }) {
134
- const { variant: r } = u();
134
+ function J({ className: t, ...e }) {
135
+ const { variant: r } = s();
135
136
  return r !== "default" ? null : /* @__PURE__ */ o(
136
137
  "div",
137
138
  {
138
- "data-slot": "product-card-info",
139
- className: s("px-1 pt-2 pb-0 space-y-1", t),
139
+ "data-testid": "product-card-info",
140
+ className: d("px-1 pt-2 pb-0 space-y-1", t),
140
141
  ...e
141
142
  }
142
143
  );
143
144
  }
144
- function H({
145
+ function K({
145
146
  className: t,
146
147
  children: e,
147
148
  ...r
148
149
  }) {
149
- const { product: a } = u();
150
+ const { product: a } = s();
150
151
  return /* @__PURE__ */ o(
151
152
  "h3",
152
153
  {
153
154
  "data-slot": "product-card-title",
154
- className: s(
155
+ className: d(
155
156
  "text-sm font-medium leading-tight text-gray-900",
156
157
  "truncate overflow-hidden whitespace-nowrap text-ellipsis",
157
158
  t
@@ -161,8 +162,29 @@ function H({
161
162
  }
162
163
  );
163
164
  }
164
- function J({ className: t }) {
165
- const { product: e, selectedProductVariant: r } = u(), a = r?.price || e?.price, n = r?.compareAtPrice || e?.compareAtPrice;
165
+ function Q({
166
+ className: t,
167
+ ...e
168
+ }) {
169
+ const { product: r, reviewsDisabled: a } = s(), n = r.reviewAnalytics;
170
+ return a || !n?.averageRating ? null : /* @__PURE__ */ o(
171
+ "div",
172
+ {
173
+ "data-slot": "product-card-review-stars",
174
+ className: d("", t),
175
+ ...e,
176
+ children: /* @__PURE__ */ o(
177
+ j,
178
+ {
179
+ averageRating: n.averageRating,
180
+ reviewCount: n.reviewCount
181
+ }
182
+ )
183
+ }
184
+ );
185
+ }
186
+ function U({ className: t }) {
187
+ const { product: e, selectedProductVariant: r } = s(), a = r?.price || e?.price, n = r?.compareAtPrice || e?.compareAtPrice;
166
188
  return /* @__PURE__ */ o(
167
189
  E,
168
190
  {
@@ -174,43 +196,44 @@ function J({ className: t }) {
174
196
  }
175
197
  );
176
198
  }
177
- function K() {
178
- const { product: t, selectedProductVariant: e, variant: r } = u();
199
+ function W() {
200
+ const { product: t, selectedProductVariant: e, variant: r } = s();
179
201
  if (r !== "priceOverlay") return null;
180
202
  const a = e?.price || t.price, n = a?.currencyCode, i = a?.amount;
181
- return !n || !i ? null : /* @__PURE__ */ o(w, { position: "top-left", children: j(i, n) });
203
+ return !n || !i ? null : /* @__PURE__ */ o(N, { position: "top-left", children: B(i, n) });
182
204
  }
183
- function at({
205
+ function dt({
184
206
  product: t,
185
207
  selectedProductVariant: e,
186
208
  variant: r = "default",
187
209
  touchable: a = !0,
188
210
  badgeText: n,
189
211
  badgeVariant: i,
190
- onProductClick: d,
212
+ onProductClick: u,
191
213
  onFavoriteToggled: c,
192
214
  children: p,
193
- favoriteButtonDisabled: l = !1
215
+ favoriteButtonDisabled: l = !1,
216
+ reviewsDisabled: v = !1
194
217
  }) {
195
- const { navigateToProduct: h } = B(), { saveProduct: g, unsaveProduct: P } = V(), [f, y] = A(t.isFavorited), x = C(() => {
196
- a && (d?.(), h({
218
+ const { navigateToProduct: g } = S(), { saveProduct: P, unsaveProduct: y } = V(), [f, x] = R(t.isFavorited), w = C(() => {
219
+ a && (u?.(), g({
197
220
  productId: t.id
198
221
  }));
199
- }, [h, t.id, a, d]), I = C(async () => {
222
+ }, [g, t.id, a, u]), I = C(async () => {
200
223
  const m = f;
201
- y(!m), c?.(!m);
224
+ x(!m), c?.(!m);
202
225
  try {
203
- m ? await P({
226
+ m ? await y({
204
227
  productId: t.id,
205
228
  shopId: t.shop.id,
206
229
  productVariantId: e?.id || t.defaultVariantId
207
- }) : await g({
230
+ }) : await P({
208
231
  productId: t.id,
209
232
  shopId: t.shop.id,
210
233
  productVariantId: e?.id || t.defaultVariantId
211
234
  });
212
235
  } catch {
213
- y(m), c?.(m);
236
+ x(m), c?.(m);
214
237
  }
215
238
  }, [
216
239
  f,
@@ -218,10 +241,10 @@ function at({
218
241
  t.shop.id,
219
242
  t.defaultVariantId,
220
243
  e?.id,
221
- g,
222
244
  P,
245
+ y,
223
246
  c
224
- ]), F = k(
247
+ ]), A = k(
225
248
  () => ({
226
249
  // Core data
227
250
  product: t,
@@ -231,11 +254,12 @@ function at({
231
254
  touchable: a,
232
255
  badgeText: n,
233
256
  badgeVariant: i,
257
+ favoriteButtonDisabled: l,
258
+ reviewsDisabled: v,
234
259
  // State
235
260
  isFavorited: f,
236
- isFavoriteButtonDisabled: l,
237
261
  // Actions
238
- onClick: x,
262
+ onClick: w,
239
263
  onFavoriteToggle: I
240
264
  }),
241
265
  [
@@ -246,33 +270,36 @@ function at({
246
270
  n,
247
271
  i,
248
272
  f,
249
- x,
273
+ w,
250
274
  I,
251
- l
275
+ l,
276
+ v
252
277
  ]
253
278
  );
254
- return /* @__PURE__ */ o(b.Provider, { value: F, children: p ?? /* @__PURE__ */ v(M, { children: [
255
- /* @__PURE__ */ v(R, { children: [
256
- /* @__PURE__ */ o(q, {}),
257
- r === "priceOverlay" && /* @__PURE__ */ o(K, {}),
258
- /* @__PURE__ */ o(w, {}),
259
- /* @__PURE__ */ o(D, {})
279
+ return /* @__PURE__ */ o(b.Provider, { value: A, children: p ?? /* @__PURE__ */ h(q, { children: [
280
+ /* @__PURE__ */ h(D, { children: [
281
+ /* @__PURE__ */ o(G, {}),
282
+ r === "priceOverlay" && /* @__PURE__ */ o(W, {}),
283
+ /* @__PURE__ */ o(N, {}),
284
+ /* @__PURE__ */ o(H, {})
260
285
  ] }),
261
- r === "default" && /* @__PURE__ */ v(G, { children: [
262
- /* @__PURE__ */ o(H, {}),
263
- /* @__PURE__ */ o(J, {})
286
+ r === "default" && /* @__PURE__ */ h(J, { children: [
287
+ /* @__PURE__ */ o(K, {}),
288
+ /* @__PURE__ */ o(Q, {}),
289
+ /* @__PURE__ */ o(U, {})
264
290
  ] })
265
291
  ] }) });
266
292
  }
267
293
  export {
268
- at as ProductCard,
269
- w as ProductCardBadge,
270
- M as ProductCardContainer,
271
- D as ProductCardFavoriteButton,
272
- q as ProductCardImage,
273
- R as ProductCardImageContainer,
274
- G as ProductCardInfo,
275
- J as ProductCardPrice,
276
- H as ProductCardTitle
294
+ dt as ProductCard,
295
+ N as ProductCardBadge,
296
+ q as ProductCardContainer,
297
+ H as ProductCardFavoriteButton,
298
+ G as ProductCardImage,
299
+ D as ProductCardImageContainer,
300
+ J as ProductCardInfo,
301
+ U as ProductCardPrice,
302
+ Q as ProductCardReviewStars,
303
+ K as ProductCardTitle
277
304
  };
278
305
  //# sourceMappingURL=product-card.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n\n // State\n isFavorited: boolean\n isFavoriteButtonDisabled: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative size-full overflow-hidden rounded-xl border-0',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, isFavoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (isFavoriteButtonDisabled) return null\n\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n favoriteButtonDisabled = false,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n\n // State\n isFavorited: isFavoritedLocal,\n isFavoriteButtonDisabled: favoriteButtonDisabled,\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n favoriteButtonDisabled,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","isFavoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","favoriteButtonDisabled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"product-card.js","sources":["../../../src/components/commerce/product-card.tsx"],"sourcesContent":["import * as React from 'react'\nimport {useCallback, useContext, useMemo, useState} from 'react'\n\nimport {type Product, type ProductVariant} from '@shopify/shop-minis-platform'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {formatMoney} from '../../lib/formatMoney'\nimport {cn} from '../../lib/utils'\nimport {Image} from '../atoms/image'\nimport {ProductVariantPrice} from '../atoms/product-variant-price'\nimport {Touchable} from '../atoms/touchable'\nimport {Badge} from '../ui/badge'\n\nimport {FavoriteButton} from './favorite-button'\n\n// Context definition\ninterface ProductCardContextValue {\n // Core data\n product: Product\n selectedProductVariant?: ProductVariant\n\n // UI configuration\n variant: 'default' | 'priceOverlay' | 'compact'\n touchable: boolean\n badgeText?: string\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n favoriteButtonDisabled: boolean\n reviewsDisabled: boolean\n\n // State\n isFavorited: boolean\n\n // Actions\n onClick: () => void\n onFavoriteToggle: () => void\n}\n\nconst ProductCardContext = React.createContext<\n ProductCardContextValue | undefined\n>(undefined)\n\nfunction useProductCardContext() {\n const context = useContext(ProductCardContext)\n if (!context) {\n throw new Error(\n 'ProductCard components must be used within a ProductCard provider'\n )\n }\n return context\n}\n\n// Primitive components (building blocks)\nfunction ProductCardContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {touchable, onClick} = useProductCardContext()\n\n const content = (\n <div\n className={cn(\n 'relative size-full overflow-hidden rounded-xl border-0',\n className\n )}\n {...props}\n />\n )\n\n if (touchable && onClick) {\n return (\n <Touchable\n onClick={onClick}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {content}\n </Touchable>\n )\n }\n\n return content\n}\n\nfunction ProductCardImageContainer({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n\n return (\n <div\n data-slot=\"product-card-image-container\"\n className={cn(\n 'relative overflow-hidden rounded-xl border border-gray-200',\n 'aspect-square',\n variant === 'compact' ? 'min-h-[104px]' : 'min-h-[134px]',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductCardImage({className, ...props}: React.ComponentProps<'img'>) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive display image locally\n const displayImage = selectedProductVariant?.image || product.featuredImage\n const src = displayImage?.url\n const alt = displayImage?.altText || product.title\n const thumbhash = product.featuredImage?.thumbhash\n\n const renderImageElement = useCallback(\n (src: string) => {\n const imageElement = thumbhash ? (\n <Image\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n aspectRatio={1}\n thumbhash={thumbhash}\n objectFit=\"cover\"\n className={cn('size-full', className)}\n {...props}\n />\n ) : (\n <img\n data-slot=\"product-card-image\"\n src={src}\n alt={alt}\n className={cn('size-full object-cover', className)}\n {...props}\n />\n )\n\n return imageElement\n },\n [alt, className, props, thumbhash]\n )\n\n return (\n <div className=\"bg-gray-100 flex items-center justify-center size-full\">\n {src ? (\n renderImageElement(src)\n ) : (\n <div className=\"text-gray-400 text-sm w-full text-center\">No Image</div>\n )}\n </div>\n )\n}\n\nfunction ProductCardBadge({\n className,\n position = 'bottom-left',\n variant,\n children,\n ...props\n}: React.ComponentProps<typeof Badge> & {\n position?: 'top-left' | 'bottom-left'\n}) {\n const {badgeText, badgeVariant} = useProductCardContext()\n // If no children provided, use badgeText from context\n const content = children || badgeText\n\n if (!content) return null\n\n return (\n <div\n className={cn(\n 'absolute z-10',\n position === 'top-left' ? 'top-3 left-3' : 'bottom-2 left-2'\n )}\n >\n <Badge\n variant={variant ?? badgeVariant ?? 'none'}\n className={cn(\n !badgeVariant &&\n !variant &&\n 'bg-black/50 text-white border-transparent',\n 'rounded',\n className\n )}\n {...props}\n >\n {content}\n </Badge>\n </div>\n )\n}\n\nfunction ProductCardFavoriteButton({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {isFavorited, favoriteButtonDisabled, onFavoriteToggle} =\n useProductCardContext()\n if (favoriteButtonDisabled) return null\n\n return (\n <div className={cn('absolute bottom-3 right-3 z-10', className)} {...props}>\n <FavoriteButton onClick={onFavoriteToggle} filled={isFavorited} />\n </div>\n )\n}\n\nfunction ProductCardInfo({className, ...props}: React.ComponentProps<'div'>) {\n const {variant} = useProductCardContext()\n if (variant !== 'default') {\n return null\n }\n\n return (\n <div\n data-testid=\"product-card-info\"\n className={cn('px-1 pt-2 pb-0 space-y-1', className)}\n {...props}\n />\n )\n}\n\nfunction ProductCardTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n const {product} = useProductCardContext()\n return (\n <h3\n data-slot=\"product-card-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900',\n 'truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children || product.title}\n </h3>\n )\n}\n\nfunction ProductCardReviewStars({\n className,\n ...props\n}: React.ComponentProps<'div'>) {\n const {product, reviewsDisabled} = useProductCardContext()\n const reviewAnalytics = product.reviewAnalytics\n\n if (reviewsDisabled || !reviewAnalytics?.averageRating) {\n return null\n }\n\n return (\n <div\n data-slot=\"product-card-review-stars\"\n className={cn('', className)}\n {...props}\n >\n <ProductReviewStars\n averageRating={reviewAnalytics.averageRating}\n reviewCount={reviewAnalytics.reviewCount}\n />\n </div>\n )\n}\n\nfunction ProductCardPrice({className}: {className?: string}) {\n const {product, selectedProductVariant} = useProductCardContext()\n\n // Derive price data locally\n const displayPrice = selectedProductVariant?.price || product?.price\n const displayCompareAtPrice =\n selectedProductVariant?.compareAtPrice || product?.compareAtPrice\n\n return (\n <ProductVariantPrice\n amount={displayPrice?.amount || ''}\n currencyCode={displayPrice?.currencyCode || ''}\n compareAtPriceAmount={displayCompareAtPrice?.amount}\n compareAtPriceCurrencyCode={displayCompareAtPrice?.currencyCode}\n className={className}\n />\n )\n}\n\n// Special PriceOverlayBadge for price overlay variant\nfunction ProductCardPriceOverlayBadge() {\n const {product, selectedProductVariant, variant} = useProductCardContext()\n if (variant !== 'priceOverlay') return null\n const displayPrice = selectedProductVariant?.price || product.price\n const currencyCode = displayPrice?.currencyCode\n const amount = displayPrice?.amount\n\n if (!currencyCode || !amount) return null\n return (\n <ProductCardBadge position=\"top-left\">\n {formatMoney(amount, currencyCode)}\n </ProductCardBadge>\n )\n}\n\nexport interface ProductCardProps {\n /** The product to display in the card */\n product: Product\n /** Optional selected variant of the product to show specific variant data */\n selectedProductVariant?: ProductVariant\n /** Visual style variant of the card */\n variant?: 'default' | 'priceOverlay' | 'compact'\n /** Whether the card can be clicked/tapped to navigate to product details */\n touchable?: boolean\n /** Optional text to display in a badge on the card */\n badgeText?: string\n /** Visual style variant for the badge */\n badgeVariant?: 'primary' | 'secondary' | 'destructive' | 'outline' | 'none'\n /** Callback fired when the product is clicked */\n onProductClick?: () => void\n /** Callback fired when the favorite button is toggled */\n onFavoriteToggled?: (isFavorited: boolean) => void\n /** Custom layout via children */\n children?: React.ReactNode\n /** Whether the favorite button is disabled */\n favoriteButtonDisabled?: boolean\n /** Whether review stars are disabled */\n reviewsDisabled?: boolean\n}\n\nfunction ProductCard({\n product,\n selectedProductVariant,\n variant = 'default',\n touchable = true,\n badgeText,\n badgeVariant,\n onProductClick,\n onFavoriteToggled,\n children,\n favoriteButtonDisabled = false,\n reviewsDisabled = false,\n}: ProductCardProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = useState(product.isFavorited)\n\n const handleClick = useCallback(() => {\n if (!touchable) return\n\n onProductClick?.()\n\n navigateToProduct({\n productId: product.id,\n })\n }, [navigateToProduct, product.id, touchable, onProductClick])\n\n const handleFavoriteClick = useCallback(async () => {\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n onFavoriteToggled?.(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: product.id,\n shopId: product.shop.id,\n productVariantId:\n selectedProductVariant?.id || product.defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n onFavoriteToggled?.(previousState)\n }\n }, [\n isFavoritedLocal,\n product.id,\n product.shop.id,\n product.defaultVariantId,\n selectedProductVariant?.id,\n saveProduct,\n unsaveProduct,\n onFavoriteToggled,\n ])\n\n const contextValue = useMemo<ProductCardContextValue>(\n () => ({\n // Core data\n product,\n selectedProductVariant,\n\n // UI configuration\n variant,\n touchable,\n badgeText,\n badgeVariant,\n favoriteButtonDisabled,\n reviewsDisabled,\n\n // State\n isFavorited: isFavoritedLocal,\n // Actions\n onClick: handleClick,\n onFavoriteToggle: handleFavoriteClick,\n }),\n [\n product,\n selectedProductVariant,\n variant,\n touchable,\n badgeText,\n badgeVariant,\n isFavoritedLocal,\n handleClick,\n handleFavoriteClick,\n favoriteButtonDisabled,\n reviewsDisabled,\n ]\n )\n\n return (\n <ProductCardContext.Provider value={contextValue}>\n {children ?? (\n <ProductCardContainer>\n <ProductCardImageContainer>\n <ProductCardImage />\n {variant === 'priceOverlay' && <ProductCardPriceOverlayBadge />}\n <ProductCardBadge />\n <ProductCardFavoriteButton />\n </ProductCardImageContainer>\n {variant === 'default' && (\n <ProductCardInfo>\n <ProductCardTitle />\n <ProductCardReviewStars />\n <ProductCardPrice />\n </ProductCardInfo>\n )}\n </ProductCardContainer>\n )}\n </ProductCardContext.Provider>\n )\n}\n\nexport {\n ProductCard,\n ProductCardContainer,\n ProductCardImageContainer,\n ProductCardImage,\n ProductCardBadge,\n ProductCardFavoriteButton,\n ProductCardInfo,\n ProductCardTitle,\n ProductCardReviewStars,\n ProductCardPrice,\n}\n"],"names":["ProductCardContext","React","useProductCardContext","context","useContext","ProductCardContainer","className","props","touchable","onClick","content","jsx","cn","Touchable","ProductCardImageContainer","variant","ProductCardImage","product","selectedProductVariant","displayImage","src","alt","thumbhash","renderImageElement","useCallback","Image","ProductCardBadge","position","children","badgeText","badgeVariant","Badge","ProductCardFavoriteButton","isFavorited","favoriteButtonDisabled","onFavoriteToggle","FavoriteButton","ProductCardInfo","ProductCardTitle","ProductCardReviewStars","reviewsDisabled","reviewAnalytics","ProductReviewStars","ProductCardPrice","displayPrice","displayCompareAtPrice","ProductVariantPrice","ProductCardPriceOverlayBadge","currencyCode","amount","formatMoney","ProductCard","onProductClick","onFavoriteToggled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","isFavoritedLocal","setIsFavoritedLocal","useState","handleClick","handleFavoriteClick","previousState","contextValue","useMemo","jsxs"],"mappings":"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"}