@shopify/shop-minis-react 0.13.2 → 0.15.0

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 (70) hide show
  1. package/dist/_virtual/index2.js +4 -4
  2. package/dist/_virtual/index3.js +2 -5
  3. package/dist/_virtual/index3.js.map +1 -1
  4. package/dist/_virtual/index4.js +2 -2
  5. package/dist/_virtual/index5.js +3 -2
  6. package/dist/_virtual/index5.js.map +1 -1
  7. package/dist/_virtual/index6.js +2 -2
  8. package/dist/_virtual/index7.js +2 -3
  9. package/dist/_virtual/index7.js.map +1 -1
  10. package/dist/_virtual/index8.js +2 -2
  11. package/dist/components/atoms/list.js.map +1 -1
  12. package/dist/components/commerce/product-link.js.map +1 -1
  13. package/dist/components/commerce/search.js.map +1 -1
  14. package/dist/components/navigation/transition-link.js.map +1 -1
  15. package/dist/hooks/intents/useIntent.js +43 -0
  16. package/dist/hooks/intents/useIntent.js.map +1 -0
  17. package/dist/index.js +45 -43
  18. package/dist/index.js.map +1 -1
  19. package/dist/internal/components/product-review-stars.js.map +1 -1
  20. package/dist/shop-minis-react/node_modules/.pnpm/@radix-ui_react-use-is-hydrated@0.1.0_@types_react@19.1.6_react@19.1.0/node_modules/@radix-ui/react-use-is-hydrated/dist/index.js +1 -1
  21. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +1 -1
  22. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +1 -1
  23. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/conversions.js +422 -0
  24. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/conversions.js.map +1 -0
  25. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/index.js +36 -0
  26. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/index.js.map +1 -0
  27. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/route.js +47 -0
  28. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@3.1.3/node_modules/color-convert/route.js.map +1 -0
  29. package/dist/shop-minis-react/node_modules/.pnpm/color-name@2.1.0/node_modules/color-name/index.js +156 -0
  30. package/dist/shop-minis-react/node_modules/.pnpm/color-name@2.1.0/node_modules/color-name/index.js.map +1 -0
  31. package/dist/shop-minis-react/node_modules/.pnpm/color-string@2.1.4/node_modules/color-string/index.js +106 -0
  32. package/dist/shop-minis-react/node_modules/.pnpm/color-string@2.1.4/node_modules/color-string/index.js.map +1 -0
  33. package/dist/shop-minis-react/node_modules/.pnpm/color@5.0.3/node_modules/color/index.js +263 -0
  34. package/dist/shop-minis-react/node_modules/.pnpm/color@5.0.3/node_modules/color/index.js.map +1 -0
  35. package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1 -1
  36. package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  37. 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
  38. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +1 -1
  39. package/dist/utils/colors.js +1 -1
  40. package/package.json +3 -3
  41. package/src/components/atoms/list.tsx +4 -5
  42. package/src/components/commerce/product-link.tsx +2 -1
  43. package/src/components/commerce/search.tsx +6 -3
  44. package/src/components/navigation/transition-link.tsx +1 -2
  45. package/src/hooks/index.ts +3 -0
  46. package/src/hooks/intents/useIntent.test.ts +316 -0
  47. package/src/hooks/intents/useIntent.ts +111 -0
  48. package/src/internal/components/product-review-stars.tsx +1 -2
  49. package/dist/_virtual/index10.js +0 -5
  50. package/dist/_virtual/index10.js.map +0 -1
  51. package/dist/_virtual/index11.js +0 -5
  52. package/dist/_virtual/index11.js.map +0 -1
  53. package/dist/_virtual/index9.js +0 -5
  54. package/dist/_virtual/index9.js.map +0 -1
  55. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/conversions.js +0 -308
  56. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/conversions.js.map +0 -1
  57. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js +0 -41
  58. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js.map +0 -1
  59. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js +0 -53
  60. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js.map +0 -1
  61. package/dist/shop-minis-react/node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js +0 -157
  62. package/dist/shop-minis-react/node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js.map +0 -1
  63. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +0 -103
  64. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js.map +0 -1
  65. package/dist/shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js +0 -269
  66. package/dist/shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js.map +0 -1
  67. package/dist/shop-minis-react/node_modules/.pnpm/is-arrayish@0.3.2/node_modules/is-arrayish/index.js +0 -10
  68. package/dist/shop-minis-react/node_modules/.pnpm/is-arrayish@0.3.2/node_modules/is-arrayish/index.js.map +0 -1
  69. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +0 -23
  70. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js.map +0 -1
@@ -1,8 +1,8 @@
1
1
  import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
2
- import { __require as o } from "../shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js";
3
- var t = /* @__PURE__ */ o();
4
- const l = /* @__PURE__ */ r(t);
2
+ import { __require as o } from "../shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js";
3
+ var t = o();
4
+ const a = /* @__PURE__ */ r(t);
5
5
  export {
6
- l as default
6
+ a as default
7
7
  };
8
8
  //# sourceMappingURL=index2.js.map
@@ -1,8 +1,5 @@
1
- import { getDefaultExportFromCjs as r } from "./_commonjsHelpers.js";
2
- import { __require as o } from "../shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js";
3
- var t = o();
4
- const a = /* @__PURE__ */ r(t);
1
+ var r = {};
5
2
  export {
6
- a as default
3
+ r as __exports
7
4
  };
8
5
  //# sourceMappingURL=index3.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
