@shopify/shop-minis-react 0.9.0 → 0.11.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.
@@ -0,0 +1,37 @@
1
+ import { jsx as f } from "react/jsx-runtime";
2
+ import { useSafeArea as g } from "../../hooks/util/useSafeArea.js";
3
+ const m = ["top", "right", "bottom", "left"], d = {
4
+ top: "var(--safe-area-inset-top, 0px)",
5
+ right: "var(--safe-area-inset-right, 0px)",
6
+ bottom: "var(--safe-area-inset-bottom, 0px)",
7
+ left: "var(--safe-area-inset-left, 0px)"
8
+ }, c = {
9
+ padding: {
10
+ top: "paddingTop",
11
+ right: "paddingRight",
12
+ bottom: "paddingBottom",
13
+ left: "paddingLeft"
14
+ },
15
+ margin: {
16
+ top: "marginTop",
17
+ right: "marginRight",
18
+ bottom: "marginBottom",
19
+ left: "marginLeft"
20
+ }
21
+ };
22
+ function x({
23
+ edges: e = m,
24
+ mode: a = "padding",
25
+ className: i,
26
+ style: r,
27
+ children: n
28
+ }) {
29
+ const o = { ...r }, s = c[a], p = g();
30
+ for (const t of e)
31
+ p?.[t] > 0 && (o[s[t]] = d[t]);
32
+ return /* @__PURE__ */ f("div", { className: i, style: o, children: n });
33
+ }
34
+ export {
35
+ x as SafeArea
36
+ };
37
+ //# sourceMappingURL=safe-area.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"safe-area.js","sources":["../../../src/components/atoms/safe-area.tsx"],"sourcesContent":["import {useSafeArea} from '../../hooks/util/useSafeArea'\n\ntype Edge = 'top' | 'right' | 'bottom' | 'left'\n\nexport interface SafeAreaDocProps {\n /** Which edges to apply safe area insets to. Defaults to all edges. */\n edges?: Edge[]\n /** Whether to apply insets as padding or margin. Defaults to 'padding'. */\n mode?: 'padding' | 'margin'\n /** Additional CSS classes */\n className?: string\n /** Additional inline styles */\n style?: React.CSSProperties\n /** Content to render inside the safe area */\n children?: React.ReactNode\n}\n\nconst ALL_EDGES: Edge[] = ['top', 'right', 'bottom', 'left']\n\nconst CSS_VAR_MAP: {[key in Edge]: string} = {\n top: 'var(--safe-area-inset-top, 0px)',\n right: 'var(--safe-area-inset-right, 0px)',\n bottom: 'var(--safe-area-inset-bottom, 0px)',\n left: 'var(--safe-area-inset-left, 0px)',\n}\n\nconst STYLE_KEY_MAP: {[key in 'padding' | 'margin']: {[edge in Edge]: string}} =\n {\n padding: {\n top: 'paddingTop',\n right: 'paddingRight',\n bottom: 'paddingBottom',\n left: 'paddingLeft',\n },\n margin: {\n top: 'marginTop',\n right: 'marginRight',\n bottom: 'marginBottom',\n left: 'marginLeft',\n },\n }\n\n/**\n * A container that applies safe area insets as padding or margin.\n *\n * Ensures content isn't hidden behind system UI (home indicator,\n * navigation bar, etc).\n *\n * Uses CSS custom properties (`--safe-area-inset-*`) injected by the\n * Shop app, which work reliably on both iOS and Android.\n *\n * Only applies inline padding/margin for edges where the safe area\n * inset is non-zero, so your Tailwind classes and styles are preserved\n * on edges that don't need safe area handling.\n *\n * @example\n * ```tsx\n * <SafeArea edges={['bottom']}>\n * <footer>Sticky footer content</footer>\n * </SafeArea>\n * ```\n *\n * @example\n * ```tsx\n * <SafeArea className=\"pt-12 px-4\">\n * <div>Custom top/side padding, safe area bottom</div>\n * </SafeArea>\n * ```\n */\nexport function SafeArea({\n edges = ALL_EDGES,\n mode = 'padding',\n className,\n style: styleProp,\n children,\n}: SafeAreaDocProps) {\n const style: React.CSSProperties = {...styleProp}\n const keys = STYLE_KEY_MAP[mode]\n const insets = useSafeArea()\n\n for (const edge of edges) {\n if (insets?.[edge] > 0) {\n ;(style as any)[keys[edge]] = CSS_VAR_MAP[edge]\n }\n }\n\n return (\n <div className={className} style={style}>\n {children}\n </div>\n )\n}\n"],"names":["ALL_EDGES","CSS_VAR_MAP","STYLE_KEY_MAP","SafeArea","edges","mode","className","styleProp","children","style","keys","insets","useSafeArea","edge","jsx"],"mappings":";;AAiBA,MAAMA,IAAoB,CAAC,OAAO,SAAS,UAAU,MAAM,GAErDC,IAAuC;AAAA,EAC3C,KAAK;AAAA,EACL,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,MAAM;AACR,GAEMC,IACJ;AAAA,EACE,SAAS;AAAA,IACP,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,EACR;AAAA,EACA,QAAQ;AAAA,IACN,KAAK;AAAA,IACL,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,MAAM;AAAA,EAAA;AAEV;AA6BK,SAASC,EAAS;AAAA,EACvB,OAAAC,IAAQJ;AAAA,EACR,MAAAK,IAAO;AAAA,EACP,WAAAC;AAAA,EACA,OAAOC;AAAA,EACP,UAAAC;AACF,GAAqB;AACb,QAAAC,IAA6B,EAAC,GAAGF,EAAS,GAC1CG,IAAOR,EAAcG,CAAI,GACzBM,IAASC,EAAY;AAE3B,aAAWC,KAAQT;AACb,IAAAO,IAASE,CAAI,IAAI,MACjBJ,EAAcC,EAAKG,CAAI,CAAC,IAAIZ,EAAYY,CAAI;AAIlD,SACG,gBAAAC,EAAA,OAAA,EAAI,WAAAR,GAAsB,OAAAG,GACxB,UAAAD,EACH,CAAA;AAEJ;"}
@@ -0,0 +1,8 @@
1
+ const t = { top: 0, right: 0, bottom: 0, left: 0 };
2
+ function e() {
3
+ return window.minisParams?.safeAreaInsets ?? t;
4
+ }
5
+ export {
6
+ e as useSafeArea
7
+ };
8
+ //# sourceMappingURL=useSafeArea.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useSafeArea.js","sources":["../../../src/hooks/util/useSafeArea.ts"],"sourcesContent":["import type {SafeAreaInsets} from '@shopify/shop-minis-platform'\n\nconst DEFAULT_INSETS: SafeAreaInsets = {top: 0, right: 0, bottom: 0, left: 0}\n\n/**\n * Returns the safe area insets for the current device.\n *\n * These values represent the areas of the screen that are obscured by\n * system UI (home indicator, navigation bar, etc). Use them to ensure\n * content isn't hidden behind system chrome.\n *\n * The values are also available as CSS custom properties:\n * `--safe-area-inset-top`, `--safe-area-inset-right`,\n * `--safe-area-inset-bottom`, `--safe-area-inset-left`\n *\n * @example\n * ```tsx\n * const {bottom} = useSafeArea()\n * return <div style={{paddingBottom: bottom}}>Content</div>\n * ```\n */\nexport function useSafeArea(): SafeAreaInsets {\n return window.minisParams?.safeAreaInsets ?? DEFAULT_INSETS\n}\n"],"names":["DEFAULT_INSETS","useSafeArea"],"mappings":"AAEA,MAAMA,IAAiC,EAAC,KAAK,GAAG,OAAO,GAAG,QAAQ,GAAG,MAAM,EAAC;AAmBrE,SAASC,IAA8B;AACrC,SAAA,OAAO,aAAa,kBAAkBD;AAC/C;"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { DATA_NAVIGATION_TYPE_ATTRIBUTE as o, NAVIGATION_TYPES as t } from "./types/index.js";
2
- import { MinisContainer as i } from "./components/MinisContainer.js";
2
+ import { MinisContainer as p } from "./components/MinisContainer.js";
3
3
  import { AddToCartButton as m } from "./components/commerce/add-to-cart.js";
