@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.
- package/dist/components/atoms/content-wrapper.js.map +1 -1
- package/dist/components/atoms/video-player.js +28 -26
- package/dist/components/atoms/video-player.js.map +1 -1
- package/dist/components/commerce/product-card.js +106 -79
- package/dist/components/commerce/product-card.js.map +1 -1
- package/dist/components/commerce/product-link.js +124 -137
- package/dist/components/commerce/product-link.js.map +1 -1
- package/dist/components/commerce/search.js +20 -20
- package/dist/components/commerce/search.js.map +1 -1
- package/dist/components/ui/sonner.js +3 -1
- package/dist/components/ui/sonner.js.map +1 -1
- package/dist/hooks/navigation/useNavigateWithTransition.js +10 -11
- package/dist/hooks/navigation/useNavigateWithTransition.js.map +1 -1
- package/dist/index.js +269 -264
- package/dist/index.js.map +1 -1
- package/dist/internal/components/product-review-stars.js +78 -0
- package/dist/internal/components/product-review-stars.js.map +1 -0
- package/dist/mocks.js +178 -107
- package/dist/mocks.js.map +1 -1
- 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
- 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
- package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +1 -1
- 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
- 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
- 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
- package/eslint/rules/prefer-sdk-hooks.cjs +9 -11
- package/package.json +1 -1
- package/src/components/atoms/content-wrapper.tsx +1 -1
- package/src/components/atoms/video-player.tsx +7 -0
- package/src/components/commerce/product-card.test.tsx +135 -0
- package/src/components/commerce/product-card.tsx +39 -5
- package/src/components/commerce/product-link.test.tsx +15 -3
- package/src/components/commerce/product-link.tsx +9 -25
- package/src/components/commerce/search.tsx +2 -2
- package/src/components/index.ts +1 -0
- package/src/components/ui/sonner.tsx +2 -2
- package/src/hooks/navigation/useNavigateWithTransition.test.ts +46 -7
- package/src/hooks/navigation/useNavigateWithTransition.ts +4 -1
- package/src/internal/components/product-review-stars.test.tsx +90 -0
- package/src/internal/components/product-review-stars.tsx +113 -0
- package/src/mocks.ts +10 -2
- 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
|
|
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:
|
|
8
|
-
format:
|
|
9
|
-
poster:
|
|
10
|
-
muted:
|
|
7
|
+
src: f,
|
|
8
|
+
format: d = "video/mp4",
|
|
9
|
+
poster: p,
|
|
10
|
+
muted: l,
|
|
11
11
|
autoplay: m,
|
|
12
|
-
width:
|
|
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:
|
|
18
|
+
onReady: c
|
|
19
19
|
}, b) => {
|
|
20
|
-
const
|
|
21
|
-
|
|
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 =
|
|
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:
|
|
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:
|
|
51
|
+
poster: p,
|
|
52
52
|
height: v,
|
|
53
|
-
width:
|
|
53
|
+
width: s,
|
|
54
54
|
sources: [
|
|
55
55
|
{
|
|
56
|
-
src:
|
|
57
|
-
type:
|
|
56
|
+
src: f,
|
|
57
|
+
type: d
|
|
58
58
|
}
|
|
59
59
|
]
|
|
60
60
|
}),
|
|
61
|
-
[
|
|
61
|
+
[l, p, v, s, f, d]
|
|
62
62
|
);
|
|
63
|
-
|
|
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]),
|
|
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
|
|
81
|
+
const u = M(() => {
|
|
82
82
|
o ? (e.current?.pause(), n(!1)) : (e.current?.play(), n(!0));
|
|
83
83
|
}, [o]);
|
|
84
|
-
|
|
84
|
+
a(() => {
|
|
85
85
|
if (!e.current) {
|
|
86
86
|
const r = document.createElement("video-js");
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
i.current?.appendChild(r), e.current = R(r, x, () => {
|
|
88
|
+
c && c(), m && u();
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
|
-
}, [x,
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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":";;;;AAsCO,MAAMA,IAETC;AAAA,EACF,CACE;AAAA,IACE,KAAAC;AAAA,IACA,QAAAC,IAAS;AAAA,IACT,QAAAC;AAAA,IACA,OAAAC;AAAA,IACA,UAAAC;AAAA,IACA,OAAAC;AAAA,IACA,QAAAC;AAAA,IACA,qBAAAC;AAAA,IACA,QAAAC;AAAA,IACA,SAAAC;AAAA,IACA,SAAAC;AAAA,IACA,SAAAC;AAAA,KAEFC,MACG;AACG,UAAAC,IAAoBC,EAAuB,IAAI,GAC/CC,IAAYD,EAAsB,IAAI,GAEtC,CAACE,GAAWC,CAAY,IAAIC,EAAS,EAAK;AAGhD,IAAAC;AAAA,MACEP;AAAA,MACA,OAAO;AAAA,QACL,MAAM,YAAY;AAChB,cAAIG,EAAU;AACR,gBAAA;AACI,oBAAAA,EAAU,QAAQ,KAAK,GAC7BE,EAAa,EAAI;AAAA,qBACVG,GAAO;AACN,sBAAA,MAAM,wBAAwBA,CAAK;AAAA,YAAA;AAAA,QAGjD;AAAA,QACA,OAAO,MAAM;AACX,UAAIL,EAAU,YACZA,EAAU,QAAQ,MAAM,GACxBE,EAAa,EAAK;AAAA,QACpB;AAAA,MACF;AAAA,MAEF,CAAA;AAAA,IACF;AAEA,UAAMI,IAAUC;AAAA,MACd,OAAO;AAAA,QACL,UAAU;AAAA,QACV,YAAY;AAAA,UACV,aAAa;AAAA,YACX,QAAQ;AAAA,UAAA;AAAA,QAEZ;AAAA,QACA,SAAS;AAAA,QACT,OAAAnB;AAAA;AAAA,QAEA,kBAAkB;AAAA,QAClB,aAAa;AAAA,QACb,QAAAD;AAAA,QACA,QAAAI;AAAA,QACA,OAAAD;AAAA,QACA,SAAS;AAAA,UACP;AAAA,YACE,KAAAL;AAAA,YACA,MAAMC;AAAA,UAAA;AAAA,QACR;AAAA,MACF;AAAA,MAEF,CAACE,GAAOD,GAAQI,GAAQD,GAAOL,GAAKC,CAAM;AAAA,IAC5C;AAEA,IAAAsB,EAAU,MAAM;AACd,YAAMC,IAAST,EAAU;AAEzB,aAAO,MAAM;AACX,QAAIS,KAAU,CAACA,EAAO,iBACpBA,EAAO,QAAQ,GACfT,EAAU,UAAU;AAAA,MAExB;AAAA,IAAA,GACC,CAACA,CAAS,CAAC,GAEdQ,EAAU,MAAM;AACV,UAAA,CAACR,EAAU,QAAS;AAExB,YAAMU,IAAa,MAAM;AACd,QAAAjB,IAAA,GACTS,EAAa,EAAI;AAAA,MACnB,GACMS,IAAc,MAAM;AACd,QAAAjB,IAAA,GACVQ,EAAa,EAAK;AAAA,MACpB,GACMU,IAAc,MAAM;AACd,QAAAjB,IAAA,GACVO,EAAa,EAAK;AAAA,MACpB;AAEU,aAAAF,EAAA,QAAQ,GAAG,QAAQU,CAAU,GAC7BV,EAAA,QAAQ,GAAG,SAASW,CAAW,GAC/BX,EAAA,QAAQ,GAAG,SAASY,CAAW,GAElC,MAAM;AACX,QAAIZ,EAAU,YACFA,EAAA,QAAQ,IAAI,QAAQU,CAAU,GAC9BV,EAAA,QAAQ,IAAI,SAASW,CAAW,GAChCX,EAAA,QAAQ,IAAI,SAASY,CAAW;AAAA,MAE9C;AAAA,IACC,GAAA,CAACjB,GAASD,GAASD,CAAM,CAAC;AAEvB,UAAAoB,IAAkBC,EAAY,MAAM;AACxC,MAAIb,KACFD,EAAU,SAAS,MAAM,GACzBE,EAAa,EAAK,MAElBF,EAAU,SAAS,KAAK,GACxBE,EAAa,EAAI;AAAA,IACnB,GACC,CAACD,CAAS,CAAC;AAEd,IAAAO,EAAU,MAAM;AACV,UAAA,CAACR,EAAU,SAAS;AAChB,cAAAe,IAAe,SAAS,cAAc,UAAU;AAGpC,QAAAjB,EAAA,SAAS,YAAYiB,CAAY,GAEnDf,EAAU,UAAUgB,EAAQD,GAAcT,GAAS,MAAM;AACvD,UAAAV,KAAWA,EAAQ,GAEfP,KACcwB,EAAA;AAAA,QAClB,CACD;AAAA,MAAA;AAAA,IACH,GACC,CAACP,GAASR,GAAmBT,GAAUO,GAASiB,CAAe,CAAC;
|
|
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
|
|
2
|
-
import * as
|
|
3
|
-
import { useState as
|
|
4
|
-
import { useShopNavigation as
|
|
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 {
|
|
7
|
-
import {
|
|
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
|
|
12
|
-
import { FavoriteButton as
|
|
13
|
-
const b =
|
|
14
|
-
function
|
|
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
|
|
23
|
+
function q({
|
|
23
24
|
className: t,
|
|
24
25
|
...e
|
|
25
26
|
}) {
|
|
26
|
-
const { touchable: r, onClick: a } =
|
|
27
|
+
const { touchable: r, onClick: a } = s(), n = /* @__PURE__ */ o(
|
|
27
28
|
"div",
|
|
28
29
|
{
|
|
29
|
-
className:
|
|
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
|
|
49
|
+
function D({
|
|
49
50
|
className: t,
|
|
50
51
|
...e
|
|
51
52
|
}) {
|
|
52
|
-
const { variant: r } =
|
|
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:
|
|
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
|
|
68
|
-
const { product: r, selectedProductVariant: a } =
|
|
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:
|
|
75
|
+
alt: u,
|
|
75
76
|
aspectRatio: 1,
|
|
76
77
|
thumbhash: c,
|
|
77
78
|
objectFit: "cover",
|
|
78
|
-
className:
|
|
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:
|
|
87
|
-
className:
|
|
87
|
+
alt: u,
|
|
88
|
+
className: d("size-full object-cover", t),
|
|
88
89
|
...e
|
|
89
90
|
}
|
|
90
91
|
),
|
|
91
|
-
[
|
|
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
|
|
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:
|
|
103
|
+
const { badgeText: i, badgeVariant: u } = s(), c = a || i;
|
|
103
104
|
return c ? /* @__PURE__ */ o(
|
|
104
105
|
"div",
|
|
105
106
|
{
|
|
106
|
-
className:
|
|
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
|
-
|
|
112
|
+
L,
|
|
112
113
|
{
|
|
113
|
-
variant: r ??
|
|
114
|
-
className:
|
|
115
|
-
!
|
|
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
|
|
127
|
+
function H({
|
|
127
128
|
className: t,
|
|
128
129
|
...e
|
|
129
130
|
}) {
|
|
130
|
-
const { isFavorited: r,
|
|
131
|
-
return a ? null : /* @__PURE__ */ o("div", { className:
|
|
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
|
|
134
|
-
const { variant: r } =
|
|
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-
|
|
139
|
-
className:
|
|
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
|
|
145
|
+
function K({
|
|
145
146
|
className: t,
|
|
146
147
|
children: e,
|
|
147
148
|
...r
|
|
148
149
|
}) {
|
|
149
|
-
const { product: a } =
|
|
150
|
+
const { product: a } = s();
|
|
150
151
|
return /* @__PURE__ */ o(
|
|
151
152
|
"h3",
|
|
152
153
|
{
|
|
153
154
|
"data-slot": "product-card-title",
|
|
154
|
-
className:
|
|
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
|
|
165
|
-
|
|
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
|
|
178
|
-
const { product: t, selectedProductVariant: e, variant: r } =
|
|
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(
|
|
203
|
+
return !n || !i ? null : /* @__PURE__ */ o(N, { position: "top-left", children: B(i, n) });
|
|
182
204
|
}
|
|
183
|
-
function
|
|
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:
|
|
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:
|
|
196
|
-
a && (
|
|
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
|
-
}, [
|
|
222
|
+
}, [g, t.id, a, u]), I = C(async () => {
|
|
200
223
|
const m = f;
|
|
201
|
-
|
|
224
|
+
x(!m), c?.(!m);
|
|
202
225
|
try {
|
|
203
|
-
m ? await
|
|
226
|
+
m ? await y({
|
|
204
227
|
productId: t.id,
|
|
205
228
|
shopId: t.shop.id,
|
|
206
229
|
productVariantId: e?.id || t.defaultVariantId
|
|
207
|
-
}) : await
|
|
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
|
-
|
|
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
|
-
]),
|
|
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:
|
|
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
|
-
|
|
273
|
+
w,
|
|
250
274
|
I,
|
|
251
|
-
l
|
|
275
|
+
l,
|
|
276
|
+
v
|
|
252
277
|
]
|
|
253
278
|
);
|
|
254
|
-
return /* @__PURE__ */ o(b.Provider, { value:
|
|
255
|
-
/* @__PURE__ */
|
|
256
|
-
/* @__PURE__ */ o(
|
|
257
|
-
r === "priceOverlay" && /* @__PURE__ */ o(
|
|
258
|
-
/* @__PURE__ */ o(
|
|
259
|
-
/* @__PURE__ */ o(
|
|
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__ */
|
|
262
|
-
/* @__PURE__ */ o(
|
|
263
|
-
/* @__PURE__ */ o(
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
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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"}
|