1
+ {"version":3,"file":"index3.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +1,5 @@
1
- var r = {};
1
+ var e = { exports: {} };
2
2
  export {
3
- r as __exports
3
+ e as __module
4
4
  };
5
5
  //# sourceMappingURL=index4.js.map
@@ -1,5 +1,6 @@
1
- var e = { exports: {} };
1
+ import { __require as r } from "../shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js";
2
+ var i = r();
2
3
  export {
3
- e as __module
4
+ i as l
4
5
  };
5
6
  //# sourceMappingURL=index5.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index5.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
1
+ {"version":3,"file":"index5.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
@@ -1,6 +1,6 @@
1
- import { __require as r } from "../shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js";
1
+ import { __require as r } from "../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";
2
2
  var i = r();
3
3
  export {
4
- i as l
4
+ i as s
5
5
  };
6
6
  //# sourceMappingURL=index6.js.map
@@ -1,6 +1,5 @@
1
- import { __require as r } from "../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";
2
- var i = r();
1
+ var r = {};
3
2
  export {
4
- i as s
3
+ r as __exports
5
4
  };
6
5
  //# sourceMappingURL=index7.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
1
+ {"version":3,"file":"index7.js","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
@@ -1,5 +1,5 @@
1
- var o = { exports: {} };
1
+ var e = { exports: {} };
2
2
  export {
3
- o as __module
3
+ e as __module
4
4
  };
5
5
  //# sourceMappingURL=index8.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sources":["../../../src/components/atoms/list.tsx"],"sourcesContent":["import {useCallback, useEffect, useRef} from 'react'\n\nimport {Virtuoso, VirtuosoProps} from 'react-virtuoso'\n\nimport {RefreshIndicator} from '../../internal/components/refresh-indicator'\nimport {usePullToRefresh} from '../../internal/usePullToRefresh'\nimport {findVirtuosoScrollableElement} from '../../internal/utils/virtuoso-dom'\nimport {cn} from '../../lib/utils'\nimport '../../styles/utilities.css'\nimport {Skeleton} from '../ui/skeleton'\n\nconst DEFAULT_REFRESH_PULL_THRESHOLD = 200\nconst ELEMENT_BIND_DELAY = 100\n\nexport interface ListDocProps<T = any> {\n /** Array of items to render */\n items: T[]\n /** Function to render each item */\n renderItem: (item: T, index: number) => React.ReactNode\n /** Height of the list container */\n height?: string | number\n /** Show scrollbar (default: false) */\n showScrollbar?: boolean\n /** Header element rendered at the top of the list */\n header?: React.ReactNode\n /** Callback to fetch more items when scrolled to bottom */\n fetchMore?: () => Promise<void>\n /** Custom loading component shown while fetching more */\n loadingComponent?: React.ReactNode\n /** Callback for pull-to-refresh */\n onRefresh?: () => Promise<void>\n /** Whether the list is currently refreshing */\n refreshing?: boolean\n /** Enable pull-to-refresh gesture (default: true) */\n enablePullToRefresh?: boolean\n}\n\nexport interface ListProps<T = any>\n extends Omit<\n VirtuosoProps<T, unknown>,\n 'data' | 'itemContent' | 'endReached'\n > {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n showScrollbar?: boolean\n header?: React.ReactNode\n fetchMore?: () => Promise<void>\n loadingComponent?: React.ReactNode\n onRefresh?: () => Promise<void>\n refreshing?: boolean\n enablePullToRefresh?: boolean\n}\n\nexport function List<T = any>({\n items,\n height,\n renderItem,\n className,\n showScrollbar = false,\n header,\n fetchMore,\n loadingComponent,\n onRefresh,\n refreshing,\n enablePullToRefresh = true,\n ...virtuosoProps\n}: ListProps<T>) {\n const inFlightFetchMoreRef = useRef<Promise<void> | null>(null)\n const virtuosoRef = useRef<any>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n const {state: pullToRefreshState, bindToElement} = usePullToRefresh({\n onRefresh,\n threshold: DEFAULT_REFRESH_PULL_THRESHOLD,\n enabled: enablePullToRefresh && Boolean(onRefresh),\n })\n\n const _fetchMore = useCallback(() => {\n // Dedupe concurrent calls by returning the same in-flight promise\n if (inFlightFetchMoreRef.current) return\n\n const current = Promise.resolve(fetchMore?.()).finally(() => {\n // Only clear if this is still the most recent promise\n if (inFlightFetchMoreRef.current === current) {\n inFlightFetchMoreRef.current = null\n }\n })\n\n inFlightFetchMoreRef.current = current\n }, [fetchMore])\n\n const itemContent = useCallback(\n (index: number, item: T) => <>{renderItem(item, index)}</>,\n [renderItem]\n )\n\n const Footer = useCallback(() => {\n if (!fetchMore) return null\n\n return loadingComponent ?? <Skeleton className=\"h-10 w-full p-8\" />\n }, [loadingComponent, fetchMore])\n\n const classNames = cn(showScrollbar ? undefined : 'no-scrollbars', className)\n\n useEffect(() => {\n if (containerRef.current && enablePullToRefresh && onRefresh) {\n let cleanup: (() => void) | undefined\n\n const findAndBind = () => {\n if (!containerRef.current) return\n\n const scrollableElement = findVirtuosoScrollableElement(\n containerRef.current\n )\n cleanup = bindToElement(scrollableElement)\n }\n\n const timeoutId = setTimeout(findAndBind, ELEMENT_BIND_DELAY)\n\n return () => {\n clearTimeout(timeoutId)\n if (cleanup) cleanup()\n }\n }\n return undefined\n }, [bindToElement, enablePullToRefresh, onRefresh])\n\n const EnhancedHeader = useCallback(() => {\n const effectivePullDistance = refreshing\n ? Math.max(pullToRefreshState.pullDistance, 140)\n : pullToRefreshState.pullDistance\n\n const refreshHeaderHeight = Math.min(\n Math.max(effectivePullDistance, 0),\n 140\n )\n\n return (\n <>\n {enablePullToRefresh && onRefresh && (\n <div\n className=\"flex items-center justify-center\"\n style={{\n height: refreshHeaderHeight,\n overflow: 'hidden',\n }}\n >\n <RefreshIndicator\n pullDistance={pullToRefreshState.pullDistance}\n threshold={DEFAULT_REFRESH_PULL_THRESHOLD}\n isRefreshing={refreshing ?? false}\n canRefresh={pullToRefreshState.canRefresh}\n className=\"relative top-0 inset-x-auto\"\n />\n </div>\n )}\n {header && <div>{header}</div>}\n </>\n )\n }, [header, enablePullToRefresh, onRefresh, pullToRefreshState, refreshing])\n\n return (\n <div\n ref={containerRef}\n className={cn('relative transition-all duration-200', classNames)}\n style={{\n height,\n }}\n >\n <Virtuoso\n ref={virtuosoRef}\n className=\"h-full w-full\"\n data={items}\n itemContent={itemContent}\n components={{\n Header: EnhancedHeader,\n Footer,\n }}\n endReached={fetchMore ? _fetchMore : undefined}\n {...virtuosoProps}\n />\n </div>\n )\n}\n"],"names":["DEFAULT_REFRESH_PULL_THRESHOLD","ELEMENT_BIND_DELAY","List","items","height","renderItem","className","showScrollbar","header","fetchMore","loadingComponent","onRefresh","refreshing","enablePullToRefresh","virtuosoProps","inFlightFetchMoreRef","useRef","virtuosoRef","containerRef","pullToRefreshState","bindToElement","usePullToRefresh","_fetchMore","useCallback","current","itemContent","index","item","Footer","jsx","Skeleton","classNames","cn","useEffect","cleanup","timeoutId","scrollableElement","findVirtuosoScrollableElement","EnhancedHeader","effectivePullDistance","refreshHeaderHeight","jsxs","Fragment","RefreshIndicator","Virtuoso"],"mappings":"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"}
1
+ {"version":3,"file":"list.js","sources":["../../../src/components/atoms/list.tsx"],"sourcesContent":["import {useCallback, useEffect, useRef} from 'react'\n\nimport {Virtuoso, VirtuosoProps} from 'react-virtuoso'\n\nimport {RefreshIndicator} from '../../internal/components/refresh-indicator'\nimport {usePullToRefresh} from '../../internal/usePullToRefresh'\nimport {findVirtuosoScrollableElement} from '../../internal/utils/virtuoso-dom'\nimport {cn} from '../../lib/utils'\nimport '../../styles/utilities.css'\nimport {Skeleton} from '../ui/skeleton'\n\nconst DEFAULT_REFRESH_PULL_THRESHOLD = 200\nconst ELEMENT_BIND_DELAY = 100\n\nexport interface ListDocProps<T = any> {\n /** Array of items to render */\n items: T[]\n /** Function to render each item */\n renderItem: (item: T, index: number) => React.ReactNode\n /** Height of the list container */\n height?: string | number\n /** Show scrollbar (default: false) */\n showScrollbar?: boolean\n /** Header element rendered at the top of the list */\n header?: React.ReactNode\n /** Callback to fetch more items when scrolled to bottom */\n fetchMore?: () => Promise<void>\n /** Custom loading component shown while fetching more */\n loadingComponent?: React.ReactNode\n /** Callback for pull-to-refresh */\n onRefresh?: () => Promise<void>\n /** Whether the list is currently refreshing */\n refreshing?: boolean\n /** Enable pull-to-refresh gesture (default: true) */\n enablePullToRefresh?: boolean\n}\n\nexport interface ListProps<T = any> extends Omit<\n VirtuosoProps<T, unknown>,\n 'data' | 'itemContent' | 'endReached'\n> {\n items: T[]\n renderItem: (item: T, index: number) => React.ReactNode\n showScrollbar?: boolean\n header?: React.ReactNode\n fetchMore?: () => Promise<void>\n loadingComponent?: React.ReactNode\n onRefresh?: () => Promise<void>\n refreshing?: boolean\n enablePullToRefresh?: boolean\n}\n\nexport function List<T = any>({\n items,\n height,\n renderItem,\n className,\n showScrollbar = false,\n header,\n fetchMore,\n loadingComponent,\n onRefresh,\n refreshing,\n enablePullToRefresh = true,\n ...virtuosoProps\n}: ListProps<T>) {\n const inFlightFetchMoreRef = useRef<Promise<void> | null>(null)\n const virtuosoRef = useRef<any>(null)\n const containerRef = useRef<HTMLDivElement>(null)\n\n const {state: pullToRefreshState, bindToElement} = usePullToRefresh({\n onRefresh,\n threshold: DEFAULT_REFRESH_PULL_THRESHOLD,\n enabled: enablePullToRefresh && Boolean(onRefresh),\n })\n\n const _fetchMore = useCallback(() => {\n // Dedupe concurrent calls by returning the same in-flight promise\n if (inFlightFetchMoreRef.current) return\n\n const current = Promise.resolve(fetchMore?.()).finally(() => {\n // Only clear if this is still the most recent promise\n if (inFlightFetchMoreRef.current === current) {\n inFlightFetchMoreRef.current = null\n }\n })\n\n inFlightFetchMoreRef.current = current\n }, [fetchMore])\n\n const itemContent = useCallback(\n (index: number, item: T) => <>{renderItem(item, index)}</>,\n [renderItem]\n )\n\n const Footer = useCallback(() => {\n if (!fetchMore) return null\n\n return loadingComponent ?? <Skeleton className=\"h-10 w-full p-8\" />\n }, [loadingComponent, fetchMore])\n\n const classNames = cn(showScrollbar ? undefined : 'no-scrollbars', className)\n\n useEffect(() => {\n if (containerRef.current && enablePullToRefresh && onRefresh) {\n let cleanup: (() => void) | undefined\n\n const findAndBind = () => {\n if (!containerRef.current) return\n\n const scrollableElement = findVirtuosoScrollableElement(\n containerRef.current\n )\n cleanup = bindToElement(scrollableElement)\n }\n\n const timeoutId = setTimeout(findAndBind, ELEMENT_BIND_DELAY)\n\n return () => {\n clearTimeout(timeoutId)\n if (cleanup) cleanup()\n }\n }\n return undefined\n }, [bindToElement, enablePullToRefresh, onRefresh])\n\n const EnhancedHeader = useCallback(() => {\n const effectivePullDistance = refreshing\n ? Math.max(pullToRefreshState.pullDistance, 140)\n : pullToRefreshState.pullDistance\n\n const refreshHeaderHeight = Math.min(\n Math.max(effectivePullDistance, 0),\n 140\n )\n\n return (\n <>\n {enablePullToRefresh && onRefresh && (\n <div\n className=\"flex items-center justify-center\"\n style={{\n height: refreshHeaderHeight,\n overflow: 'hidden',\n }}\n >\n <RefreshIndicator\n pullDistance={pullToRefreshState.pullDistance}\n threshold={DEFAULT_REFRESH_PULL_THRESHOLD}\n isRefreshing={refreshing ?? false}\n canRefresh={pullToRefreshState.canRefresh}\n className=\"relative top-0 inset-x-auto\"\n />\n </div>\n )}\n {header && <div>{header}</div>}\n </>\n )\n }, [header, enablePullToRefresh, onRefresh, pullToRefreshState, refreshing])\n\n return (\n <div\n ref={containerRef}\n className={cn('relative transition-all duration-200', classNames)}\n style={{\n height,\n }}\n >\n <Virtuoso\n ref={virtuosoRef}\n className=\"h-full w-full\"\n data={items}\n itemContent={itemContent}\n components={{\n Header: EnhancedHeader,\n Footer,\n }}\n endReached={fetchMore ? _fetchMore : undefined}\n {...virtuosoProps}\n />\n </div>\n )\n}\n"],"names":["DEFAULT_REFRESH_PULL_THRESHOLD","ELEMENT_BIND_DELAY","List","items","height","renderItem","className","showScrollbar","header","fetchMore","loadingComponent","onRefresh","refreshing","enablePullToRefresh","virtuosoProps","inFlightFetchMoreRef","useRef","virtuosoRef","containerRef","pullToRefreshState","bindToElement","usePullToRefresh","_fetchMore","useCallback","current","itemContent","index","item","Footer","jsx","Skeleton","classNames","cn","useEffect","cleanup","timeoutId","scrollableElement","findVirtuosoScrollableElement","EnhancedHeader","effectivePullDistance","refreshHeaderHeight","jsxs","Fragment","RefreshIndicator","Virtuoso"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {useProductImpression} from '../../internal/useProductImpression'\nimport {cn} from '../../lib/utils'\nimport {formatMoney} from '../../utils/formatMoney'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nimport {FavoriteButton} from './favorite-button'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn('', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n hideFavoriteAction = false,\n onPress,\n filled = false,\n customAction,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n hideFavoriteAction?: boolean\n onPress?: () => void\n filled?: boolean\n customAction?: React.ReactNode\n}) {\n const favoriteAction = hideFavoriteAction ? null : (\n <FavoriteButton filled={filled} onClick={onPress} />\n )\n\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {customAction ? <>{customAction}</> : favoriteAction}\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkDocProps {\n /** The product to display */\n product: Product\n /** Hide the favorite/save button */\n hideFavoriteAction?: boolean\n /** Callback when the product link is clicked */\n onClick?: (product: Product) => void\n /** Hide the review stars */\n reviewsDisabled?: boolean\n /** Custom action element to replace the favorite button. Must be provided with `onCustomActionClick`. */\n customAction?: React.ReactNode\n /** Callback when the custom action is clicked. Must be provided with `customAction`. */\n onCustomActionClick?: () => void\n /** Whether to disable impression tracking */\n impressionTrackingDisabled?: boolean\n}\n\nexport type ProductLinkProps = {\n product: Product\n hideFavoriteAction?: boolean\n onClick?: (product: Product) => void\n reviewsDisabled?: boolean\n impressionTrackingDisabled?: boolean\n} & (\n | {\n customAction?: never\n onCustomActionClick?: never\n }\n | {\n customAction: React.ReactNode\n onCustomActionClick: () => void\n }\n)\n\n// Composed ProductLink component\nfunction ProductLink({\n product,\n hideFavoriteAction = false,\n onClick,\n customAction,\n onCustomActionClick,\n reviewsDisabled = false,\n impressionTrackingDisabled = false,\n}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n const {ref: impressionRef} = useProductImpression({\n productId: product.id,\n skip: impressionTrackingDisabled,\n })\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n onClick?.(product)\n }, [navigateToProduct, id, onClick, product])\n\n const handleActionPress = React.useCallback(async () => {\n if (customAction || onCustomActionClick) {\n onCustomActionClick?.()\n return\n }\n\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n customAction,\n onCustomActionClick,\n isFavoritedLocal,\n unsaveProduct,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n ])\n\n return (\n <div ref={impressionRef}>\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {averageRating && !reviewsDisabled ? (\n <ProductLinkRating>\n <ProductReviewStars\n averageRating={averageRating}\n reviewCount={reviewCount}\n />\n </ProductLinkRating>\n ) : null}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n customAction={customAction}\n hideFavoriteAction={hideFavoriteAction}\n />\n </ProductLinkRoot>\n </div>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","hideFavoriteAction","filled","customAction","favoriteAction","FavoriteButton","CardAction","Fragment","ProductLink","product","onClick","onCustomActionClick","reviewsDisabled","impressionTrackingDisabled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","impressionRef","useProductImpression","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","ProductReviewStars"],"mappings":";;;;;;;;;;;;;AAiBA,MAAMA,IAAsBC,EAAI,IAAI;AAAA,EAClC,UAAU;AAAA,IACR,QAAQ;AAAA,MACN,YAAY;AAAA,MACZ,UAAU;AAAA,IACZ;AAAA,IACA,UAAU;AAAA,MACR,MAAM;AAAA,MACN,OAAO;AAAA,MACP,OAAO;AAAA,IAAA;AAAA,EAEX;AAAA,EACA,iBAAiB;AAAA,IACf,QAAQ;AAAA,IACR,UAAU;AAAA,EAAA;AAEd,CAAC;AAWD,SAASC,EAAgB;AAAA,EACvB,WAAAC;AAAA,EACA,QAAAC;AAAA,EACA,UAAAC;AAAA,EACA,SAAAC,IAAU;AAAA,EACV,SAAAC;AAAA,EACA,GAAGC;AACL,GAAyB;AAIrB,SAAA,gBAAAC;AAAA,IAACC;AAAA,IAAA;AAAA,MACC,SAASH;AAAA,MACT,UAAU,EAAC,SAAS,IAAG;AAAA,MACvB,YAAY;AAAA,QACV,SAAS,EAAC,MAAM,SAAS,UAAU,MAAM,MAAM,YAAW;AAAA,MAC5D;AAAA,MAEA,UAAA,gBAAAE;AAAA,QAVSH,IAAUK,IAAqBC;AAAA,QAUvC;AAAA,UACC,WAAWC;AAAA,YACTb,EAAoB,EAAC,QAAAI,GAAQ,UAAAC,GAAS;AAAA,YACtC;AAAA,YACAF;AAAA,UACF;AAAA,UACC,GAAGK;AAAA,QAAA;AAAA,MAAA;AAAA,IACN;AAAA,EACF;AAEJ;AAEA,SAASM,EAAiB;AAAA,EACxB,WAAAX;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,GAAGI;AACL,GAAuE;AAEnE,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI;AAAA,QACT;AAAA,QACAT,MAAW,eACP,4BACA;AAAA,QACJD;AAAA,MACF;AAAA,MACC,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASO,EAAgB;AAAA,EACvB,WAAAZ;AAAA,EACA,QAAAC,IAAS;AAAA,EACT,GAAGI;AACL,GAEG;AAEC,SAAA,gBAAAC;AAAA,IAACO;AAAA,IAAA;AAAA,MACC,WAAWH;AAAA,QACTT,MAAW,eACP,uCACA;AAAA,QACJD;AAAA,MACF;AAAA,MACC,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASS,GAAiB;AAAA,EACxB,WAAAd;AAAA,EACA,UAAAe;AAAA,EACA,GAAGV;AACL,GAA+B;AAE3B,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI;AAAA,QACT;AAAA,QACAV;AAAA,MACF;AAAA,MACC,GAAGK;AAAA,MAEH,UAAAU;AAAA,IAAA;AAAA,EACH;AAEJ;AAEA,SAASC,GAAiB,EAAC,WAAAhB,GAAW,GAAGK,KAAqC;AAE1E,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI,EAAG,2BAA2BV,CAAS;AAAA,MACjD,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASY,GAAwB;AAAA,EAC/B,WAAAjB;AAAA,EACA,GAAGK;AACL,GAAiC;AAE7B,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI,EAAG,uCAAuCV,CAAS;AAAA,MAC7D,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASa,GAAyB;AAAA,EAChC,WAAAlB;AAAA,EACA,GAAGK;AACL,GAAiC;AAE7B,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI,EAAG,sCAAsCV,CAAS;AAAA,MAC5D,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASc,GAAyB;AAAA,EAChC,WAAAnB;AAAA,EACA,GAAGK;AACL,GAAiC;AAE7B,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI,EAAG,sCAAsCV,CAAS;AAAA,MAC5D,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASe,GAAkB,EAAC,WAAApB,GAAW,GAAGK,KAAqC;AAE3E,SAAA,gBAAAC;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAWI,EAAG,IAAIV,CAAS;AAAA,MAC1B,GAAGK;AAAA,IAAA;AAAA,EACN;AAEJ;AAEA,SAASgB,GAAmB;AAAA,EAC1B,WAAArB;AAAA,EACA,oBAAAsB,IAAqB;AAAA,EACrB,SAAAlB;AAAA,EACA,QAAAmB,IAAS;AAAA,EACT,cAAAC;AAAA,EACA,GAAGnB;AACL,GAKG;AACD,QAAMoB,IAAiBH,IAAqB,yBACzCI,GAAe,EAAA,QAAAH,GAAgB,SAASnB,GAAS;AAIlD,SAAA,gBAAAE;AAAA,IAACqB;AAAA,IAAA;AAAA,MACC,WAAWjB,EAAG,uCAAuCV,CAAS;AAAA,MAC7D,GAAGK;AAAA,MAEJ,UAAA,gBAAAC;AAAA,QAACC;AAAA,QAAA;AAAA,UACC,iBAAe;AAAA,UACf,SAASH;AAAA,UACT,UAAU,EAAC,SAAS,KAAK,OAAO,KAAI;AAAA,UACpC,YAAY;AAAA,YACV,SAAS,EAAC,MAAM,SAAS,UAAU,MAAM,MAAM,YAAW;AAAA,YAC1D,OAAO,EAAC,MAAM,SAAS,UAAU,MAAM,MAAM,YAAW;AAAA,UAC1D;AAAA,UAEC,UAAAoB,IAAkB,gBAAAlB,EAAAsB,GAAA,EAAA,UAAAJ,EAAA,CAAa,IAAMC;AAAA,QAAA;AAAA,MAAA;AAAA,IACxC;AAAA,EACF;AAEJ;AAqCA,SAASI,GAAY;AAAA,EACnB,SAAAC;AAAA,EACA,oBAAAR,IAAqB;AAAA,EACrB,SAAAS;AAAA,EACA,cAAAP;AAAA,EACA,qBAAAQ;AAAA,EACA,iBAAAC,IAAkB;AAAA,EAClB,4BAAAC,IAA6B;AAC/B,GAAqB;AACb,QAAA,EAAC,mBAAAC,EAAiB,IAAIC,EAAkB,GACxC,EAAC,aAAAC,GAAa,eAAAC,EAAa,IAAIC,EAAwB,GACvD,EAAC,KAAKC,EAAa,IAAIC,EAAqB;AAAA,IAChD,WAAWX,EAAQ;AAAA,IACnB,MAAMI;AAAA,EAAA,CACP,GAEK;AAAA,IACJ,IAAAQ;AAAA,IACA,OAAAC;AAAA,IACA,eAAAC;AAAA,IACA,iBAAAC;AAAA,IACA,OAAAC;AAAA,IACA,gBAAAC;AAAA,IACA,aAAAC;AAAA,IACA,iBAAAC;AAAA,IACA,kBAAAC;AAAA,IACA,MAAAC;AAAA,EAAA,IACErB,GAGE,CAACsB,GAAkBC,CAAmB,IAAIC,EAAM,SAASN,CAAW,GAEpEO,IAAgBV,GAAiB,eACjCW,IAAcX,GAAiB,aAC/BY,IAASX,GAAO,SAClBY,EAAYZ,GAAO,QAAQA,GAAO,YAAY,IAC9C,QACEa,IAAWf,GAAe,KAC1BgB,IAAehB,GAAe,WAAWD,GACzCkB,IAAuBd,GAAgB,SACzCW,EAAYX,GAAgB,QAAQA,GAAgB,YAAY,IAChE,QACEe,IAAcD,KAAwBA,MAAyBJ,GAE/DM,IAAcT,EAAM,YAAY,MAAM;AACxB,IAAAnB,EAAA;AAAA,MAChB,WAAWO;AAAA,IAAA,CACZ,GACDX,IAAUD,CAAO;AAAA,KAChB,CAACK,GAAmBO,GAAIX,GAASD,CAAO,CAAC,GAEtCkC,IAAoBV,EAAM,YAAY,YAAY;AACtD,QAAI9B,KAAgBQ,GAAqB;AACjB,MAAAA,IAAA;AACtB;AAAA,IAAA;AAGF,UAAMiC,IAAgBb;AAGtB,IAAAC,EAAoB,CAACY,CAAa;AAE9B,QAAA;AACF,MAAIA,IACF,MAAM3B,EAAc;AAAA,QAClB,WAAWI;AAAA,QACX,QAAQS,EAAK;AAAA,QACb,kBAAkBF,GAAiB,MAAMC;AAAA,MAAA,CAC1C,IAED,MAAMb,EAAY;AAAA,QAChB,WAAWK;AAAA,QACX,QAAQS,EAAK;AAAA,QACb,kBAAkBF,GAAiB,MAAMC;AAAA,MAAA,CAC1C;AAAA,YAEW;AAEd,MAAAG,EAAoBY,CAAa;AAAA,IAAA;AAAA,EACnC,GACC;AAAA,IACDzC;AAAA,IACAQ;AAAA,IACAoB;AAAA,IACAd;AAAA,IACAI;AAAA,IACAS,EAAK;AAAA,IACLF,GAAiB;AAAA,IACjBC;AAAA,IACAb;AAAA,EAAA,CACD;AAGC,SAAA,gBAAA/B,EAAC,OAAI,EAAA,KAAKkC,GACR,UAAA,gBAAA0B;AAAA,IAACnE;AAAA,IAAA;AAAA,MACC,QAAO;AAAA,MACP,UAAU+D,IAAc,UAAU;AAAA,MAClC,SAASC;AAAA,MAET,UAAA;AAAA,QAAC,gBAAAzD,EAAAK,GAAA,EAAiB,QAAO,cACtB,UACCgD,IAAA,gBAAArD;AAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAKqD;AAAA,YACL,KAAKC;AAAA,YACL,WAAU;AAAA,UAAA;AAAA,QAAA,IAGX,gBAAAtD,EAAA,OAAA,EAAI,WAAU,yFAAwF,qBAEvG,CAAA,GAEJ;AAAA,QAEA,gBAAA4D,EAACtD,GAAgB,EAAA,QAAO,cACtB,UAAA;AAAA,UAAA,gBAAAN,EAACQ,MAAkB,UAAM6B,EAAA,CAAA;AAAA,UAExBY,KAAiB,CAACtB,IACjB,gBAAA3B,EAACc,IACC,EAAA,UAAA,gBAAAd;AAAA,YAAC6D;AAAA,YAAA;AAAA,cACC,eAAAZ;AAAA,cACA,aAAAC;AAAA,YAAA;AAAA,aAEJ,IACE;AAAA,UAEJ,gBAAAlD,EAACU,IACE,EAAA,UAAA8C,IAEG,gBAAAI,EAAAtC,GAAA,EAAA,UAAA;AAAA,YAAA,gBAAAtB,EAACa,MAA0B,UAAOsC,EAAA,CAAA;AAAA,YAClC,gBAAAnD,EAACY,MACE,UACH2C,EAAA,CAAA;AAAA,UAAA,EACF,CAAA,IAEA,gBAAAvD,EAACW,IAAyB,EAAA,UAAAwC,EAAA,CAAO,EAErC,CAAA;AAAA,QAAA,GACF;AAAA,QAEA,gBAAAnD;AAAA,UAACe;AAAA,UAAA;AAAA,YACC,QAAQ+B;AAAA,YACR,SAASY;AAAA,YACT,cAAAxC;AAAA,YACA,oBAAAF;AAAA,UAAA;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA,GAEJ;AAEJ;"}
1
+ {"version":3,"file":"product-link.js","sources":["../../../src/components/commerce/product-link.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {type Product} from '@shopify/shop-minis-platform'\nimport {cva, type VariantProps} from 'class-variance-authority'\nimport {Slot as SlotPrimitive} from 'radix-ui'\n\nimport {useShopNavigation} from '../../hooks/navigation/useShopNavigation'\nimport {useSavedProductsActions} from '../../hooks/user/useSavedProductsActions'\nimport {ProductReviewStars} from '../../internal/components/product-review-stars'\nimport {useProductImpression} from '../../internal/useProductImpression'\nimport {cn} from '../../lib/utils'\nimport {formatMoney} from '../../utils/formatMoney'\nimport {Touchable} from '../atoms/touchable'\nimport {Card, CardContent, CardAction} from '../ui/card'\n\nimport {FavoriteButton} from './favorite-button'\n\nconst productLinkVariants = cva('', {\n variants: {\n layout: {\n horizontal: 'w-full !flex-row items-center gap-3 px-4 py-3',\n vertical: 'flex-col',\n },\n discount: {\n none: '',\n small: '',\n large: '',\n },\n },\n defaultVariants: {\n layout: 'horizontal',\n discount: 'none',\n },\n})\n\n// Primitive components (building blocks)\nexport interface ProductLinkRootProps\n extends\n React.ComponentProps<typeof Card>,\n VariantProps<typeof productLinkVariants> {\n layout?: 'horizontal' | 'vertical'\n asChild?: boolean\n onPress?: () => void\n}\n\nfunction ProductLinkRoot({\n className,\n layout,\n discount,\n asChild = false,\n onPress,\n ...props\n}: ProductLinkRootProps) {\n const Comp = asChild ? SlotPrimitive.Root : Card\n\n return (\n <Touchable\n onClick={onPress}\n whileTap={{opacity: 0.7}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n <Comp\n className={cn(\n productLinkVariants({layout, discount}),\n 'border-0 bg-white rounded-xl shadow-lg shadow-black/10',\n className\n )}\n {...props}\n />\n </Touchable>\n )\n}\n\nfunction ProductLinkImage({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<'div'> & {layout?: 'horizontal' | 'vertical'}) {\n return (\n <div\n data-slot=\"product-link-image\"\n className={cn(\n 'overflow-hidden rounded-md bg-muted',\n layout === 'horizontal'\n ? 'h-16 w-16 flex-shrink-0'\n : 'aspect-square w-full',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkInfo({\n className,\n layout = 'horizontal',\n ...props\n}: React.ComponentProps<typeof CardContent> & {\n layout?: 'horizontal' | 'vertical'\n}) {\n return (\n <CardContent\n className={cn(\n layout === 'horizontal'\n ? 'flex-1 min-w-0 space-y-1 px-0 py-0'\n : 'space-y-2',\n className\n )}\n {...props}\n />\n )\n}\n\nfunction ProductLinkTitle({\n className,\n children,\n ...props\n}: React.ComponentProps<'h3'>) {\n return (\n <h3\n data-slot=\"product-link-title\"\n className={cn(\n 'text-sm font-medium leading-tight text-gray-900 truncate overflow-hidden whitespace-nowrap text-ellipsis',\n className\n )}\n {...props}\n >\n {children}\n </h3>\n )\n}\n\nfunction ProductLinkPrice({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-price\"\n className={cn('flex items-center gap-2', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkCurrentPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-current-price\"\n className={cn('text-sm font-semibold text-gray-900', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkOriginalPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-original-price\"\n className={cn('text-sm text-gray-500 line-through', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkDiscountPrice({\n className,\n ...props\n}: React.ComponentProps<'span'>) {\n return (\n <span\n data-slot=\"product-link-discount-price\"\n className={cn('text-sm font-semibold text-red-600', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkRating({className, ...props}: React.ComponentProps<'div'>) {\n return (\n <div\n data-slot=\"product-link-rating\"\n className={cn('', className)}\n {...props}\n />\n )\n}\n\nfunction ProductLinkActions({\n className,\n hideFavoriteAction = false,\n onPress,\n filled = false,\n customAction,\n ...props\n}: React.ComponentProps<typeof CardAction> & {\n hideFavoriteAction?: boolean\n onPress?: () => void\n filled?: boolean\n customAction?: React.ReactNode\n}) {\n const favoriteAction = hideFavoriteAction ? null : (\n <FavoriteButton filled={filled} onClick={onPress} />\n )\n\n return (\n <CardAction\n className={cn('flex-shrink-0 self-center px-0 py-0', className)}\n {...props}\n >\n <Touchable\n stopPropagation\n onClick={onPress}\n whileTap={{opacity: 0.7, scale: 0.95}}\n transition={{\n opacity: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n scale: {type: 'tween', duration: 0.08, ease: 'easeInOut'},\n }}\n >\n {customAction ? <>{customAction}</> : favoriteAction}\n </Touchable>\n </CardAction>\n )\n}\n\nexport interface ProductLinkDocProps {\n /** The product to display */\n product: Product\n /** Hide the favorite/save button */\n hideFavoriteAction?: boolean\n /** Callback when the product link is clicked */\n onClick?: (product: Product) => void\n /** Hide the review stars */\n reviewsDisabled?: boolean\n /** Custom action element to replace the favorite button. Must be provided with `onCustomActionClick`. */\n customAction?: React.ReactNode\n /** Callback when the custom action is clicked. Must be provided with `customAction`. */\n onCustomActionClick?: () => void\n /** Whether to disable impression tracking */\n impressionTrackingDisabled?: boolean\n}\n\nexport type ProductLinkProps = {\n product: Product\n hideFavoriteAction?: boolean\n onClick?: (product: Product) => void\n reviewsDisabled?: boolean\n impressionTrackingDisabled?: boolean\n} & (\n | {\n customAction?: never\n onCustomActionClick?: never\n }\n | {\n customAction: React.ReactNode\n onCustomActionClick: () => void\n }\n)\n\n// Composed ProductLink component\nfunction ProductLink({\n product,\n hideFavoriteAction = false,\n onClick,\n customAction,\n onCustomActionClick,\n reviewsDisabled = false,\n impressionTrackingDisabled = false,\n}: ProductLinkProps) {\n const {navigateToProduct} = useShopNavigation()\n const {saveProduct, unsaveProduct} = useSavedProductsActions()\n const {ref: impressionRef} = useProductImpression({\n productId: product.id,\n skip: impressionTrackingDisabled,\n })\n\n const {\n id,\n title,\n featuredImage,\n reviewAnalytics,\n price,\n compareAtPrice,\n isFavorited,\n selectedVariant,\n defaultVariantId,\n shop,\n } = product\n\n // Local state for optimistic UI updates\n const [isFavoritedLocal, setIsFavoritedLocal] = React.useState(isFavorited)\n\n const averageRating = reviewAnalytics?.averageRating\n const reviewCount = reviewAnalytics?.reviewCount\n const amount = price?.amount\n ? formatMoney(price?.amount, price?.currencyCode)\n : undefined\n const imageUrl = featuredImage?.url\n const imageAltText = featuredImage?.altText || title\n const compareAtPriceAmount = compareAtPrice?.amount\n ? formatMoney(compareAtPrice?.amount, compareAtPrice?.currencyCode)\n : undefined\n const hasDiscount = compareAtPriceAmount && compareAtPriceAmount !== amount\n\n const handlePress = React.useCallback(() => {\n navigateToProduct({\n productId: id,\n })\n onClick?.(product)\n }, [navigateToProduct, id, onClick, product])\n\n const handleActionPress = React.useCallback(async () => {\n if (customAction || onCustomActionClick) {\n onCustomActionClick?.()\n return\n }\n\n const previousState = isFavoritedLocal\n\n // Optimistic update\n setIsFavoritedLocal(!previousState)\n\n try {\n if (previousState) {\n await unsaveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n } else {\n await saveProduct({\n productId: id,\n shopId: shop.id,\n productVariantId: selectedVariant?.id || defaultVariantId,\n })\n }\n } catch (error) {\n // Revert optimistic update on error\n setIsFavoritedLocal(previousState)\n }\n }, [\n customAction,\n onCustomActionClick,\n isFavoritedLocal,\n unsaveProduct,\n id,\n shop.id,\n selectedVariant?.id,\n defaultVariantId,\n saveProduct,\n ])\n\n return (\n <div ref={impressionRef}>\n <ProductLinkRoot\n layout=\"horizontal\"\n discount={hasDiscount ? 'small' : 'none'}\n onPress={handlePress}\n >\n <ProductLinkImage layout=\"horizontal\">\n {imageUrl ? (\n <img\n src={imageUrl}\n alt={imageAltText}\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"h-full w-full bg-muted flex items-center justify-center text-muted-foreground text-xs\">\n No Image\n </div>\n )}\n </ProductLinkImage>\n\n <ProductLinkInfo layout=\"horizontal\">\n <ProductLinkTitle>{title}</ProductLinkTitle>\n\n {averageRating && !reviewsDisabled ? (\n <ProductLinkRating>\n <ProductReviewStars\n averageRating={averageRating}\n reviewCount={reviewCount}\n />\n </ProductLinkRating>\n ) : null}\n\n <ProductLinkPrice>\n {hasDiscount ? (\n <>\n <ProductLinkDiscountPrice>{amount}</ProductLinkDiscountPrice>\n <ProductLinkOriginalPrice>\n {compareAtPriceAmount}\n </ProductLinkOriginalPrice>\n </>\n ) : (\n <ProductLinkCurrentPrice>{amount}</ProductLinkCurrentPrice>\n )}\n </ProductLinkPrice>\n </ProductLinkInfo>\n\n <ProductLinkActions\n filled={isFavoritedLocal}\n onPress={handleActionPress}\n customAction={customAction}\n hideFavoriteAction={hideFavoriteAction}\n />\n </ProductLinkRoot>\n </div>\n )\n}\n\nexport {ProductLink}\n"],"names":["productLinkVariants","cva","ProductLinkRoot","className","layout","discount","asChild","onPress","props","jsx","Touchable","SlotPrimitive.Root","Card","cn","ProductLinkImage","ProductLinkInfo","CardContent","ProductLinkTitle","children","ProductLinkPrice","ProductLinkCurrentPrice","ProductLinkOriginalPrice","ProductLinkDiscountPrice","ProductLinkRating","ProductLinkActions","hideFavoriteAction","filled","customAction","favoriteAction","FavoriteButton","CardAction","Fragment","ProductLink","product","onClick","onCustomActionClick","reviewsDisabled","impressionTrackingDisabled","navigateToProduct","useShopNavigation","saveProduct","unsaveProduct","useSavedProductsActions","impressionRef","useProductImpression","id","title","featuredImage","reviewAnalytics","price","compareAtPrice","isFavorited","selectedVariant","defaultVariantId","shop","isFavoritedLocal","setIsFavoritedLocal","React","averageRating","reviewCount","amount","formatMoney","imageUrl","imageAltText","compareAtPriceAmount","hasDiscount","handlePress","handleActionPress","previousState","jsxs","ProductReviewStars"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sources":["../../../src/components/commerce/search.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useContext, useState, useCallback} from 'react'\n\nimport {SearchIcon, X} from 'lucide-react'\n\nimport {useProductSearch} from '../../hooks/product/useProductSearch'\nimport {cn} from '../../lib/utils'\nimport {type Product} from '../../types'\nimport {IconButton} from '../atoms/icon-button'\nimport {List} from '../atoms/list'\nimport {Input} from '../ui/input'\n\nimport {ProductLink} from './product-link'\nimport {ProductLinkSkeleton} from './product-link-skeleton'\n\ninterface SearchContextValue {\n query: string\n setQuery: (query: string) => void\n products: Product[] | null\n loading: boolean\n error: Error | null\n fetchMore?: () => Promise<void>\n hasNextPage: boolean\n isTyping: boolean\n}\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\nfunction useSearchContext() {\n const context = useContext(SearchContext)\n if (!context) {\n throw new Error('useSearchContext must be used within a SearchProvider')\n }\n return context\n}\n\nexport interface SearchProviderProps {\n initialQuery?: string\n children: React.ReactNode\n}\n\nfunction SearchProvider({initialQuery = '', children}: SearchProviderProps) {\n const [query, setQueryState] = useState(initialQuery)\n\n const {products, loading, error, fetchMore, hasNextPage, isTyping} =\n useProductSearch({\n query,\n fetchPolicy: 'network-only',\n })\n\n const setQuery = useCallback((newQuery: string) => {\n setQueryState(newQuery)\n }, [])\n\n const contextValue: SearchContextValue = {\n query,\n setQuery,\n products,\n loading,\n error,\n fetchMore,\n hasNextPage,\n isTyping,\n }\n\n return (\n <SearchContext.Provider value={contextValue}>\n {children}\n </SearchContext.Provider>\n )\n}\n\nexport interface SearchInputProps {\n placeholder?: string\n className?: string\n inputProps?: React.ComponentProps<'input'>\n}\n\nfunction SearchInput({\n placeholder = 'Search products...',\n className,\n inputProps,\n}: SearchInputProps) {\n const {query, setQuery} = useSearchContext()\n\n const handleQueryChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n setQuery(event.target.value)\n inputProps?.onChange?.(event)\n },\n [inputProps, setQuery]\n )\n\n return (\n <div className=\"relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100\">\n <div className=\"relative flex items-center\">\n <SearchIcon\n size={18}\n className={cn('text-accent-foreground opacity-60')}\n />\n </div>\n <div className=\"relative flex-1 flex items-center mx-2\">\n <Input\n name=\"search\"\n onChange={handleQueryChange}\n placeholder={placeholder}\n type=\"search\"\n role=\"searchbox\"\n autoComplete=\"off\"\n value={query}\n data-testid=\"search-input\"\n {...inputProps}\n className={cn(\n `w-full flex overflow-hidden rounded-radius-28 border-none shadow-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60`,\n className\n )}\n />\n </div>\n <div className=\"relative flex items-center\">\n {query === '' ? null : (\n <IconButton\n Icon={X}\n size=\"sm\"\n filled={false}\n iconStyles=\"\"\n onClick={() => setQuery('')}\n buttonStyles=\"flex items-center rounded-radius-max bg-[var(--grayscale-l20)]\"\n />\n )}\n </div>\n </div>\n )\n}\n\nexport interface SearchResultsListProps {\n renderItem?: (product: Product, index: number) => React.ReactNode\n height?: number\n itemHeight?: number\n initialStateComponent?: React.JSX.Element\n showScrollbar?: boolean\n overscanCount?: number\n}\n\nfunction SearchResultsList({\n height = window.innerHeight,\n renderItem,\n initialStateComponent,\n showScrollbar,\n}: SearchResultsListProps) {\n const {query, products, loading, fetchMore, hasNextPage, isTyping} =\n useSearchContext()\n\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n const shouldShowStartingState = query.trim().length === 0\n const shouldShowLoading =\n (!products || products.length === 0) && (loading || isTyping)\n const shouldShowEmptyState = (!products || products.length === 0) && !loading\n\n if (shouldShowStartingState) {\n return (\n initialStateComponent || (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n Start typing to search for products\n </div>\n )\n )\n }\n\n if (shouldShowLoading) {\n return (\n <div className=\"flex flex-col px-4 py-4\">\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n </div>\n )\n }\n\n if (shouldShowEmptyState) {\n return (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n {`No products found for \"${query}\"`}\n </div>\n )\n }\n\n return (\n <List\n items={products || []}\n height={height}\n renderItem={_renderItem}\n fetchMore={hasNextPage ? fetchMore : undefined}\n showScrollbar={showScrollbar}\n />\n )\n}\n\ninterface SearchProviderPropsWithoutChildren\n extends Omit<SearchProviderProps, 'children'> {}\nexport interface SearchResultsProps\n extends SearchProviderPropsWithoutChildren,\n SearchInputProps,\n SearchResultsListProps {\n showSearchInput?: boolean\n onProductClick?: (product: Product) => void\n}\n\nfunction Search({\n initialQuery,\n placeholder,\n inputProps,\n height,\n className,\n renderItem,\n itemHeight,\n onProductClick,\n}: SearchResultsProps) {\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink\n key={product.id}\n product={product}\n hideFavoriteAction\n onClick={onProductClick}\n />\n </div>\n )\n }\n\n return (\n <SearchProvider initialQuery={initialQuery}>\n <div className={cn('flex flex-col relative', className)}>\n <div className=\"absolute top-0 left-0 right-0 p-4 w-full z-20 bg-background\">\n <SearchInput placeholder={placeholder} inputProps={inputProps} />\n </div>\n <div className=\"h-14\" />\n <SearchResultsList\n height={height}\n renderItem={_renderItem}\n itemHeight={itemHeight}\n showScrollbar\n />\n </div>\n </SearchProvider>\n )\n}\n\nexport {SearchProvider, SearchInput, SearchResultsList, Search}\n"],"names":["SearchContext","createContext","useSearchContext","context","useContext","SearchProvider","initialQuery","children","query","setQueryState","useState","products","loading","error","fetchMore","hasNextPage","isTyping","useProductSearch","setQuery","useCallback","newQuery","contextValue","SearchInput","placeholder","className","inputProps","handleQueryChange","event","jsxs","jsx","SearchIcon","cn","Input","IconButton","X","SearchResultsList","height","renderItem","initialStateComponent","showScrollbar","_renderItem","product","index","ProductLink","shouldShowStartingState","shouldShowLoading","shouldShowEmptyState","ProductLinkSkeleton","List","Search","itemHeight","onProductClick"],"mappings":"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"}
1
+ {"version":3,"file":"search.js","sources":["../../../src/components/commerce/search.tsx"],"sourcesContent":["import * as React from 'react'\nimport {createContext, useContext, useState, useCallback} from 'react'\n\nimport {SearchIcon, X} from 'lucide-react'\n\nimport {useProductSearch} from '../../hooks/product/useProductSearch'\nimport {cn} from '../../lib/utils'\nimport {type Product} from '../../types'\nimport {IconButton} from '../atoms/icon-button'\nimport {List} from '../atoms/list'\nimport {Input} from '../ui/input'\n\nimport {ProductLink} from './product-link'\nimport {ProductLinkSkeleton} from './product-link-skeleton'\n\ninterface SearchContextValue {\n query: string\n setQuery: (query: string) => void\n products: Product[] | null\n loading: boolean\n error: Error | null\n fetchMore?: () => Promise<void>\n hasNextPage: boolean\n isTyping: boolean\n}\n\nconst SearchContext = createContext<SearchContextValue | null>(null)\n\nfunction useSearchContext() {\n const context = useContext(SearchContext)\n if (!context) {\n throw new Error('useSearchContext must be used within a SearchProvider')\n }\n return context\n}\n\nexport interface SearchProviderProps {\n initialQuery?: string\n children: React.ReactNode\n}\n\nfunction SearchProvider({initialQuery = '', children}: SearchProviderProps) {\n const [query, setQueryState] = useState(initialQuery)\n\n const {products, loading, error, fetchMore, hasNextPage, isTyping} =\n useProductSearch({\n query,\n fetchPolicy: 'network-only',\n })\n\n const setQuery = useCallback((newQuery: string) => {\n setQueryState(newQuery)\n }, [])\n\n const contextValue: SearchContextValue = {\n query,\n setQuery,\n products,\n loading,\n error,\n fetchMore,\n hasNextPage,\n isTyping,\n }\n\n return (\n <SearchContext.Provider value={contextValue}>\n {children}\n </SearchContext.Provider>\n )\n}\n\nexport interface SearchInputProps {\n placeholder?: string\n className?: string\n inputProps?: React.ComponentProps<'input'>\n}\n\nfunction SearchInput({\n placeholder = 'Search products...',\n className,\n inputProps,\n}: SearchInputProps) {\n const {query, setQuery} = useSearchContext()\n\n const handleQueryChange = useCallback(\n (event: React.ChangeEvent<HTMLInputElement>) => {\n setQuery(event.target.value)\n inputProps?.onChange?.(event)\n },\n [inputProps, setQuery]\n )\n\n return (\n <div className=\"relative flex flex-1 items-center rounded-full pl-4 pr-2 py-1 bg-gray-100\">\n <div className=\"relative flex items-center\">\n <SearchIcon\n size={18}\n className={cn('text-accent-foreground opacity-60')}\n />\n </div>\n <div className=\"relative flex-1 flex items-center mx-2\">\n <Input\n name=\"search\"\n onChange={handleQueryChange}\n placeholder={placeholder}\n type=\"search\"\n role=\"searchbox\"\n autoComplete=\"off\"\n value={query}\n data-testid=\"search-input\"\n {...inputProps}\n className={cn(\n `w-full flex overflow-hidden rounded-radius-28 border-none shadow-none py-4 px-0 text-text placeholder:text-text placeholder:opacity-60`,\n className\n )}\n />\n </div>\n <div className=\"relative flex items-center\">\n {query === '' ? null : (\n <IconButton\n Icon={X}\n size=\"sm\"\n filled={false}\n iconStyles=\"\"\n onClick={() => setQuery('')}\n buttonStyles=\"flex items-center rounded-radius-max bg-[var(--grayscale-l20)]\"\n />\n )}\n </div>\n </div>\n )\n}\n\nexport interface SearchResultsListProps {\n renderItem?: (product: Product, index: number) => React.ReactNode\n height?: number\n itemHeight?: number\n initialStateComponent?: React.JSX.Element\n showScrollbar?: boolean\n overscanCount?: number\n}\n\nfunction SearchResultsList({\n height = window.innerHeight,\n renderItem,\n initialStateComponent,\n showScrollbar,\n}: SearchResultsListProps) {\n const {query, products, loading, fetchMore, hasNextPage, isTyping} =\n useSearchContext()\n\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink key={product.id} product={product} hideFavoriteAction />\n </div>\n )\n }\n\n const shouldShowStartingState = query.trim().length === 0\n const shouldShowLoading =\n (!products || products.length === 0) && (loading || isTyping)\n const shouldShowEmptyState = (!products || products.length === 0) && !loading\n\n if (shouldShowStartingState) {\n return (\n initialStateComponent || (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n Start typing to search for products\n </div>\n )\n )\n }\n\n if (shouldShowLoading) {\n return (\n <div className=\"flex flex-col px-4 py-4\">\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n <ProductLinkSkeleton className=\"mb-4\" />\n </div>\n )\n }\n\n if (shouldShowEmptyState) {\n return (\n <div className=\"flex items-center justify-center h-32 text-gray-500\">\n {`No products found for \"${query}\"`}\n </div>\n )\n }\n\n return (\n <List\n items={products || []}\n height={height}\n renderItem={_renderItem}\n fetchMore={hasNextPage ? fetchMore : undefined}\n showScrollbar={showScrollbar}\n />\n )\n}\n\ninterface SearchProviderPropsWithoutChildren extends Omit<\n SearchProviderProps,\n 'children'\n> {}\nexport interface SearchResultsProps\n extends\n SearchProviderPropsWithoutChildren,\n SearchInputProps,\n SearchResultsListProps {\n showSearchInput?: boolean\n onProductClick?: (product: Product) => void\n}\n\nfunction Search({\n initialQuery,\n placeholder,\n inputProps,\n height,\n className,\n renderItem,\n itemHeight,\n onProductClick,\n}: SearchResultsProps) {\n const _renderItem = (product: Product, index: number) => {\n if (renderItem) {\n return renderItem(product, index)\n }\n\n return (\n <div className=\"p-2\">\n <ProductLink\n key={product.id}\n product={product}\n hideFavoriteAction\n onClick={onProductClick}\n />\n </div>\n )\n }\n\n return (\n <SearchProvider initialQuery={initialQuery}>\n <div className={cn('flex flex-col relative', className)}>\n <div className=\"absolute top-0 left-0 right-0 p-4 w-full z-20 bg-background\">\n <SearchInput placeholder={placeholder} inputProps={inputProps} />\n </div>\n <div className=\"h-14\" />\n <SearchResultsList\n height={height}\n renderItem={_renderItem}\n itemHeight={itemHeight}\n showScrollbar\n />\n </div>\n </SearchProvider>\n )\n}\n\nexport {SearchProvider, SearchInput, SearchResultsList, Search}\n"],"names":["SearchContext","createContext","useSearchContext","context","useContext","SearchProvider","initialQuery","children","query","setQueryState","useState","products","loading","error","fetchMore","hasNextPage","isTyping","useProductSearch","setQuery","useCallback","newQuery","contextValue","SearchInput","placeholder","className","inputProps","handleQueryChange","event","jsxs","jsx","SearchIcon","cn","Input","IconButton","X","SearchResultsList","height","renderItem","initialStateComponent","showScrollbar","_renderItem","product","index","ProductLink","shouldShowStartingState","shouldShowLoading","shouldShowEmptyState","ProductLinkSkeleton","List","Search","itemHeight","onProductClick"],"mappings":";;;;;;;;;;;AA0BA,MAAMA,IAAgBC,EAAyC,IAAI;AAEnE,SAASC,IAAmB;AACpB,QAAAC,IAAUC,EAAWJ,CAAa;AACxC,MAAI,CAACG;AACG,UAAA,IAAI,MAAM,uDAAuD;AAElE,SAAAA;AACT;AAOA,SAASE,EAAe,EAAC,cAAAC,IAAe,IAAI,UAAAC,KAAgC;AAC1E,QAAM,CAACC,GAAOC,CAAa,IAAIC,EAASJ,CAAY,GAE9C,EAAC,UAAAK,GAAU,SAAAC,GAAS,OAAAC,GAAO,WAAAC,GAAW,aAAAC,GAAa,UAAAC,MACvDC,EAAiB;AAAA,IACf,OAAAT;AAAA,IACA,aAAa;AAAA,EAAA,CACd,GAEGU,IAAWC,EAAY,CAACC,MAAqB;AACjD,IAAAX,EAAcW,CAAQ;AAAA,EACxB,GAAG,EAAE,GAECC,IAAmC;AAAA,IACvC,OAAAb;AAAA,IACA,UAAAU;AAAA,IACA,UAAAP;AAAA,IACA,SAAAC;AAAA,IACA,OAAAC;AAAA,IACA,WAAAC;AAAA,IACA,aAAAC;AAAA,IACA,UAAAC;AAAA,EACF;AAEA,2BACGhB,EAAc,UAAd,EAAuB,OAAOqB,GAC5B,UAAAd,GACH;AAEJ;AAQA,SAASe,EAAY;AAAA,EACnB,aAAAC,IAAc;AAAA,EACd,WAAAC;AAAA,EACA,YAAAC;AACF,GAAqB;AACnB,QAAM,EAAC,OAAAjB,GAAO,UAAAU,EAAQ,IAAIhB,EAAiB,GAErCwB,IAAoBP;AAAA,IACxB,CAACQ,MAA+C;AACrC,MAAAT,EAAAS,EAAM,OAAO,KAAK,GAC3BF,GAAY,WAAWE,CAAK;AAAA,IAC9B;AAAA,IACA,CAACF,GAAYP,CAAQ;AAAA,EACvB;AAGE,SAAA,gBAAAU,EAAC,OAAI,EAAA,WAAU,6EACb,UAAA;AAAA,IAAC,gBAAAC,EAAA,OAAA,EAAI,WAAU,8BACb,UAAA,gBAAAA;AAAA,MAACC;AAAAA,MAAA;AAAA,QACC,MAAM;AAAA,QACN,WAAWC,EAAG,mCAAmC;AAAA,MAAA;AAAA,IAAA,GAErD;AAAA,IACA,gBAAAF,EAAC,OAAI,EAAA,WAAU,0CACb,UAAA,gBAAAA;AAAA,MAACG;AAAA,MAAA;AAAA,QACC,MAAK;AAAA,QACL,UAAUN;AAAA,QACV,aAAAH;AAAA,QACA,MAAK;AAAA,QACL,MAAK;AAAA,QACL,cAAa;AAAA,QACb,OAAOf;AAAA,QACP,eAAY;AAAA,QACX,GAAGiB;AAAA,QACJ,WAAWM;AAAA,UACT;AAAA,UACAP;AAAA,QAAA;AAAA,MACF;AAAA,IAAA,GAEJ;AAAA,sBACC,OAAI,EAAA,WAAU,8BACZ,UAAAhB,MAAU,KAAK,OACd,gBAAAqB;AAAA,MAACI;AAAA,MAAA;AAAA,QACC,MAAMC;AAAA,QACN,MAAK;AAAA,QACL,QAAQ;AAAA,QACR,YAAW;AAAA,QACX,SAAS,MAAMhB,EAAS,EAAE;AAAA,QAC1B,cAAa;AAAA,MAAA;AAAA,IAAA,EAGnB,CAAA;AAAA,EAAA,GACF;AAEJ;AAWA,SAASiB,EAAkB;AAAA,EACzB,QAAAC,IAAS,OAAO;AAAA,EAChB,YAAAC;AAAA,EACA,uBAAAC;AAAA,EACA,eAAAC;AACF,GAA2B;AACnB,QAAA,EAAC,OAAA/B,GAAO,UAAAG,GAAU,SAAAC,GAAS,WAAAE,GAAW,aAAAC,GAAa,UAAAC,MACvDd,EAAiB,GAEbsC,IAAc,CAACC,GAAkBC,MACjCL,IACKA,EAAWI,GAASC,CAAK,IAIhC,gBAAAb,EAAC,OAAI,EAAA,WAAU,OACb,UAAA,gBAAAA,EAACc,GAA6B,EAAA,SAAAF,GAAkB,oBAAkB,GAAA,GAAhDA,EAAQ,EAAyC,GACrE,GAIEG,IAA0BpC,EAAM,KAAK,EAAE,WAAW,GAClDqC,KACH,CAAClC,KAAYA,EAAS,WAAW,OAAOC,KAAWI,IAChD8B,KAAwB,CAACnC,KAAYA,EAAS,WAAW,MAAM,CAACC;AAEtE,SAAIgC,IAEAN,KACE,gBAAAT,EAAC,OAAI,EAAA,WAAU,uDAAsD,UAErE,uCAAA,IAKFgB,IAEA,gBAAAjB,EAAC,OAAI,EAAA,WAAU,2BACb,UAAA;AAAA,IAAC,gBAAAC,EAAAkB,GAAA,EAAoB,WAAU,OAAO,CAAA;AAAA,IACtC,gBAAAlB,EAACkB,GAAoB,EAAA,WAAU,OAAO,CAAA;AAAA,IACtC,gBAAAlB,EAACkB,GAAoB,EAAA,WAAU,OAAO,CAAA;AAAA,IACtC,gBAAAlB,EAACkB,GAAoB,EAAA,WAAU,OAAO,CAAA;AAAA,EAAA,GACxC,IAIAD,sBAEC,OAAI,EAAA,WAAU,uDACZ,UAAA,0BAA0BtC,CAAK,KAClC,IAKF,gBAAAqB;AAAA,IAACmB;AAAA,IAAA;AAAA,MACC,OAAOrC,KAAY,CAAC;AAAA,MACpB,QAAAyB;AAAA,MACA,YAAYI;AAAA,MACZ,WAAWzB,IAAcD,IAAY;AAAA,MACrC,eAAAyB;AAAA,IAAA;AAAA,EACF;AAEJ;AAeA,SAASU,EAAO;AAAA,EACd,cAAA3C;AAAA,EACA,aAAAiB;AAAA,EACA,YAAAE;AAAA,EACA,QAAAW;AAAA,EACA,WAAAZ;AAAA,EACA,YAAAa;AAAA,EACA,YAAAa;AAAA,EACA,gBAAAC;AACF,GAAuB;AACf,QAAAX,IAAc,CAACC,GAAkBC,MACjCL,IACKA,EAAWI,GAASC,CAAK,IAIhC,gBAAAb,EAAC,OAAI,EAAA,WAAU,OACb,UAAA,gBAAAA;AAAA,IAACc;AAAA,IAAA;AAAA,MAEC,SAAAF;AAAA,MACA,oBAAkB;AAAA,MAClB,SAASU;AAAA,IAAA;AAAA,IAHJV,EAAQ;AAAA,EAAA,GAKjB;AAKF,SAAA,gBAAAZ,EAACxB,KAAe,cAAAC,GACd,UAAA,gBAAAsB,EAAC,SAAI,WAAWG,EAAG,0BAA0BP,CAAS,GACpD,UAAA;AAAA,IAAA,gBAAAK,EAAC,SAAI,WAAU,+DACb,4BAACP,GAAY,EAAA,aAAAC,GAA0B,YAAAE,GAAwB,EACjE,CAAA;AAAA,IACA,gBAAAI,EAAC,OAAI,EAAA,WAAU,OAAO,CAAA;AAAA,IACtB,gBAAAA;AAAA,MAACM;AAAA,MAAA;AAAA,QACC,QAAAC;AAAA,QACA,YAAYI;AAAA,QACZ,YAAAU;AAAA,QACA,eAAa;AAAA,MAAA;AAAA,IAAA;AAAA,EACf,EAAA,CACF,EACF,CAAA;AAEJ;"}
@@ -1 +1 @@
1
- {"version":3,"file":"transition-link.js","sources":["../../../src/components/navigation/transition-link.tsx"],"sourcesContent":["import {forwardRef, AnchorHTMLAttributes} from 'react'\n\nimport {useHref} from 'react-router'\n\nimport {useNavigateWithTransition} from '../../hooks/navigation/useNavigateWithTransition'\n\nexport interface TransitionLinkDocProps {\n /** The target path to navigate to */\n to: string\n /** Click handler called before navigation */\n onClick?: React.MouseEventHandler<HTMLAnchorElement>\n /** Content to render inside the link */\n children?: React.ReactNode\n}\n\nexport interface TransitionLinkProps\n extends AnchorHTMLAttributes<HTMLAnchorElement> {\n to: string\n}\n\nconst ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i\n\nexport const TransitionLink = forwardRef<\n HTMLAnchorElement,\n TransitionLinkProps\n>(({onClick, to, children, ...props}, forwardedRef) => {\n const transitionNavigate = useNavigateWithTransition()\n\n const isAbsolute = typeof to === 'string' && ABSOLUTE_URL_REGEX.test(to)\n\n if (isAbsolute) {\n console.warn(\n `TransitionLink: absolute URLs are not supported. Please update to a valid relative path.`\n )\n }\n\n const href = useHref(to)\n\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (onClick) onClick(event)\n\n if (!event.defaultPrevented) {\n event.preventDefault()\n transitionNavigate(to)\n }\n }\n\n return (\n <a\n {...props}\n onClick={handleClick}\n href={isAbsolute ? undefined : href}\n ref={forwardedRef}\n >\n {children}\n </a>\n )\n})\n"],"names":["ABSOLUTE_URL_REGEX","TransitionLink","forwardRef","onClick","to","children","props","forwardedRef","transitionNavigate","useNavigateWithTransition","isAbsolute","href","useHref","jsx","event"],"mappings":";;;;AAoBA,MAAMA,IAAqB,iCAEdC,IAAiBC,EAG5B,CAAC,EAAC,SAAAC,GAAS,IAAAC,GAAI,UAAAC,GAAU,GAAGC,EAAK,GAAGC,MAAiB;AACrD,QAAMC,IAAqBC,EAA0B,GAE/CC,IAAa,OAAON,KAAO,YAAYJ,EAAmB,KAAKI,CAAE;AAEvE,EAAIM,KACM,QAAA;AAAA,IACN;AAAA,EACF;AAGI,QAAAC,IAAOC,EAAQR,CAAE;AAYrB,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAGP;AAAA,MACJ,SAZgB,CAACQ,MAA+C;AAC9D,QAAAX,OAAiBW,CAAK,GAErBA,EAAM,qBACTA,EAAM,eAAe,GACrBN,EAAmBJ,CAAE;AAAA,MAEzB;AAAA,MAMI,MAAMM,IAAa,SAAYC;AAAA,MAC/B,KAAKJ;AAAA,MAEJ,UAAAF;AAAA,IAAA;AAAA,EACH;AAEJ,CAAC;"}
1
+ {"version":3,"file":"transition-link.js","sources":["../../../src/components/navigation/transition-link.tsx"],"sourcesContent":["import {forwardRef, AnchorHTMLAttributes} from 'react'\n\nimport {useHref} from 'react-router'\n\nimport {useNavigateWithTransition} from '../../hooks/navigation/useNavigateWithTransition'\n\nexport interface TransitionLinkDocProps {\n /** The target path to navigate to */\n to: string\n /** Click handler called before navigation */\n onClick?: React.MouseEventHandler<HTMLAnchorElement>\n /** Content to render inside the link */\n children?: React.ReactNode\n}\n\nexport interface TransitionLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n to: string\n}\n\nconst ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|\\/\\/)/i\n\nexport const TransitionLink = forwardRef<\n HTMLAnchorElement,\n TransitionLinkProps\n>(({onClick, to, children, ...props}, forwardedRef) => {\n const transitionNavigate = useNavigateWithTransition()\n\n const isAbsolute = typeof to === 'string' && ABSOLUTE_URL_REGEX.test(to)\n\n if (isAbsolute) {\n console.warn(\n `TransitionLink: absolute URLs are not supported. Please update to a valid relative path.`\n )\n }\n\n const href = useHref(to)\n\n const handleClick = (event: React.MouseEvent<HTMLAnchorElement>) => {\n if (onClick) onClick(event)\n\n if (!event.defaultPrevented) {\n event.preventDefault()\n transitionNavigate(to)\n }\n }\n\n return (\n <a\n {...props}\n onClick={handleClick}\n href={isAbsolute ? undefined : href}\n ref={forwardedRef}\n >\n {children}\n </a>\n )\n})\n"],"names":["ABSOLUTE_URL_REGEX","TransitionLink","forwardRef","onClick","to","children","props","forwardedRef","transitionNavigate","useNavigateWithTransition","isAbsolute","href","useHref","jsx","event"],"mappings":";;;;AAmBA,MAAMA,IAAqB,iCAEdC,IAAiBC,EAG5B,CAAC,EAAC,SAAAC,GAAS,IAAAC,GAAI,UAAAC,GAAU,GAAGC,EAAK,GAAGC,MAAiB;AACrD,QAAMC,IAAqBC,EAA0B,GAE/CC,IAAa,OAAON,KAAO,YAAYJ,EAAmB,KAAKI,CAAE;AAEvE,EAAIM,KACM,QAAA;AAAA,IACN;AAAA,EACF;AAGI,QAAAC,IAAOC,EAAQR,CAAE;AAYrB,SAAA,gBAAAS;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAGP;AAAA,MACJ,SAZgB,CAACQ,MAA+C;AAC9D,QAAAX,OAAiBW,CAAK,GAErBA,EAAM,qBACTA,EAAM,eAAe,GACrBN,EAAmBJ,CAAE;AAAA,MAEzB;AAAA,MAMI,MAAMM,IAAa,SAAYC;AAAA,MAC/B,KAAKJ;AAAA,MAEJ,UAAAF;AAAA,IAAA;AAAA,EACH;AAEJ,CAAC;"}
@@ -0,0 +1,43 @@
1
+ import { useMemo as f } from "react";
2
+ import { useDeeplink as p } from "../navigation/useDeeplink.js";
3
+ function q() {
4
+ const { queryParams: a } = p();
5
+ return f(() => {
6
+ const c = a?.intentQuery;
7
+ if (!c) return { query: null, data: null };
8
+ let e;
9
+ try {
10
+ e = decodeURIComponent(c);
11
+ } catch {
12
+ e = c;
13
+ }
14
+ const u = e.indexOf(":");
15
+ if (u === -1) return { query: null, data: null };
16
+ const y = e.slice(0, u), r = e.slice(u + 1), l = r.indexOf(",");
17
+ let o = l === -1 ? r : r.slice(0, l), s = l === -1 ? null : r.slice(l + 1) || null;
18
+ const n = o.match(/^gid:\/\/shopify\/(\w+)(?:\/(.+))?$/);
19
+ if (n && (o = `shopify/${n[1]}`, s = n[2] ? `gid://shopify/${n[1]}/${n[2]}` : null), !o) return { query: null, data: null };
20
+ let d = null;
21
+ const i = a?.intentData;
22
+ if (i) {
23
+ let t;
24
+ try {
25
+ t = JSON.parse(i);
26
+ } catch {
27
+ try {
28
+ t = JSON.parse(decodeURIComponent(i));
29
+ } catch {
30
+ }
31
+ }
32
+ t !== null && typeof t == "object" && !Array.isArray(t) && (d = t);
33
+ }
34
+ return {
35
+ query: { action: y, type: o, value: s },
36
+ data: d
37
+ };
38
+ }, [a]);
39
+ }
40
+ export {
41
+ q as useIntent
42
+ };
43
+ //# sourceMappingURL=useIntent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useIntent.js","sources":["../../../src/hooks/intents/useIntent.ts"],"sourcesContent":["import {useMemo} from 'react'\n\nimport {useDeeplink} from '../navigation/useDeeplink'\n\n/**\n * Structured description of an intent, following the Shopify Intents API\n * URI format: `action:type,value`\n *\n * @see https://shopify.dev/docs/api/admin-extensions/latest/target-apis/utility-apis/intents-api\n */\nexport interface IntentQuery {\n /** Verb describing the operation (e.g., 'try_on', 'create', 'edit') */\n action: string\n /** Resource type identifier (e.g., 'shopify/Product', 'shop/UserImage') */\n type: string\n /** Resource GID (e.g., 'gid://shopify/Product/123') if applicable */\n value: string | null\n}\n\nexport interface UseIntentReturn {\n /** Parsed intent query, or null if no intent was passed */\n query: IntentQuery | null\n /** Additional JSON data passed with the intent, or null */\n data: {[key: string]: unknown} | null\n}\n\n/**\n * Parses an intent passed to this mini via deeplink.\n *\n * Follows the Shopify Intents API URI format (`action:type,value`) with\n * an optional JSON data payload passed separately.\n *\n * @see https://shopify.dev/docs/api/admin-extensions/latest/target-apis/utility-apis/intents-api\n *\n * Deeplink format:\n * ?intentQuery=action:type,value&intentData={\"key\":\"value\"}\n *\n * Examples:\n * ?intentQuery=try_on:shopify/Product,gid://shopify/Product/123\n * ?intentQuery=create:shopify/Product\n * ?intentQuery=edit:shopify/Product,gid://shopify/Product/123&intentData={\"variantId\":\"456\"}\n *\n * Shorthand GID syntax (type inferred from GID):\n * ?intentQuery=edit:gid://shopify/Product/123\n * ?intentQuery=create:gid://shopify/Product\n *\n * Use this hook to receive intents from the host app (Host → Mini direction).\n */\nexport function useIntent(): UseIntentReturn {\n const {queryParams} = useDeeplink()\n\n return useMemo(() => {\n const raw = queryParams?.intentQuery\n\n if (!raw) return {query: null, data: null}\n\n let decoded: string\n try {\n decoded = decodeURIComponent(raw)\n } catch {\n decoded = raw\n }\n\n const colonIdx = decoded.indexOf(':')\n if (colonIdx === -1) return {query: null, data: null}\n\n const action = decoded.slice(0, colonIdx)\n const rest = decoded.slice(colonIdx + 1)\n const commaIdx = rest.indexOf(',')\n\n let type = commaIdx === -1 ? rest : rest.slice(0, commaIdx)\n let value = commaIdx === -1 ? null : rest.slice(commaIdx + 1) || null\n\n // Shorthand GID syntax: edit:gid://shopify/Product/123\n // Infer type from GID and use full GID as value\n const gidMatch = type.match(/^gid:\\/\\/shopify\\/(\\w+)(?:\\/(.+))?$/)\n if (gidMatch) {\n type = `shopify/${gidMatch[1]}`\n value = gidMatch[2] ? `gid://shopify/${gidMatch[1]}/${gidMatch[2]}` : null\n }\n\n if (!type) return {query: null, data: null}\n\n let data: {[key: string]: unknown} | null = null\n const rawData = queryParams?.intentData\n if (rawData) {\n let parsed: unknown\n try {\n parsed = JSON.parse(rawData)\n } catch {\n try {\n parsed = JSON.parse(decodeURIComponent(rawData))\n } catch {\n // malformed JSON — ignore\n }\n }\n if (\n parsed !== null &&\n typeof parsed === 'object' &&\n !Array.isArray(parsed)\n ) {\n data = parsed as {[key: string]: unknown}\n }\n }\n\n return {\n query: {action, type, value},\n data,\n }\n }, [queryParams])\n}\n"],"names":["useIntent","queryParams","useDeeplink","useMemo","raw","decoded","colonIdx","action","rest","commaIdx","type","value","gidMatch","data","rawData","parsed"],"mappings":";;AAgDO,SAASA,IAA6B;AACrC,QAAA,EAAC,aAAAC,EAAW,IAAIC,EAAY;AAElC,SAAOC,EAAQ,MAAM;AACnB,UAAMC,IAAMH,GAAa;AAEzB,QAAI,CAACG,EAAK,QAAO,EAAC,OAAO,MAAM,MAAM,KAAI;AAErC,QAAAC;AACA,QAAA;AACF,MAAAA,IAAU,mBAAmBD,CAAG;AAAA,IAAA,QAC1B;AACI,MAAAC,IAAAD;AAAA,IAAA;AAGN,UAAAE,IAAWD,EAAQ,QAAQ,GAAG;AACpC,QAAIC,MAAa,GAAI,QAAO,EAAC,OAAO,MAAM,MAAM,KAAI;AAEpD,UAAMC,IAASF,EAAQ,MAAM,GAAGC,CAAQ,GAClCE,IAAOH,EAAQ,MAAMC,IAAW,CAAC,GACjCG,IAAWD,EAAK,QAAQ,GAAG;AAEjC,QAAIE,IAAOD,MAAa,KAAKD,IAAOA,EAAK,MAAM,GAAGC,CAAQ,GACtDE,IAAQF,MAAa,KAAK,OAAOD,EAAK,MAAMC,IAAW,CAAC,KAAK;AAI3D,UAAAG,IAAWF,EAAK,MAAM,qCAAqC;AAMjE,QALIE,MACKF,IAAA,WAAWE,EAAS,CAAC,CAAC,IACrBD,IAAAC,EAAS,CAAC,IAAI,iBAAiBA,EAAS,CAAC,CAAC,IAAIA,EAAS,CAAC,CAAC,KAAK,OAGpE,CAACF,EAAM,QAAO,EAAC,OAAO,MAAM,MAAM,KAAI;AAE1C,QAAIG,IAAwC;AAC5C,UAAMC,IAAUb,GAAa;AAC7B,QAAIa,GAAS;AACP,UAAAC;AACA,UAAA;AACO,QAAAA,IAAA,KAAK,MAAMD,CAAO;AAAA,MAAA,QACrB;AACF,YAAA;AACF,UAAAC,IAAS,KAAK,MAAM,mBAAmBD,CAAO,CAAC;AAAA,QAAA,QACzC;AAAA,QAAA;AAAA,MAER;AAGA,MAAAC,MAAW,QACX,OAAOA,KAAW,YAClB,CAAC,MAAM,QAAQA,CAAM,MAEdF,IAAAE;AAAA,IACT;AAGK,WAAA;AAAA,MACL,OAAO,EAAC,QAAAR,GAAQ,MAAAG,GAAM,OAAAC,EAAK;AAAA,MAC3B,MAAAE;AAAA,IACF;AAAA,EAAA,GACC,CAACZ,CAAW,CAAC;AAClB;"}
package/dist/index.js CHANGED
@@ -87,21 +87,22 @@ import { useShare as Ht } from "./hooks/util/useShare.js";
87
87
  import { useImagePicker as zt } from "./hooks/util/useImagePicker.js";
88
88
  import { useKeyboardAvoidingView as Yt } from "./hooks/util/useKeyboardAvoidingView.js";
89
89
  import { useRequestPermissions as Kt } from "./hooks/util/useRequestPermissions.js";
90
- import { useOnMiniFocus as Zt } from "./hooks/events/useOnMiniFocus.js";
91
- import { useOnMiniBlur as Jt } from "./hooks/events/useOnMiniBlur.js";
92
- import { useOnMiniClose as $t } from "./hooks/events/useOnMiniClose.js";
93
- import { useOnAppStateChange as ea } from "./hooks/events/useOnAppStateChange.js";
94
- import { useOnNavigateBack as ta } from "./hooks/events/useOnNavigateBack.js";
95
- import { buildDeeplinkUrl as pa } from "./utils/buildDeeplinkUrl.js";
96
- import { MiniEntityNotFoundError as ma, MiniError as na, MiniNetworkError as sa, formatError as la } from "./utils/errors.js";
97
- import { extractBrandTheme as fa, formatReviewCount as xa, getFeaturedImages as ca, normalizeRating as da } from "./utils/merchant-card.js";
98
- import { parseUrl as ga } from "./utils/parseUrl.js";
99
- import { dataURLToBlob as Aa, fileToDataUri as Da, getResizedImageUrl as Pa, getThumbhashBlobURL as Ta, getThumbhashDataURL as ha } from "./utils/image.js";
100
- import { formatMoney as Ra } from "./utils/formatMoney.js";
101
- import { UserState as wa, UserTokenGenerateUserErrorCode as Ba } from "./shop-minis-platform/src/types/user.js";
102
- import { ContentCreateUserErrorCode as Ma, MinisContentStatus as Fa } from "./shop-minis-platform/src/types/content.js";
103
- import { Social as ba } from "./shop-minis-platform/src/types/share.js";
104
- import { DATA_FETCHING_DEFAULT_FETCH_POLICY as ka, DATA_FETCHING_DEFAULT_PAGE_SIZE as Na } from "./shop-minis-platform/src/constants.js";
90
+ import { useIntent as Zt } from "./hooks/intents/useIntent.js";
91
+ import { useOnMiniFocus as Jt } from "./hooks/events/useOnMiniFocus.js";
92
+ import { useOnMiniBlur as $t } from "./hooks/events/useOnMiniBlur.js";
93
+ import { useOnMiniClose as ea } from "./hooks/events/useOnMiniClose.js";
94
+ import { useOnAppStateChange as ta } from "./hooks/events/useOnAppStateChange.js";
95
+ import { useOnNavigateBack as pa } from "./hooks/events/useOnNavigateBack.js";
96
+ import { buildDeeplinkUrl as ma } from "./utils/buildDeeplinkUrl.js";
97
+ import { MiniEntityNotFoundError as sa, MiniError as la, MiniNetworkError as ua, formatError as fa } from "./utils/errors.js";
98
+ import { extractBrandTheme as ca, formatReviewCount as da, getFeaturedImages as Ca, normalizeRating as ga } from "./utils/merchant-card.js";
99
+ import { parseUrl as Aa } from "./utils/parseUrl.js";
100
+ import { dataURLToBlob as Pa, fileToDataUri as Ta, getResizedImageUrl as ha, getThumbhashBlobURL as Ia, getThumbhashDataURL as Ra } from "./utils/image.js";
101
+ import { formatMoney as wa } from "./utils/formatMoney.js";
102
+ import { UserState as Ea, UserTokenGenerateUserErrorCode as Ma } from "./shop-minis-platform/src/types/user.js";
103
+ import { ContentCreateUserErrorCode as Ua, MinisContentStatus as ba } from "./shop-minis-platform/src/types/content.js";
104
+ import { Social as ka } from "./shop-minis-platform/src/types/share.js";
105
+ import { DATA_FETCHING_DEFAULT_FETCH_POLICY as Oa, DATA_FETCHING_DEFAULT_PAGE_SIZE as _a } from "./shop-minis-platform/src/constants.js";
105
106
  export {
106
107
  Tr as Accordion,
107
108
  hr as AccordionContent,
@@ -142,10 +143,10 @@ export {
142
143
  me as CarouselNext,
143
144
  ne as CarouselPrevious,
144
145
  le as Checkbox,
145
- Ma as ContentCreateUserErrorCode,
146
+ Ua as ContentCreateUserErrorCode,
146
147
  cr as ContentWrapper,
147
- ka as DATA_FETCHING_DEFAULT_FETCH_POLICY,
148
- Na as DATA_FETCHING_DEFAULT_PAGE_SIZE,
148
+ Oa as DATA_FETCHING_DEFAULT_FETCH_POLICY,
149
+ _a as DATA_FETCHING_DEFAULT_PAGE_SIZE,
149
150
  o as DATA_NAVIGATION_TYPE_ATTRIBUTE,
150
151
  fe as Dialog,
151
152
  xe as DialogClose,
@@ -182,11 +183,11 @@ export {
182
183
  B as MerchantCardName,
183
184
  E as MerchantCardRating,
184
185
  b as MerchantCardSkeleton,
185
- ma as MiniEntityNotFoundError,
186
- na as MiniError,
187
- sa as MiniNetworkError,
186
+ sa as MiniEntityNotFoundError,
187
+ la as MiniError,
188
+ ua as MiniNetworkError,
188
189
  p as MinisContainer,
189
- Fa as MinisContentStatus,
190
+ ba as MinisContentStatus,
190
191
  q as MinisRouter,
191
192
  t as NAVIGATION_TYPES,
192
193
  u as ProductCard,
@@ -236,29 +237,29 @@ export {
236
237
  Co as SheetTitle,
237
238
  go as SheetTrigger,
238
239
  ho as Skeleton,
239
- ba as Social,
240
+ ka as Social,
240
241
  Dr as StaticArea,
241
242
  fr as TextInput,
242
243
  Ao as Toaster,
243
244
  or as Touchable,
244
245
  Q as TransitionLink,
245
- wa as UserState,
246
- Ba as UserTokenGenerateUserErrorCode,
246
+ Ea as UserState,
247
+ Ma as UserTokenGenerateUserErrorCode,
247
248
  lr as VideoPlayer,
248
249
  Qr as badgeVariants,
249
- pa as buildDeeplinkUrl,
250
- Aa as dataURLToBlob,
251
- fa as extractBrandTheme,
252
- Da as fileToDataUri,
253
- la as formatError,
254
- Ra as formatMoney,
255
- xa as formatReviewCount,
256
- ca as getFeaturedImages,
257
- Pa as getResizedImageUrl,
258
- Ta as getThumbhashBlobURL,
259
- ha as getThumbhashDataURL,
260
- da as normalizeRating,
261
- ga as parseUrl,
250
+ ma as buildDeeplinkUrl,
251
+ Pa as dataURLToBlob,
252
+ ca as extractBrandTheme,
253
+ Ta as fileToDataUri,
254
+ fa as formatError,
255
+ wa as formatMoney,
256
+ da as formatReviewCount,
257
+ Ca as getFeaturedImages,
258
+ ha as getResizedImageUrl,
259
+ Ia as getThumbhashBlobURL,
260
+ Ra as getThumbhashDataURL,
261
+ ga as normalizeRating,
262
+ Aa as parseUrl,
262
263
  Po as toast,
263
264
  xt as useARPreview,
264
265
  dt as useAsyncStorage,
@@ -274,13 +275,14 @@ export {
274
275
  zo as useGenerateUserToken,
275
276
  zt as useImagePicker,
276
277
  At as useImageUpload,
278
+ Zt as useIntent,
277
279
  Yt as useKeyboardAvoidingView,
278
280
  wt as useNavigateWithTransition,
279
- ea as useOnAppStateChange,
280
- Jt as useOnMiniBlur,
281
- $t as useOnMiniClose,
282
- Zt as useOnMiniFocus,
283
- ta as useOnNavigateBack,
281
+ ta as useOnAppStateChange,
282
+ $t as useOnMiniBlur,
283
+ ea as useOnMiniClose,
284
+ Jt as useOnMiniFocus,
285
+ pa as useOnNavigateBack,
284
286
  yo as useOrders,
285
287
  ut as usePopularProducts,
286
288
  Jo as useProduct,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"product-review-stars.js","sources":["../../../src/internal/components/product-review-stars.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {Star, StarHalf} from 'lucide-react'\n\nimport {cn} from '../../lib/utils'\nimport {formatReviewCount, normalizeRating} from '../../utils/merchant-card'\n\nconst starSizeClasses = {\n sm: 'h-3 w-3',\n md: 'h-4 w-4',\n lg: 'h-5 w-5',\n} as const\n\nconst textSizeClasses = {\n sm: 'text-xs',\n md: 'text-sm',\n lg: 'text-base',\n} as const\n\nexport interface ProductReviewStarsProps\n extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Average rating value (0-5)\n */\n averageRating: number\n /**\n * Total number of reviews\n */\n reviewCount?: number | null\n /**\n * Custom color for filled star\n * @default 'var(--grayscale-d100)' (black)\n */\n filledStarColor?: string\n /**\n * Custom color for empty star\n * @default 'var(--grayscale-l20)' (light gray)\n */\n emptyStarColor?: string\n /**\n * Size of the stars\n * @default 'sm'\n */\n size?: 'sm' | 'md' | 'lg'\n}\n\nexport const ProductReviewStars = React.forwardRef<\n HTMLDivElement,\n ProductReviewStarsProps\n>(\n (\n {\n averageRating,\n reviewCount,\n size = 'sm',\n filledStarColor = 'var(--grayscale-d100)',\n emptyStarColor = 'var(--grayscale-l20)',\n className,\n ...props\n },\n ref\n ) => {\n const normalizedRating = Math.min(5, normalizeRating(averageRating))\n const filledStars = Math.floor(normalizedRating)\n const remainder = normalizedRating % 1\n\n return (\n <div\n ref={ref}\n className={cn('flex items-center', className)}\n data-slot=\"product-review-stars\"\n {...props}\n >\n <div className=\"relative\">\n <div className=\"flex\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n className={cn(starSizeClasses[size])}\n style={{fill: emptyStarColor}}\n strokeWidth={0}\n />\n ))}\n </div>\n <div className=\"flex absolute top-0 left-0\">\n {Array.from({length: filledStars}, (_, i) => (\n <Star\n key={i}\n className={cn(starSizeClasses[size])}\n style={{fill: filledStarColor}}\n strokeWidth={0}\n />\n ))}\n {remainder >= 0.5 && (\n <StarHalf\n className={cn(starSizeClasses[size])}\n style={{fill: filledStarColor}}\n strokeWidth={0}\n />\n )}\n </div>\n </div>\n {reviewCount && (\n <span className={cn(textSizeClasses[size], 'ml-1')}>\n ({formatReviewCount(reviewCount)})\n </span>\n )}\n </div>\n )\n }\n)\n\nProductReviewStars.displayName = 'ProductReviewStars'\n"],"names":["starSizeClasses","textSizeClasses","ProductReviewStars","React","averageRating","reviewCount","size","filledStarColor","emptyStarColor","className","props","ref","normalizedRating","normalizeRating","filledStars","remainder","jsxs","cn","jsx","_","i","Star","StarHalf","formatReviewCount"],"mappings":";;;;;;AAOA,MAAMA,IAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GAEMC,IAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GA6BaC,IAAqBC,EAAM;AAAA,EAItC,CACE;AAAA,IACE,eAAAC;AAAA,IACA,aAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,iBAAAC,IAAkB;AAAA,IAClB,gBAAAC,IAAiB;AAAA,IACjB,WAAAC;AAAA,IACA,GAAGC;AAAA,KAELC,MACG;AACH,UAAMC,IAAmB,KAAK,IAAI,GAAGC,EAAgBT,CAAa,CAAC,GAC7DU,IAAc,KAAK,MAAMF,CAAgB,GACzCG,IAAYH,IAAmB;AAGnC,WAAA,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAL;AAAA,QACA,WAAWM,EAAG,qBAAqBR,CAAS;AAAA,QAC5C,aAAU;AAAA,QACT,GAAGC;AAAA,QAEJ,UAAA;AAAA,UAAC,gBAAAM,EAAA,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,YAAC,gBAAAE,EAAA,OAAA,EAAI,WAAU,QACZ,UAAM,MAAA,KAAK,EAAC,QAAQ,EAAC,GAAG,CAACC,GAAGC,MAC3B,gBAAAF;AAAA,cAACG;AAAA,cAAA;AAAA,gBAEC,WAAWJ,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,gBACnC,OAAO,EAAC,MAAME,EAAc;AAAA,gBAC5B,aAAa;AAAA,cAAA;AAAA,cAHRY;AAAA,YAKR,CAAA,GACH;AAAA,YACA,gBAAAJ,EAAC,OAAI,EAAA,WAAU,8BACZ,UAAA;AAAA,cAAA,MAAM,KAAK,EAAC,QAAQF,KAAc,CAACK,GAAGC,MACrC,gBAAAF;AAAA,gBAACG;AAAA,gBAAA;AAAA,kBAEC,WAAWJ,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,kBACnC,OAAO,EAAC,MAAMC,EAAe;AAAA,kBAC7B,aAAa;AAAA,gBAAA;AAAA,gBAHRa;AAAA,cAAA,CAKR;AAAA,cACAL,KAAa,OACZ,gBAAAG;AAAA,gBAACI;AAAA,gBAAA;AAAA,kBACC,WAAWL,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,kBACnC,OAAO,EAAC,MAAMC,EAAe;AAAA,kBAC7B,aAAa;AAAA,gBAAA;AAAA,cAAA;AAAA,YACf,EAEJ,CAAA;AAAA,UAAA,GACF;AAAA,UACCF,uBACE,QAAK,EAAA,WAAWY,EAAGhB,EAAgBK,CAAI,GAAG,MAAM,GAAG,UAAA;AAAA,YAAA;AAAA,YAChDiB,EAAkBlB,CAAW;AAAA,YAAE;AAAA,UAAA,EACnC,CAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAGN;AAEAH,EAAmB,cAAc;"}
1
+ {"version":3,"file":"product-review-stars.js","sources":["../../../src/internal/components/product-review-stars.tsx"],"sourcesContent":["import * as React from 'react'\n\nimport {Star, StarHalf} from 'lucide-react'\n\nimport {cn} from '../../lib/utils'\nimport {formatReviewCount, normalizeRating} from '../../utils/merchant-card'\n\nconst starSizeClasses = {\n sm: 'h-3 w-3',\n md: 'h-4 w-4',\n lg: 'h-5 w-5',\n} as const\n\nconst textSizeClasses = {\n sm: 'text-xs',\n md: 'text-sm',\n lg: 'text-base',\n} as const\n\nexport interface ProductReviewStarsProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Average rating value (0-5)\n */\n averageRating: number\n /**\n * Total number of reviews\n */\n reviewCount?: number | null\n /**\n * Custom color for filled star\n * @default 'var(--grayscale-d100)' (black)\n */\n filledStarColor?: string\n /**\n * Custom color for empty star\n * @default 'var(--grayscale-l20)' (light gray)\n */\n emptyStarColor?: string\n /**\n * Size of the stars\n * @default 'sm'\n */\n size?: 'sm' | 'md' | 'lg'\n}\n\nexport const ProductReviewStars = React.forwardRef<\n HTMLDivElement,\n ProductReviewStarsProps\n>(\n (\n {\n averageRating,\n reviewCount,\n size = 'sm',\n filledStarColor = 'var(--grayscale-d100)',\n emptyStarColor = 'var(--grayscale-l20)',\n className,\n ...props\n },\n ref\n ) => {\n const normalizedRating = Math.min(5, normalizeRating(averageRating))\n const filledStars = Math.floor(normalizedRating)\n const remainder = normalizedRating % 1\n\n return (\n <div\n ref={ref}\n className={cn('flex items-center', className)}\n data-slot=\"product-review-stars\"\n {...props}\n >\n <div className=\"relative\">\n <div className=\"flex\">\n {Array.from({length: 5}, (_, i) => (\n <Star\n key={i}\n className={cn(starSizeClasses[size])}\n style={{fill: emptyStarColor}}\n strokeWidth={0}\n />\n ))}\n </div>\n <div className=\"flex absolute top-0 left-0\">\n {Array.from({length: filledStars}, (_, i) => (\n <Star\n key={i}\n className={cn(starSizeClasses[size])}\n style={{fill: filledStarColor}}\n strokeWidth={0}\n />\n ))}\n {remainder >= 0.5 && (\n <StarHalf\n className={cn(starSizeClasses[size])}\n style={{fill: filledStarColor}}\n strokeWidth={0}\n />\n )}\n </div>\n </div>\n {reviewCount && (\n <span className={cn(textSizeClasses[size], 'ml-1')}>\n ({formatReviewCount(reviewCount)})\n </span>\n )}\n </div>\n )\n }\n)\n\nProductReviewStars.displayName = 'ProductReviewStars'\n"],"names":["starSizeClasses","textSizeClasses","ProductReviewStars","React","averageRating","reviewCount","size","filledStarColor","emptyStarColor","className","props","ref","normalizedRating","normalizeRating","filledStars","remainder","jsxs","cn","jsx","_","i","Star","StarHalf","formatReviewCount"],"mappings":";;;;;;AAOA,MAAMA,IAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GAEMC,IAAkB;AAAA,EACtB,IAAI;AAAA,EACJ,IAAI;AAAA,EACJ,IAAI;AACN,GA4BaC,IAAqBC,EAAM;AAAA,EAItC,CACE;AAAA,IACE,eAAAC;AAAA,IACA,aAAAC;AAAA,IACA,MAAAC,IAAO;AAAA,IACP,iBAAAC,IAAkB;AAAA,IAClB,gBAAAC,IAAiB;AAAA,IACjB,WAAAC;AAAA,IACA,GAAGC;AAAA,KAELC,MACG;AACH,UAAMC,IAAmB,KAAK,IAAI,GAAGC,EAAgBT,CAAa,CAAC,GAC7DU,IAAc,KAAK,MAAMF,CAAgB,GACzCG,IAAYH,IAAmB;AAGnC,WAAA,gBAAAI;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,KAAAL;AAAA,QACA,WAAWM,EAAG,qBAAqBR,CAAS;AAAA,QAC5C,aAAU;AAAA,QACT,GAAGC;AAAA,QAEJ,UAAA;AAAA,UAAC,gBAAAM,EAAA,OAAA,EAAI,WAAU,YACb,UAAA;AAAA,YAAC,gBAAAE,EAAA,OAAA,EAAI,WAAU,QACZ,UAAM,MAAA,KAAK,EAAC,QAAQ,EAAC,GAAG,CAACC,GAAGC,MAC3B,gBAAAF;AAAA,cAACG;AAAA,cAAA;AAAA,gBAEC,WAAWJ,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,gBACnC,OAAO,EAAC,MAAME,EAAc;AAAA,gBAC5B,aAAa;AAAA,cAAA;AAAA,cAHRY;AAAA,YAKR,CAAA,GACH;AAAA,YACA,gBAAAJ,EAAC,OAAI,EAAA,WAAU,8BACZ,UAAA;AAAA,cAAA,MAAM,KAAK,EAAC,QAAQF,KAAc,CAACK,GAAGC,MACrC,gBAAAF;AAAA,gBAACG;AAAA,gBAAA;AAAA,kBAEC,WAAWJ,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,kBACnC,OAAO,EAAC,MAAMC,EAAe;AAAA,kBAC7B,aAAa;AAAA,gBAAA;AAAA,gBAHRa;AAAA,cAAA,CAKR;AAAA,cACAL,KAAa,OACZ,gBAAAG;AAAA,gBAACI;AAAA,gBAAA;AAAA,kBACC,WAAWL,EAAGjB,EAAgBM,CAAI,CAAC;AAAA,kBACnC,OAAO,EAAC,MAAMC,EAAe;AAAA,kBAC7B,aAAa;AAAA,gBAAA;AAAA,cAAA;AAAA,YACf,EAEJ,CAAA;AAAA,UAAA,GACF;AAAA,UACCF,uBACE,QAAK,EAAA,WAAWY,EAAGhB,EAAgBK,CAAI,GAAG,MAAM,GAAG,UAAA;AAAA,YAAA;AAAA,YAChDiB,EAAkBlB,CAAW;AAAA,YAAE;AAAA,UAAA,EACnC,CAAA;AAAA,QAAA;AAAA,MAAA;AAAA,IAEJ;AAAA,EAAA;AAGN;AAEAH,EAAmB,cAAc;"}
@@ -1,4 +1,4 @@
1
- import { s as r } from "../../../../../../../../_virtual/index7.js";
1
+ import { s as r } from "../../../../../../../../_virtual/index6.js";
2
2
  function s() {
3
3
  return r.useSyncExternalStore(
4
4
  e,
@@ -1,4 +1,4 @@
1
- import { __module as q } from "../../../../../../../../_virtual/index5.js";
1
+ import { __module as q } from "../../../../../../../../_virtual/index4.js";
2
2
  import { __require as F } from "../../../../../global@4.4.0/node_modules/global/window.js";
3
3
  import { __require as N } from "../../../../../@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/extends.js";
4
4
  import { __require as J } from "../../../../../is-function@1.0.2/node_modules/is-function/index.js";
@@ -1,4 +1,4 @@
1
- import { __exports as r } from "../../../../../../../../_virtual/index9.js";
1
+ import { __exports as r } from "../../../../../../../../_virtual/index7.js";
2
2
  import { __require as a } from "./dom.js";
3
3
  import { __require as o } from "./dom-parser.js";
4
4
  var i;