4
4
  import { BuyNowButton as s } from "./components/commerce/buy-now.js";
5
5
  import { ProductCard as u, ProductCardBadge as f, ProductCardContainer as x, ProductCardFavoriteButton as c, ProductCardImage as d, ProductCardImageContainer as C, ProductCardInfo as g, ProductCardPrice as S, ProductCardReviewStars as A, ProductCardTitle as D } from "./components/commerce/product-card.js";
@@ -18,159 +18,161 @@ import { IconButton as X } from "./components/atoms/icon-button.js";
18
18
  import { Image as rr } from "./components/atoms/image.js";
19
19
  import { Touchable as or } from "./components/atoms/touchable.js";
20
20
  import { LongPressDetector as ar } from "./components/atoms/long-press-detector.js";
21
- import { AlertDialogAtom as pr } from "./components/atoms/alert-dialog.js";
21
+ import { AlertDialogAtom as ir } from "./components/atoms/alert-dialog.js";
22
22
  import { List as nr } from "./components/atoms/list.js";
23
23
  import { VideoPlayer as lr } from "./components/atoms/video-player.js";
24
24
  import { TextInput as fr } from "./components/atoms/text-input.js";
25
25
  import { ContentWrapper as cr } from "./components/atoms/content-wrapper.js";
26
26
  import { ProductVariantPrice as Cr } from "./components/atoms/product-variant-price.js";
27
- import { StaticArea as Sr } from "./components/atoms/static-area.js";
28
- import { Accordion as Dr, AccordionContent as Pr, AccordionItem as Tr, AccordionTrigger as hr } from "./components/ui/accordion.js";
29
- import { Alert as Rr, AlertDescription as vr, AlertTitle as wr } from "./components/ui/alert.js";
30
- import { AlertDialog as Er, AlertDialogAction as Mr, AlertDialogCancel as Fr, AlertDialogContent as Ur, AlertDialogDescription as br, AlertDialogFooter as Lr, AlertDialogHeader as kr, AlertDialogOverlay as Nr, AlertDialogPortal as Or, AlertDialogTitle as _r, AlertDialogTrigger as yr } from "./components/ui/alert-dialog.js";
31
- import { Avatar as Hr, AvatarFallback as Vr, AvatarImage as zr } from "./components/ui/avatar.js";
32
- import { Badge as Yr, badgeVariants as qr } from "./components/ui/badge.js";
33
- import { Card as Qr, CardAction as Zr, CardContent as jr, CardDescription as Jr, CardFooter as Xr, CardHeader as $r, CardTitle as re } from "./components/ui/card.js";
34
- import { Carousel as oe, CarouselContent as te, CarouselItem as ae, CarouselNext as ie, CarouselPrevious as pe } from "./components/ui/carousel.js";
35
- import { Checkbox as ne } from "./components/ui/checkbox.js";
36
- import { Dialog as le, DialogClose as ue, DialogContent as fe, DialogDescription as xe, DialogFooter as ce, DialogHeader as de, DialogOverlay as Ce, DialogPortal as ge, DialogTitle as Se, DialogTrigger as Ae } from "./components/ui/dialog.js";
37
- import { Drawer as Pe, DrawerClose as Te, DrawerContent as he, DrawerDescription as Ie, DrawerFooter as Re, DrawerHeader as ve, DrawerOverlay as we, DrawerPortal as Be, DrawerTitle as Ee, DrawerTrigger as Me } from "./components/ui/drawer.js";
38
- import { Input as Ue } from "./components/ui/input.js";
39
- import { Label as Le } from "./components/ui/label.js";
40
- import { Progress as Ne } from "./components/ui/progress.js";
41
- import { RadioGroup as _e, RadioGroupItem as ye } from "./components/ui/radio-group.js";
42
- import { ResizableHandle as He, ResizablePanel as Ve, ResizablePanelGroup as ze } from "./components/ui/resizable.js";
43
- import { ScrollArea as Ye, ScrollBar as qe } from "./components/ui/scroll-area.js";
44
- import { Select as Qe, SelectContent as Ze, SelectGroup as je, SelectItem as Je, SelectLabel as Xe, SelectScrollDownButton as $e, SelectScrollUpButton as ro, SelectSeparator as eo, SelectTrigger as oo, SelectValue as to } from "./components/ui/select.js";
45
- import { Separator as io } from "./components/ui/separator.js";
46
- import { Sheet as mo, SheetClose as no, SheetContent as so, SheetDescription as lo, SheetFooter as uo, SheetHeader as fo, SheetTitle as xo, SheetTrigger as co } from "./components/ui/sheet.js";
47
- import { Toaster as go } from "./components/ui/sonner.js";
48
- import { toast as Ao } from "./shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js";
49
- import { Skeleton as Po } from "./components/ui/skeleton.js";
50
- import { useRecentProducts as ho } from "./hooks/user/useRecentProducts.js";
51
- import { useRecentShops as Ro } from "./hooks/user/useRecentShops.js";
52
- import { useSavedProducts as wo } from "./hooks/user/useSavedProducts.js";
53
- import { useSavedProductsActions as Eo } from "./hooks/user/useSavedProductsActions.js";
54
- import { useFollowedShops as Fo } from "./hooks/user/useFollowedShops.js";
55
- import { useFollowedShopsActions as bo } from "./hooks/user/useFollowedShopsActions.js";
56
- import { useCurrentUser as ko } from "./hooks/user/useCurrentUser.js";
57
- import { useOrders as Oo } from "./hooks/user/useOrders.js";
58
- import { useBuyerAttributes as yo } from "./hooks/user/useBuyerAttributes.js";
59
- import { useGenerateUserToken as Ho } from "./hooks/user/useGenerateUserToken.js";
60
- import { useProductListActions as zo } from "./hooks/product/useProductListActions.js";
61
- import { useProductLists as Yo } from "./hooks/product/useProductLists.js";
62
- import { useProductList as Ko } from "./hooks/product/useProductList.js";
63
- import { useProduct as Zo } from "./hooks/product/useProduct.js";
64
- import { useProducts as Jo } from "./hooks/product/useProducts.js";
65
- import { useProductVariants as $o } from "./hooks/product/useProductVariants.js";
66
- import { useProductMedia as et } from "./hooks/product/useProductMedia.js";
67
- import { useProductReviews as tt } from "./hooks/product/useProductReviews.js";
68
- import { useProductSearch as it } from "./hooks/product/useProductSearch.js";
69
- import { useRecommendedProducts as mt } from "./hooks/product/useRecommendedProducts.js";
70
- import { usePopularProducts as st } from "./hooks/product/usePopularProducts.js";
71
- import { useARPreview as ut } from "./hooks/product/useARPreview.js";
72
- import { useAsyncStorage as xt } from "./hooks/storage/useAsyncStorage.js";
73
- import { useSecureStorage as dt } from "./hooks/storage/useSecureStorage.js";
74
- import { useImageUpload as gt } from "./hooks/storage/useImageUpload.js";
75
- import { useShopNavigation as At } from "./hooks/navigation/useShopNavigation.js";
76
- import { useCloseMini as Pt } from "./hooks/navigation/useCloseMini.js";
77
- import { useDeeplink as ht } from "./hooks/navigation/useDeeplink.js";
78
- import { useNavigateWithTransition as Rt } from "./hooks/navigation/useNavigateWithTransition.js";
79
- import { useShop as wt } from "./hooks/shop/useShop.js";
80
- import { useRecommendedShops as Et } from "./hooks/shop/useRecommendedShops.js";
81
- import { useCreateImageContent as Ft } from "./hooks/content/useCreateImageContent.js";
82
- import { useErrorToast as bt } from "./hooks/util/useErrorToast.js";
83
- import { useErrorScreen as kt } from "./hooks/util/useErrorScreen.js";
84
- import { useShare as Ot } from "./hooks/util/useShare.js";
85
- import { useImagePicker as yt } from "./hooks/util/useImagePicker.js";
86
- import { useKeyboardAvoidingView as Ht } from "./hooks/util/useKeyboardAvoidingView.js";
87
- import { useRequestPermissions as zt } from "./hooks/util/useRequestPermissions.js";
88
- import { useOnMiniFocus as Yt } from "./hooks/events/useOnMiniFocus.js";
89
- import { useOnMiniBlur as Kt } from "./hooks/events/useOnMiniBlur.js";
90
- import { useOnMiniClose as Zt } from "./hooks/events/useOnMiniClose.js";
91
- import { useOnAppStateChange as Jt } from "./hooks/events/useOnAppStateChange.js";
92
- import { useOnNavigateBack as $t } from "./hooks/events/useOnNavigateBack.js";
93
- import { buildDeeplinkUrl as ea } from "./utils/buildDeeplinkUrl.js";
94
- import { MiniEntityNotFoundError as ta, MiniError as aa, MiniNetworkError as ia, formatError as pa } from "./utils/errors.js";
95
- import { extractBrandTheme as na, formatReviewCount as sa, getFeaturedImages as la, normalizeRating as ua } from "./utils/merchant-card.js";
96
- import { parseUrl as xa } from "./utils/parseUrl.js";
97
- import { dataURLToBlob as da, fileToDataUri as Ca, getResizedImageUrl as ga, getThumbhashBlobURL as Sa, getThumbhashDataURL as Aa } from "./utils/image.js";
98
- import { formatMoney as Pa } from "./utils/formatMoney.js";
99
- import { UserState as ha, UserTokenGenerateUserErrorCode as Ia } from "./shop-minis-platform/src/types/user.js";
100
- import { ContentCreateUserErrorCode as va, MinisContentStatus as wa } from "./shop-minis-platform/src/types/content.js";
101
- import { Social as Ea } from "./shop-minis-platform/src/types/share.js";
102
- import { DATA_FETCHING_DEFAULT_FETCH_POLICY as Fa, DATA_FETCHING_DEFAULT_PAGE_SIZE as Ua } from "./shop-minis-platform/src/constants.js";
27
+ import { SafeArea as Sr } from "./components/atoms/safe-area.js";
28
+ import { StaticArea as Dr } from "./components/atoms/static-area.js";
29
+ import { Accordion as Tr, AccordionContent as hr, AccordionItem as Ir, AccordionTrigger as Rr } from "./components/ui/accordion.js";
30
+ import { Alert as wr, AlertDescription as Br, AlertTitle as Er } from "./components/ui/alert.js";
31
+ import { AlertDialog as Fr, AlertDialogAction as Ur, AlertDialogCancel as br, AlertDialogContent as Lr, AlertDialogDescription as kr, AlertDialogFooter as Nr, AlertDialogHeader as Or, AlertDialogOverlay as _r, AlertDialogPortal as yr, AlertDialogTitle as Gr, AlertDialogTrigger as Hr } from "./components/ui/alert-dialog.js";
32
+ import { Avatar as zr, AvatarFallback as Wr, AvatarImage as Yr } from "./components/ui/avatar.js";
33
+ import { Badge as Kr, badgeVariants as Qr } from "./components/ui/badge.js";
34
+ import { Card as jr, CardAction as Jr, CardContent as Xr, CardDescription as $r, CardFooter as re, CardHeader as ee, CardTitle as oe } from "./components/ui/card.js";
35
+ import { Carousel as ae, CarouselContent as pe, CarouselItem as ie, CarouselNext as me, CarouselPrevious as ne } from "./components/ui/carousel.js";
36
+ import { Checkbox as le } from "./components/ui/checkbox.js";
37
+ import { Dialog as fe, DialogClose as xe, DialogContent as ce, DialogDescription as de, DialogFooter as Ce, DialogHeader as ge, DialogOverlay as Se, DialogPortal as Ae, DialogTitle as De, DialogTrigger as Pe } from "./components/ui/dialog.js";
38
+ import { Drawer as he, DrawerClose as Ie, DrawerContent as Re, DrawerDescription as ve, DrawerFooter as we, DrawerHeader as Be, DrawerOverlay as Ee, DrawerPortal as Me, DrawerTitle as Fe, DrawerTrigger as Ue } from "./components/ui/drawer.js";
39
+ import { Input as Le } from "./components/ui/input.js";
40
+ import { Label as Ne } from "./components/ui/label.js";
41
+ import { Progress as _e } from "./components/ui/progress.js";
42
+ import { RadioGroup as Ge, RadioGroupItem as He } from "./components/ui/radio-group.js";
43
+ import { ResizableHandle as ze, ResizablePanel as We, ResizablePanelGroup as Ye } from "./components/ui/resizable.js";
44
+ import { ScrollArea as Ke, ScrollBar as Qe } from "./components/ui/scroll-area.js";
45
+ import { Select as je, SelectContent as Je, SelectGroup as Xe, SelectItem as $e, SelectLabel as ro, SelectScrollDownButton as eo, SelectScrollUpButton as oo, SelectSeparator as to, SelectTrigger as ao, SelectValue as po } from "./components/ui/select.js";
46
+ import { Separator as mo } from "./components/ui/separator.js";
47
+ import { Sheet as so, SheetClose as lo, SheetContent as uo, SheetDescription as fo, SheetFooter as xo, SheetHeader as co, SheetTitle as Co, SheetTrigger as go } from "./components/ui/sheet.js";
48
+ import { Toaster as Ao } from "./components/ui/sonner.js";
49
+ import { toast as Po } from "./shop-minis-react/node_modules/.pnpm/sonner@2.0.5_react-dom@19.1.0_react@19.1.0__react@19.1.0/node_modules/sonner/dist/index.js";
50
+ import { Skeleton as ho } from "./components/ui/skeleton.js";
51
+ import { useRecentProducts as Ro } from "./hooks/user/useRecentProducts.js";
52
+ import { useRecentShops as wo } from "./hooks/user/useRecentShops.js";
53
+ import { useSavedProducts as Eo } from "./hooks/user/useSavedProducts.js";
54
+ import { useSavedProductsActions as Fo } from "./hooks/user/useSavedProductsActions.js";
55
+ import { useFollowedShops as bo } from "./hooks/user/useFollowedShops.js";
56
+ import { useFollowedShopsActions as ko } from "./hooks/user/useFollowedShopsActions.js";
57
+ import { useCurrentUser as Oo } from "./hooks/user/useCurrentUser.js";
58
+ import { useOrders as yo } from "./hooks/user/useOrders.js";
59
+ import { useBuyerAttributes as Ho } from "./hooks/user/useBuyerAttributes.js";
60
+ import { useGenerateUserToken as zo } from "./hooks/user/useGenerateUserToken.js";
61
+ import { useProductListActions as Yo } from "./hooks/product/useProductListActions.js";
62
+ import { useProductLists as Ko } from "./hooks/product/useProductLists.js";
63
+ import { useProductList as Zo } from "./hooks/product/useProductList.js";
64
+ import { useProduct as Jo } from "./hooks/product/useProduct.js";
65
+ import { useProducts as $o } from "./hooks/product/useProducts.js";
66
+ import { useProductVariants as et } from "./hooks/product/useProductVariants.js";
67
+ import { useProductMedia as tt } from "./hooks/product/useProductMedia.js";
68
+ import { useProductReviews as pt } from "./hooks/product/useProductReviews.js";
69
+ import { useProductSearch as mt } from "./hooks/product/useProductSearch.js";
70
+ import { useRecommendedProducts as st } from "./hooks/product/useRecommendedProducts.js";
71
+ import { usePopularProducts as ut } from "./hooks/product/usePopularProducts.js";
72
+ import { useARPreview as xt } from "./hooks/product/useARPreview.js";
73
+ import { useAsyncStorage as dt } from "./hooks/storage/useAsyncStorage.js";
74
+ import { useSecureStorage as gt } from "./hooks/storage/useSecureStorage.js";
75
+ import { useImageUpload as At } from "./hooks/storage/useImageUpload.js";
76
+ import { useShopNavigation as Pt } from "./hooks/navigation/useShopNavigation.js";
77
+ import { useCloseMini as ht } from "./hooks/navigation/useCloseMini.js";
78
+ import { useDeeplink as Rt } from "./hooks/navigation/useDeeplink.js";
79
+ import { useNavigateWithTransition as wt } from "./hooks/navigation/useNavigateWithTransition.js";
80
+ import { useShop as Et } from "./hooks/shop/useShop.js";
81
+ import { useRecommendedShops as Ft } from "./hooks/shop/useRecommendedShops.js";
82
+ import { useCreateImageContent as bt } from "./hooks/content/useCreateImageContent.js";
83
+ import { useErrorToast as kt } from "./hooks/util/useErrorToast.js";
84
+ import { useErrorScreen as Ot } from "./hooks/util/useErrorScreen.js";
85
+ import { useSafeArea as yt } from "./hooks/util/useSafeArea.js";
86
+ import { useShare as Ht } from "./hooks/util/useShare.js";
87
+ import { useImagePicker as zt } from "./hooks/util/useImagePicker.js";
88
+ import { useKeyboardAvoidingView as Yt } from "./hooks/util/useKeyboardAvoidingView.js";
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";
103
105
  export {
104
- Dr as Accordion,
105
- Pr as AccordionContent,
106
- Tr as AccordionItem,
107
- hr as AccordionTrigger,
106
+ Tr as Accordion,
107
+ hr as AccordionContent,
108
+ Ir as AccordionItem,
109
+ Rr as AccordionTrigger,
108
110
  m as AddToCartButton,
109
- Rr as Alert,
110
- vr as AlertDescription,
111
- Er as AlertDialog,
112
- Mr as AlertDialogAction,
113
- pr as AlertDialogAtom,
114
- Fr as AlertDialogCancel,
115
- Ur as AlertDialogContent,
116
- br as AlertDialogDescription,
117
- Lr as AlertDialogFooter,
118
- kr as AlertDialogHeader,
119
- Nr as AlertDialogOverlay,
120
- Or as AlertDialogPortal,
121
- _r as AlertDialogTitle,
122
- yr as AlertDialogTrigger,
123
- wr as AlertTitle,
124
- Hr as Avatar,
125
- Vr as AvatarFallback,
126
- zr as AvatarImage,
127
- Yr as Badge,
111
+ wr as Alert,
112
+ Br as AlertDescription,
113
+ Fr as AlertDialog,
114
+ Ur as AlertDialogAction,
115
+ ir as AlertDialogAtom,
116
+ br as AlertDialogCancel,
117
+ Lr as AlertDialogContent,
118
+ kr as AlertDialogDescription,
119
+ Nr as AlertDialogFooter,
120
+ Or as AlertDialogHeader,
121
+ _r as AlertDialogOverlay,
122
+ yr as AlertDialogPortal,
123
+ Gr as AlertDialogTitle,
124
+ Hr as AlertDialogTrigger,
125
+ Er as AlertTitle,
126
+ zr as Avatar,
127
+ Wr as AvatarFallback,
128
+ Yr as AvatarImage,
129
+ Kr as Badge,
128
130
  j as Button,
129
131
  s as BuyNowButton,
130
- Qr as Card,
131
- Zr as CardAction,
132
- jr as CardContent,
133
- Jr as CardDescription,
134
- Xr as CardFooter,
135
- $r as CardHeader,
136
- re as CardTitle,
137
- oe as Carousel,
138
- te as CarouselContent,
139
- ae as CarouselItem,
140
- ie as CarouselNext,
141
- pe as CarouselPrevious,
142
- ne as Checkbox,
143
- va as ContentCreateUserErrorCode,
132
+ jr as Card,
133
+ Jr as CardAction,
134
+ Xr as CardContent,
135
+ $r as CardDescription,
136
+ re as CardFooter,
137
+ ee as CardHeader,
138
+ oe as CardTitle,
139
+ ae as Carousel,
140
+ pe as CarouselContent,
141
+ ie as CarouselItem,
142
+ me as CarouselNext,
143
+ ne as CarouselPrevious,
144
+ le as Checkbox,
145
+ Ma as ContentCreateUserErrorCode,
144
146
  cr as ContentWrapper,
145
- Fa as DATA_FETCHING_DEFAULT_FETCH_POLICY,
146
- Ua as DATA_FETCHING_DEFAULT_PAGE_SIZE,
147
+ ka as DATA_FETCHING_DEFAULT_FETCH_POLICY,
148
+ Na as DATA_FETCHING_DEFAULT_PAGE_SIZE,
147
149
  o as DATA_NAVIGATION_TYPE_ATTRIBUTE,
148
- le as Dialog,
149
- ue as DialogClose,
150
- fe as DialogContent,
151
- xe as DialogDescription,
152
- ce as DialogFooter,
153
- de as DialogHeader,
154
- Ce as DialogOverlay,
155
- ge as DialogPortal,
156
- Se as DialogTitle,
157
- Ae as DialogTrigger,
158
- Pe as Drawer,
159
- Te as DrawerClose,
160
- he as DrawerContent,
161
- Ie as DrawerDescription,
162
- Re as DrawerFooter,
163
- ve as DrawerHeader,
164
- we as DrawerOverlay,
165
- Be as DrawerPortal,
166
- Ee as DrawerTitle,
167
- Me as DrawerTrigger,
150
+ fe as Dialog,
151
+ xe as DialogClose,
152
+ ce as DialogContent,
153
+ de as DialogDescription,
154
+ Ce as DialogFooter,
155
+ ge as DialogHeader,
156
+ Se as DialogOverlay,
157
+ Ae as DialogPortal,
158
+ De as DialogTitle,
159
+ Pe as DialogTrigger,
160
+ he as Drawer,
161
+ Ie as DrawerClose,
162
+ Re as DrawerContent,
163
+ ve as DrawerDescription,
164
+ we as DrawerFooter,
165
+ Be as DrawerHeader,
166
+ Ee as DrawerOverlay,
167
+ Me as DrawerPortal,
168
+ Fe as DrawerTitle,
169
+ Ue as DrawerTrigger,
168
170
  V as FavoriteButton,
169
171
  X as IconButton,
170
172
  rr as Image,
171
173
  W as ImageContentWrapper,
172
- Ue as Input,
173
- Le as Label,
174
+ Le as Input,
175
+ Ne as Label,
174
176
  nr as List,
175
177
  ar as LongPressDetector,
176
178
  I as MerchantCard,
@@ -180,11 +182,11 @@ export {
180
182
  B as MerchantCardName,
181
183
  E as MerchantCardRating,
182
184
  b as MerchantCardSkeleton,
183
- ta as MiniEntityNotFoundError,
184
- aa as MiniError,
185
- ia as MiniNetworkError,
186
- i as MinisContainer,
187
- wa as MinisContentStatus,
185
+ ma as MiniEntityNotFoundError,
186
+ na as MiniError,
187
+ sa as MiniNetworkError,
188
+ p as MinisContainer,
189
+ Fa as MinisContentStatus,
188
190
  q as MinisRouter,
189
191
  t as NAVIGATION_TYPES,
190
192
  u as ProductCard,
@@ -200,105 +202,107 @@ export {
200
202
  D as ProductCardTitle,
201
203
  T as ProductLink,
202
204
  Cr as ProductVariantPrice,
203
- Ne as Progress,
205
+ _e as Progress,
204
206
  k as QuantitySelector,
205
- _e as RadioGroup,
206
- ye as RadioGroupItem,
207
- He as ResizableHandle,
208
- Ve as ResizablePanel,
209
- ze as ResizablePanelGroup,
210
- Ye as ScrollArea,
211
- qe as ScrollBar,
207
+ Ge as RadioGroup,
208
+ He as RadioGroupItem,
209
+ ze as ResizableHandle,
210
+ We as ResizablePanel,
211
+ Ye as ResizablePanelGroup,
212
+ Sr as SafeArea,
213
+ Ke as ScrollArea,
214
+ Qe as ScrollBar,
212
215
  O as Search,
213
216
  _ as SearchInput,
214
217
  y as SearchProvider,
215
218
  G as SearchResultsList,
216
- Qe as Select,
217
- Ze as SelectContent,
218
- je as SelectGroup,
219
- Je as SelectItem,
220
- Xe as SelectLabel,
221
- $e as SelectScrollDownButton,
222
- ro as SelectScrollUpButton,
223
- eo as SelectSeparator,
224
- oo as SelectTrigger,
225
- to as SelectValue,
226
- io as Separator,
227
- mo as Sheet,
228
- no as SheetClose,
229
- so as SheetContent,
230
- lo as SheetDescription,
231
- uo as SheetFooter,
232
- fo as SheetHeader,
233
- xo as SheetTitle,
234
- co as SheetTrigger,
235
- Po as Skeleton,
236
- Ea as Social,
237
- Sr as StaticArea,
219
+ je as Select,
220
+ Je as SelectContent,
221
+ Xe as SelectGroup,
222
+ $e as SelectItem,
223
+ ro as SelectLabel,
224
+ eo as SelectScrollDownButton,
225
+ oo as SelectScrollUpButton,
226
+ to as SelectSeparator,
227
+ ao as SelectTrigger,
228
+ po as SelectValue,
229
+ mo as Separator,
230
+ so as Sheet,
231
+ lo as SheetClose,
232
+ uo as SheetContent,
233
+ fo as SheetDescription,
234
+ xo as SheetFooter,
235
+ co as SheetHeader,
236
+ Co as SheetTitle,
237
+ go as SheetTrigger,
238
+ ho as Skeleton,
239
+ ba as Social,
240
+ Dr as StaticArea,
238
241
  fr as TextInput,
239
- go as Toaster,
242
+ Ao as Toaster,
240
243
  or as Touchable,
241
244
  Q as TransitionLink,
242
- ha as UserState,
243
- Ia as UserTokenGenerateUserErrorCode,
245
+ wa as UserState,
246
+ Ba as UserTokenGenerateUserErrorCode,
244
247
  lr as VideoPlayer,
245
- qr as badgeVariants,
246
- ea as buildDeeplinkUrl,
247
- da as dataURLToBlob,
248
- na as extractBrandTheme,
249
- Ca as fileToDataUri,
250
- pa as formatError,
251
- Pa as formatMoney,
252
- sa as formatReviewCount,
253
- la as getFeaturedImages,
254
- ga as getResizedImageUrl,
255
- Sa as getThumbhashBlobURL,
256
- Aa as getThumbhashDataURL,
257
- ua as normalizeRating,
258
- xa as parseUrl,
259
- Ao as toast,
260
- ut as useARPreview,
261
- xt as useAsyncStorage,
262
- yo as useBuyerAttributes,
263
- Pt as useCloseMini,
264
- Ft as useCreateImageContent,
265
- ko as useCurrentUser,
266
- ht as useDeeplink,
267
- kt as useErrorScreen,
268
- bt as useErrorToast,
269
- Fo as useFollowedShops,
270
- bo as useFollowedShopsActions,
271
- Ho as useGenerateUserToken,
272
- yt as useImagePicker,
273
- gt as useImageUpload,
274
- Ht as useKeyboardAvoidingView,
275
- Rt as useNavigateWithTransition,
276
- Jt as useOnAppStateChange,
277
- Kt as useOnMiniBlur,
278
- Zt as useOnMiniClose,
279
- Yt as useOnMiniFocus,
280
- $t as useOnNavigateBack,
281
- Oo as useOrders,
282
- st as usePopularProducts,
283
- Zo as useProduct,
284
- Ko as useProductList,
285
- zo as useProductListActions,
286
- Yo as useProductLists,
287
- et as useProductMedia,
288
- tt as useProductReviews,
289
- it as useProductSearch,
290
- $o as useProductVariants,
291
- Jo as useProducts,
292
- ho as useRecentProducts,
293
- Ro as useRecentShops,
294
- mt as useRecommendedProducts,
295
- Et as useRecommendedShops,
296
- zt as useRequestPermissions,
297
- wo as useSavedProducts,
298
- Eo as useSavedProductsActions,
299
- dt as useSecureStorage,
300
- Ot as useShare,
301
- wt as useShop,
302
- At as useShopNavigation
248
+ 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,
262
+ Po as toast,
263
+ xt as useARPreview,
264
+ dt as useAsyncStorage,
265
+ Ho as useBuyerAttributes,
266
+ ht as useCloseMini,
267
+ bt as useCreateImageContent,
268
+ Oo as useCurrentUser,
269
+ Rt as useDeeplink,
270
+ Ot as useErrorScreen,
271
+ kt as useErrorToast,
272
+ bo as useFollowedShops,
273
+ ko as useFollowedShopsActions,
274
+ zo as useGenerateUserToken,
275
+ zt as useImagePicker,
276
+ At as useImageUpload,
277
+ Yt as useKeyboardAvoidingView,
278
+ wt as useNavigateWithTransition,
279
+ ea as useOnAppStateChange,
280
+ Jt as useOnMiniBlur,
281
+ $t as useOnMiniClose,
282
+ Zt as useOnMiniFocus,
283
+ ta as useOnNavigateBack,
284
+ yo as useOrders,
285
+ ut as usePopularProducts,
286
+ Jo as useProduct,
287
+ Zo as useProductList,
288
+ Yo as useProductListActions,
289
+ Ko as useProductLists,
290
+ tt as useProductMedia,
291
+ pt as useProductReviews,
292
+ mt as useProductSearch,
293
+ et as useProductVariants,
294
+ $o as useProducts,
295
+ Ro as useRecentProducts,
296
+ wo as useRecentShops,
297
+ st as useRecommendedProducts,
298
+ Ft as useRecommendedShops,
299
+ Kt as useRequestPermissions,
300
+ yt as useSafeArea,
301
+ Eo as useSavedProducts,
302
+ Fo as useSavedProductsActions,
303
+ gt as useSecureStorage,
304
+ Ht as useShare,
305
+ Et as useShop,
306
+ Pt as useShopNavigation
303
307
  };
304
308
  //# sourceMappingURL=index.js.map
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,4 +1,4 @@
1
- import { __module as t } from "../../../../../../_virtual/index11.js";
1
+ import { __module as t } from "../../../../../../_virtual/index10.js";
2
2
  import { __require as z } from "../../../is-arrayish@0.3.2/node_modules/is-arrayish/index.js";
3
3
  var l;
4
4
  function v() {
@@ -1,4 +1,4 @@
1
- import { __module as r } from "../../../../../../../_virtual/index10.js";
1
+ import { __module as r } from "../../../../../../../_virtual/index11.js";
2
2
  import { __require as o } from "../cjs/use-sync-external-store-shim.production.js";
3
3
  import { __require as i } from "../cjs/use-sync-external-store-shim.development.js";
4
4
  var e;
@@ -122,10 +122,6 @@ module.exports = {
122
122
  messages: {
123
123
  missingScope:
124
124
  '{{source}} requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array.',
125
- missingScopeProductCard:
126
- 'Component "ProductCard" requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array or set favoriteButtonDisabled to true on all ProductCard instances.',
127
- missingScopeProductLink:
128
- 'Component "ProductLink" requires scope "{{scope}}" in src/manifest.json. Add "{{scope}}" to the "scopes" array or set hideFavoriteAction to true (or provide a customAction) on all ProductLink instances.',
129
125
  missingPermission:
130
126
  '{{reason}} requires permission "{{permission}}" in src/manifest.json. Add "{{permission}}" to the "permissions" array.',
131
127
  missingTrustedDomain:
@@ -149,9 +145,6 @@ module.exports = {
149
145
  const requiredPermissions = new Set()
150
146
  const requiredDomains = new Set()
151
147
  const fixedIssues = new Set()
152
- // Track how components are actually used (e.g., with specific props)
153
- const componentUsagePatterns = new Map()
154
-
155
148
  // Check module-level cache first to avoid repeated file I/O
156
149
  if (manifestPathCache && fs.existsSync(manifestPathCache)) {
157
150
  // Check if manifest.json has been modified (for IDE integration)
@@ -465,78 +458,6 @@ module.exports = {
465
458
  }
466
459
  },
467
460
 
468
- // Track ProductCard and ProductLink usage with disabled favorite functionality
469
- JSXElement(node) {
470
- const elementName = node.openingElement.name.name
471
-
472
- // Handle ProductCard with favoriteButtonDisabled prop
473
- if (elementName === 'ProductCard') {
474
- // Check if favoriteButtonDisabled prop is present and true
475
- const favoriteDisabledProp = node.openingElement.attributes.find(
476
- attr =>
477
- attr.type === 'JSXAttribute' &&
478
- attr.name?.name === 'favoriteButtonDisabled'
479
- )
480
-
481
- const isDisabled =
482
- favoriteDisabledProp &&
483
- // Shorthand syntax: <ProductCard favoriteButtonDisabled />
484
- (favoriteDisabledProp.value === null ||
485
- // Explicit true: <ProductCard favoriteButtonDisabled={true} />
486
- (favoriteDisabledProp.value?.type === 'JSXExpressionContainer' &&
487
- favoriteDisabledProp.value?.expression?.type === 'Literal' &&
488
- favoriteDisabledProp.value?.expression?.value === true))
489
-
490
- // Track usage pattern
491
- const componentPath = 'commerce/product-card'
492
- if (!componentUsagePatterns.has(componentPath)) {
493
- componentUsagePatterns.set(componentPath, {
494
- allDisabled: true,
495
- hasUsage: true,
496
- })
497
- }
498
- const pattern = componentUsagePatterns.get(componentPath)
499
- pattern.allDisabled = pattern.allDisabled && isDisabled
500
- }
501
-
502
- // Handle ProductLink with hideFavoriteAction or customAction props
503
- if (elementName === 'ProductLink') {
504
- // Check if hideFavoriteAction prop is present and true
505
- const hideFavoriteProp = node.openingElement.attributes.find(
506
- attr =>
507
- attr.type === 'JSXAttribute' &&
508
- attr.name?.name === 'hideFavoriteAction'
509
- )
510
-
511
- // Check if customAction prop is present (replaces favorite action)
512
- const customActionProp = node.openingElement.attributes.find(
513
- attr =>
514
- attr.type === 'JSXAttribute' && attr.name?.name === 'customAction'
515
- )
516
-
517
- const isFavoriteDisabled =
518
- // hideFavoriteAction={true} or shorthand
519
- (hideFavoriteProp &&
520
- (hideFavoriteProp.value === null || // shorthand
521
- (hideFavoriteProp.value?.type === 'JSXExpressionContainer' &&
522
- hideFavoriteProp.value?.expression?.type === 'Literal' &&
523
- hideFavoriteProp.value?.expression?.value === true))) ||
524
- // customAction is provided (any truthy value replaces favorites)
525
- customActionProp !== undefined
526
-
527
- // Track usage pattern
528
- const componentPath = 'commerce/product-link'
529
- if (!componentUsagePatterns.has(componentPath)) {
530
- componentUsagePatterns.set(componentPath, {
531
- allDisabled: true,
532
- hasUsage: true,
533
- })
534
- }
535
- const pattern = componentUsagePatterns.get(componentPath)
536
- pattern.allDisabled = pattern.allDisabled && isFavoriteDisabled
537
- }
538
- },
539
-
540
461
  // Check JSX attributes for external URLs
541
462
  JSXAttribute(node) {
542
463
  if (!node.value || node.value.type !== 'Literal') {
@@ -669,18 +590,6 @@ module.exports = {
669
590
  // Check scopes for components
670
591
  usedComponents.forEach(
671
592
  ({path: componentPath, name: componentName, node}) => {
672
- // Special handling for components with conditional favorite functionality
673
- if (
674
- componentPath === 'commerce/product-card' ||
675
- componentPath === 'commerce/product-link'
676
- ) {
677
- const usagePattern = componentUsagePatterns.get(componentPath)
678
- // Skip scope requirement if all usages have favorites disabled
679
- if (usagePattern?.hasUsage && usagePattern?.allDisabled) {
680
- return // No scope required when favorite functionality is disabled
681
- }
682
- }
683
-
684
593
  const componentData = componentScopesMap[componentPath]
685
594
  if (
686
595
  !componentData ||
@@ -789,13 +698,7 @@ module.exports = {
789
698
  const sourceName = issue.hookName || issue.componentName
790
699
  const sourceType = issue.hookName ? 'Hook' : 'Component'
791
700
 
792
- // Use custom message for ProductCard and ProductLink
793
- let messageId = 'missingScope'
794
- if (issue.componentName === 'ProductCard') {
795
- messageId = 'missingScopeProductCard'
796
- } else if (issue.componentName === 'ProductLink') {
797
- messageId = 'missingScopeProductLink'
798
- }
701
+ const messageId = 'missingScope'
799
702
 
800
703
  context.report({
801
704
  loc: {line: 1, column: 0},
@@ -1,18 +1 @@
1
- {
2
- "commerce/product-card": {
3
- "scopes": [
4
- "product_list:write"
5
- ],
6
- "hooks": [
7
- "useSavedProductsActions"
8
- ]
9
- },
10
- "commerce/product-link": {
11
- "scopes": [
12
- "product_list:write"
13
- ],
14
- "hooks": [
15
- "useSavedProductsActions"
16
- ]
17
- }
18
- }
1
+ {}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shopify/shop-minis-react",
3
3
  "license": "SEE LICENSE IN LICENSE.txt",
4
- "version": "0.9.0",
4
+ "version": "0.11.0",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -0,0 +1,92 @@
1
+ import {useSafeArea} from '../../hooks/util/useSafeArea'
2
+
3
+ type Edge = 'top' | 'right' | 'bottom' | 'left'
4
+
5
+ export interface SafeAreaDocProps {
6
+ /** Which edges to apply safe area insets to. Defaults to all edges. */
7
+ edges?: Edge[]
8
+ /** Whether to apply insets as padding or margin. Defaults to 'padding'. */
9
+ mode?: 'padding' | 'margin'
10
+ /** Additional CSS classes */
11
+ className?: string
12
+ /** Additional inline styles */
13
+ style?: React.CSSProperties
14
+ /** Content to render inside the safe area */
15
+ children?: React.ReactNode
16
+ }
17
+
18
+ const ALL_EDGES: Edge[] = ['top', 'right', 'bottom', 'left']
19
+
20
+ const CSS_VAR_MAP: {[key in Edge]: string} = {
21
+ top: 'var(--safe-area-inset-top, 0px)',
22
+ right: 'var(--safe-area-inset-right, 0px)',
23
+ bottom: 'var(--safe-area-inset-bottom, 0px)',
24
+ left: 'var(--safe-area-inset-left, 0px)',
25
+ }
26
+
27
+ const STYLE_KEY_MAP: {[key in 'padding' | 'margin']: {[edge in Edge]: string}} =
28
+ {
29
+ padding: {
30
+ top: 'paddingTop',
31
+ right: 'paddingRight',
32
+ bottom: 'paddingBottom',
33
+ left: 'paddingLeft',
34
+ },
35
+ margin: {
36
+ top: 'marginTop',
37
+ right: 'marginRight',
38
+ bottom: 'marginBottom',
39
+ left: 'marginLeft',
40
+ },
41
+ }
42
+
43
+ /**
44
+ * A container that applies safe area insets as padding or margin.
45
+ *
46
+ * Ensures content isn't hidden behind system UI (home indicator,
47
+ * navigation bar, etc).
48
+ *
49
+ * Uses CSS custom properties (`--safe-area-inset-*`) injected by the
50
+ * Shop app, which work reliably on both iOS and Android.
51
+ *
52
+ * Only applies inline padding/margin for edges where the safe area
53
+ * inset is non-zero, so your Tailwind classes and styles are preserved
54
+ * on edges that don't need safe area handling.
55
+ *
56
+ * @example
57
+ * ```tsx
58
+ * <SafeArea edges={['bottom']}>
59
+ * <footer>Sticky footer content</footer>
60
+ * </SafeArea>
61
+ * ```
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * <SafeArea className="pt-12 px-4">
66
+ * <div>Custom top/side padding, safe area bottom</div>
67
+ * </SafeArea>
68
+ * ```
69
+ */
70
+ export function SafeArea({
71
+ edges = ALL_EDGES,
72
+ mode = 'padding',
73
+ className,
74
+ style: styleProp,
75
+ children,
76
+ }: SafeAreaDocProps) {
77
+ const style: React.CSSProperties = {...styleProp}
78
+ const keys = STYLE_KEY_MAP[mode]
79
+ const insets = useSafeArea()
80
+
81
+ for (const edge of edges) {
82
+ if (insets?.[edge] > 0) {
83
+ ;(style as any)[keys[edge]] = CSS_VAR_MAP[edge]
84
+ }
85
+ }
86
+
87
+ return (
88
+ <div className={className} style={style}>
89
+ {children}
90
+ </div>
91
+ )
92
+ }
@@ -27,6 +27,7 @@ export * from './atoms/video-player'
27
27
  export * from './atoms/text-input'
28
28
  export * from './atoms/content-wrapper'
29
29
  export * from './atoms/product-variant-price'
30
+ export * from './atoms/safe-area'
30
31
  export * from './atoms/static-area'
31
32
 
32
33
  export * from './ui/accordion'
@@ -45,6 +45,7 @@ export * from './content/useCreateImageContent'
45
45
  // - Utility Hooks
46
46
  export * from './util/useErrorToast'
47
47
  export * from './util/useErrorScreen'
48
+ export * from './util/useSafeArea'
48
49
  export * from './util/useShare'
49
50
  export * from './util/useImagePicker'
50
51
  export * from './util/useKeyboardAvoidingView'
@@ -0,0 +1,24 @@
1
+ import type {SafeAreaInsets} from '@shopify/shop-minis-platform'
2
+
3
+ const DEFAULT_INSETS: SafeAreaInsets = {top: 0, right: 0, bottom: 0, left: 0}
4
+
5
+ /**
6
+ * Returns the safe area insets for the current device.
7
+ *
8
+ * These values represent the areas of the screen that are obscured by
9
+ * system UI (home indicator, navigation bar, etc). Use them to ensure
10
+ * content isn't hidden behind system chrome.
11
+ *
12
+ * The values are also available as CSS custom properties:
13
+ * `--safe-area-inset-top`, `--safe-area-inset-right`,
14
+ * `--safe-area-inset-bottom`, `--safe-area-inset-left`
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * const {bottom} = useSafeArea()
19
+ * return <div style={{paddingBottom: bottom}}>Content</div>
20
+ * ```
21
+ */
22
+ export function useSafeArea(): SafeAreaInsets {
23
+ return window.minisParams?.safeAreaInsets ?? DEFAULT_INSETS
24
+ }