@shopify/shop-minis-react 0.0.21 → 0.0.22

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 (210) hide show
  1. package/dist/_virtual/___vite-browser-external.js +7 -0
  2. package/dist/_virtual/___vite-browser-external.js.map +1 -0
  3. package/dist/_virtual/__vite-browser-external.js +5 -0
  4. package/dist/_virtual/__vite-browser-external.js.map +1 -0
  5. package/dist/_virtual/_commonjsHelpers.js +24 -4
  6. package/dist/_virtual/_commonjsHelpers.js.map +1 -1
  7. package/dist/_virtual/browser-index.js +8 -0
  8. package/dist/_virtual/browser-index.js.map +1 -0
  9. package/dist/_virtual/browser-index2.js +5 -0
  10. package/dist/_virtual/browser-index2.js.map +1 -0
  11. package/dist/_virtual/clock.js +6 -0
  12. package/dist/_virtual/clock.js.map +1 -0
  13. package/dist/_virtual/conventions.js +5 -0
  14. package/dist/_virtual/conventions.js.map +1 -0
  15. package/dist/_virtual/document.js +8 -0
  16. package/dist/_virtual/document.js.map +1 -0
  17. package/dist/_virtual/dom-parser.js +5 -0
  18. package/dist/_virtual/dom-parser.js.map +1 -0
  19. package/dist/_virtual/dom.js +5 -0
  20. package/dist/_virtual/dom.js.map +1 -0
  21. package/dist/_virtual/entities.js +5 -0
  22. package/dist/_virtual/entities.js.map +1 -0
  23. package/dist/_virtual/extends.js +5 -0
  24. package/dist/_virtual/extends.js.map +1 -0
  25. package/dist/_virtual/index10.js +5 -0
  26. package/dist/_virtual/index10.js.map +1 -0
  27. package/dist/_virtual/index11.js +5 -0
  28. package/dist/_virtual/index11.js.map +1 -0
  29. package/dist/_virtual/index2.js +5 -2
  30. package/dist/_virtual/index2.js.map +1 -1
  31. package/dist/_virtual/index3.js +5 -3
  32. package/dist/_virtual/index3.js.map +1 -1
  33. package/dist/_virtual/index5.js +6 -0
  34. package/dist/_virtual/index5.js.map +1 -0
  35. package/dist/_virtual/index6.js +6 -0
  36. package/dist/_virtual/index6.js.map +1 -0
  37. package/dist/_virtual/index7.js +5 -0
  38. package/dist/_virtual/index7.js.map +1 -0
  39. package/dist/_virtual/index8.js +5 -0
  40. package/dist/_virtual/index8.js.map +1 -0
  41. package/dist/_virtual/index9.js +5 -0
  42. package/dist/_virtual/index9.js.map +1 -0
  43. package/dist/_virtual/parse-sidx.js +8 -0
  44. package/dist/_virtual/parse-sidx.js.map +1 -0
  45. package/dist/_virtual/sax.js +5 -0
  46. package/dist/_virtual/sax.js.map +1 -0
  47. package/dist/_virtual/window.js +8 -0
  48. package/dist/_virtual/window.js.map +1 -0
  49. package/dist/components/atoms/video-player.js +121 -0
  50. package/dist/components/atoms/video-player.js.map +1 -0
  51. package/dist/components/commerce/merchant-card.js +236 -106
  52. package/dist/components/commerce/merchant-card.js.map +1 -1
  53. package/dist/index.js +199 -192
  54. package/dist/index.js.map +1 -1
  55. package/dist/mocks.js +114 -87
  56. package/dist/mocks.js.map +1 -1
  57. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/extends.js +20 -0
  58. package/dist/shop-minis-react/node_modules/.pnpm/@babel_runtime@7.27.6/node_modules/@babel/runtime/helpers/extends.js.map +1 -0
  59. 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
  60. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/byte-helpers.js +79 -0
  61. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/byte-helpers.js.map +1 -0
  62. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/codecs.js +102 -0
  63. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/codecs.js.map +1 -0
  64. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/containers.js +149 -0
  65. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/containers.js.map +1 -0
  66. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/decode-b64-to-uint8-array.js +13 -0
  67. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/decode-b64-to-uint8-array.js.map +1 -0
  68. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/ebml-helpers.js +77 -0
  69. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/ebml-helpers.js.map +1 -0
  70. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/id3-helpers.js +15 -0
  71. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/id3-helpers.js.map +1 -0
  72. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-groups.js +13 -0
  73. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-groups.js.map +1 -0
  74. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-types.js +7 -0
  75. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/media-types.js.map +1 -0
  76. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/mp4-helpers.js +31 -0
  77. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/mp4-helpers.js.map +1 -0
  78. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/nal-helpers.js +39 -0
  79. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/nal-helpers.js.map +1 -0
  80. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/resolve-url.js +14 -0
  81. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/resolve-url.js.map +1 -0
  82. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/stream.js +33 -0
  83. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_vhs-utils@4.1.1/node_modules/@videojs/vhs-utils/es/stream.js.map +1 -0
  84. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/http-handler.js +42 -0
  85. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/http-handler.js.map +1 -0
  86. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js +169 -0
  87. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/index.js.map +1 -0
  88. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/interceptors.js +75 -0
  89. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/interceptors.js.map +1 -0
  90. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/retry.js +70 -0
  91. package/dist/shop-minis-react/node_modules/.pnpm/@videojs_xhr@2.7.0/node_modules/@videojs/xhr/lib/retry.js.map +1 -0
  92. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/conventions.js +124 -0
  93. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/conventions.js.map +1 -0
  94. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/dom-parser.js +151 -0
  95. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/dom-parser.js.map +1 -0
  96. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/dom.js +993 -0
  97. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/dom.js.map +1 -0
  98. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/entities.js +2146 -0
  99. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/entities.js.map +1 -0
  100. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js +14 -0
  101. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/index.js.map +1 -0
  102. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/sax.js +346 -0
  103. package/dist/shop-minis-react/node_modules/.pnpm/@xmldom_xmldom@0.8.10/node_modules/@xmldom/xmldom/lib/sax.js.map +1 -0
  104. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/conversions.js +308 -0
  105. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/conversions.js.map +1 -0
  106. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js +41 -0
  107. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/index.js.map +1 -0
  108. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js +53 -0
  109. package/dist/shop-minis-react/node_modules/.pnpm/color-convert@2.0.1/node_modules/color-convert/route.js.map +1 -0
  110. package/dist/shop-minis-react/node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js +157 -0
  111. package/dist/shop-minis-react/node_modules/.pnpm/color-name@1.1.4/node_modules/color-name/index.js.map +1 -0
  112. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js +103 -0
  113. package/dist/shop-minis-react/node_modules/.pnpm/color-string@1.9.1/node_modules/color-string/index.js.map +1 -0
  114. package/dist/shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js +269 -0
  115. package/dist/shop-minis-react/node_modules/.pnpm/color@4.2.3/node_modules/color/index.js.map +1 -0
  116. package/dist/shop-minis-react/node_modules/.pnpm/global@4.4.0/node_modules/global/document.js +13 -0
  117. package/dist/shop-minis-react/node_modules/.pnpm/global@4.4.0/node_modules/global/document.js.map +1 -0
  118. package/dist/shop-minis-react/node_modules/.pnpm/global@4.4.0/node_modules/global/window.js +12 -0
  119. package/dist/shop-minis-react/node_modules/.pnpm/global@4.4.0/node_modules/global/window.js.map +1 -0
  120. package/dist/shop-minis-react/node_modules/.pnpm/is-arrayish@0.3.2/node_modules/is-arrayish/index.js +10 -0
  121. package/dist/shop-minis-react/node_modules/.pnpm/is-arrayish@0.3.2/node_modules/is-arrayish/index.js.map +1 -0
  122. package/dist/shop-minis-react/node_modules/.pnpm/is-function@1.0.2/node_modules/is-function/index.js +18 -0
  123. package/dist/shop-minis-react/node_modules/.pnpm/is-function@1.0.2/node_modules/is-function/index.js.map +1 -0
  124. package/dist/shop-minis-react/node_modules/.pnpm/m3u8-parser@7.2.0/node_modules/m3u8-parser/dist/m3u8-parser.es.js +855 -0
  125. package/dist/shop-minis-react/node_modules/.pnpm/m3u8-parser@7.2.0/node_modules/m3u8-parser/dist/m3u8-parser.es.js.map +1 -0
  126. package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js +1102 -0
  127. package/dist/shop-minis-react/node_modules/.pnpm/mpd-parser@1.3.1/node_modules/mpd-parser/dist/mpd-parser.es.js.map +1 -0
  128. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/tools/parse-sidx.js +32 -0
  129. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/tools/parse-sidx.js.map +1 -0
  130. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/utils/clock.js +34 -0
  131. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/utils/clock.js.map +1 -0
  132. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/utils/numbers.js +17 -0
  133. package/dist/shop-minis-react/node_modules/.pnpm/mux.js@7.1.0/node_modules/mux.js/lib/utils/numbers.js.map +1 -0
  134. package/dist/shop-minis-react/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.js +1 -1
  135. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js +23 -0
  136. package/dist/shop-minis-react/node_modules/.pnpm/simple-swizzle@0.2.2/node_modules/simple-swizzle/index.js.map +1 -0
  137. 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
  138. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js +27824 -0
  139. package/dist/shop-minis-react/node_modules/.pnpm/video.js@8.23.3/node_modules/video.js/dist/video.es.js.map +1 -0
  140. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/browser-index.js +26 -0
  141. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/browser-index.js.map +1 -0
  142. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vtt.js +802 -0
  143. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vtt.js.map +1 -0
  144. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vttcue.js +188 -0
  145. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vttcue.js.map +1 -0
  146. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vttregion.js +104 -0
  147. package/dist/shop-minis-react/node_modules/.pnpm/videojs-vtt.js@0.15.5/node_modules/videojs-vtt.js/lib/vttregion.js.map +1 -0
  148. package/dist/shop-minis-react.css +1 -1
  149. package/dist/utils/colors.js +6 -0
  150. package/dist/utils/colors.js.map +1 -0
  151. package/dist/utils/merchant-card.js +44 -0
  152. package/dist/utils/merchant-card.js.map +1 -0
  153. package/package.json +5 -1
  154. package/src/components/atoms/video-player.tsx +213 -0
  155. package/src/components/commerce/merchant-card.tsx +219 -49
  156. package/src/components/index.ts +1 -0
  157. package/src/mocks.ts +148 -16
  158. package/src/stories/MerchantCard.stories.tsx +63 -9
  159. package/src/utils/colors.ts +5 -0
  160. package/src/utils/index.ts +1 -0
  161. package/src/utils/merchant-card.ts +99 -0
  162. package/src/hooks/navigation/useCloseMini.doc.ts +0 -32
  163. package/src/hooks/navigation/useCloseMini.example.tsx +0 -7
  164. package/src/hooks/navigation/useDeeplink.doc.ts +0 -32
  165. package/src/hooks/navigation/useDeeplink.example.tsx +0 -7
  166. package/src/hooks/navigation/useShopNavigation.doc.ts +0 -32
  167. package/src/hooks/navigation/useShopNavigation.example.tsx +0 -32
  168. package/src/hooks/product/useCuratedProducts.doc.ts +0 -32
  169. package/src/hooks/product/useCuratedProducts.example.tsx +0 -10
  170. package/src/hooks/product/useProductList.doc.ts +0 -32
  171. package/src/hooks/product/useProductList.example.tsx +0 -9
  172. package/src/hooks/product/useProductListActions.doc.ts +0 -32
  173. package/src/hooks/product/useProductListActions.example.tsx +0 -60
  174. package/src/hooks/product/useProductLists.doc.ts +0 -32
  175. package/src/hooks/product/useProductLists.example.tsx +0 -9
  176. package/src/hooks/product/useProductSearch.doc.ts +0 -32
  177. package/src/hooks/product/useProductSearch.example.tsx +0 -13
  178. package/src/hooks/product/useRecommendedProducts.doc.ts +0 -32
  179. package/src/hooks/product/useRecommendedProducts.example.tsx +0 -7
  180. package/src/hooks/shop/useRecommendedShops.doc.ts +0 -32
  181. package/src/hooks/shop/useRecommendedShops.example.tsx +0 -7
  182. package/src/hooks/shop/useShop.doc.ts +0 -31
  183. package/src/hooks/shop/useShop.example.tsx +0 -7
  184. package/src/hooks/shop/useShopCartActions.doc.ts +0 -32
  185. package/src/hooks/shop/useShopCartActions.example.tsx +0 -28
  186. package/src/hooks/storage/useAsyncStorage.doc.ts +0 -32
  187. package/src/hooks/storage/useAsyncStorage.example.tsx +0 -30
  188. package/src/hooks/storage/useImageUpload.doc.ts +0 -32
  189. package/src/hooks/storage/useImageUpload.example.tsx +0 -20
  190. package/src/hooks/storage/useSecureStorage.doc.ts +0 -32
  191. package/src/hooks/storage/useSecureStorage.example.tsx +0 -23
  192. package/src/hooks/user/useBuyerAttributes.doc.ts +0 -32
  193. package/src/hooks/user/useBuyerAttributes.example.tsx +0 -14
  194. package/src/hooks/user/useCurrentUser.doc.ts +0 -31
  195. package/src/hooks/user/useCurrentUser.example.tsx +0 -7
  196. package/src/hooks/user/useFollowedShopsActions.doc.ts +0 -32
  197. package/src/hooks/user/useFollowedShopsActions.example.tsx +0 -16
  198. package/src/hooks/user/useOrders.doc.ts +0 -32
  199. package/src/hooks/user/useOrders.example.tsx +0 -7
  200. package/src/hooks/user/useRecentProducts.doc.ts +0 -32
  201. package/src/hooks/user/useRecentProducts.example.tsx +0 -13
  202. package/src/hooks/user/useSavedProducts.doc.ts +0 -32
  203. package/src/hooks/user/useSavedProducts.example.tsx +0 -13
  204. package/src/hooks/user/useSavedProductsActions.doc.ts +0 -32
  205. package/src/hooks/user/useSavedProductsActions.example.tsx +0 -32
  206. package/src/hooks/util/useErrorScreen.doc.ts +0 -32
  207. package/src/hooks/util/useErrorScreen.example.tsx +0 -15
  208. package/src/hooks/util/useErrorToast.doc.ts +0 -32
  209. package/src/hooks/util/useErrorToast.example.tsx +0 -15
  210. package/src/hooks/util/useImagePicker.example.tsx +0 -77
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colors.js","sources":["../../src/utils/colors.ts"],"sourcesContent":["import Color from 'color'\n\nexport const isDarkColor = (color: string): boolean => {\n return Color(color).darken(0.2).isDark()\n}\n"],"names":["isDarkColor","color","Color"],"mappings":";AAEa,MAAAA,IAAc,CAACC,MACnBC,EAAMD,CAAK,EAAE,OAAO,GAAG,EAAE,OAAO;"}
@@ -0,0 +1,44 @@
1
+ function u(e) {
2
+ return e >= 1e6 ? `${Math.floor(e / 1e5) / 10}M` : e >= 1e3 ? `${Math.floor(e / 1e3)}K` : e.toString();
3
+ }
4
+ function m(e) {
5
+ return Math.round(e * 10) / 10;
6
+ }
7
+ function l(e) {
8
+ if (!e)
9
+ return {
10
+ type: "default",
11
+ backgroundColor: "white"
12
+ };
13
+ if (e.headerTheme) {
14
+ const o = e.headerTheme.coverImage?.url ?? e.headerTheme.thumbnailImage?.url, r = e.headerTheme.coverImage?.thumbhash ?? e.headerTheme.thumbnailImage?.thumbhash ?? void 0, a = e.colors?.coverDominant;
15
+ if (o && a)
16
+ return {
17
+ type: "coverImage",
18
+ coverImageUrl: o,
19
+ coverImageThumbhash: r,
20
+ backgroundColor: a
21
+ };
22
+ }
23
+ return e.colors?.primary ? {
24
+ type: "brandColor",
25
+ backgroundColor: e.colors.primary
26
+ } : e.colors?.logoDominant ? {
27
+ type: "logoColor",
28
+ backgroundColor: e.colors.logoDominant
29
+ } : {
30
+ type: "default",
31
+ backgroundColor: "white"
32
+ };
33
+ }
34
+ function t(e, o = 4) {
35
+ const r = e?.featuredImages.slice(0, o);
36
+ return r ? r.length === 3 ? e?.featuredImages.slice(0, 2) : r : [];
37
+ }
38
+ export {
39
+ l as extractBrandTheme,
40
+ u as formatReviewCount,
41
+ t as getFeaturedImages,
42
+ m as normalizeRating
43
+ };
44
+ //# sourceMappingURL=merchant-card.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merchant-card.js","sources":["../../src/utils/merchant-card.ts"],"sourcesContent":["import {type Shop, type BrandSettings} from '@shopify/shop-minis-platform'\n\nexport type ExtractedBrandTheme =\n | {\n type: 'coverImage'\n coverImageUrl: string\n coverImageThumbhash?: string\n backgroundColor: string\n }\n | {\n type: 'brandColor'\n backgroundColor: string\n }\n | {\n type: 'logoColor'\n backgroundColor: string\n }\n | {\n type: 'default'\n backgroundColor: string\n }\n\nexport function formatReviewCount(count: number): string {\n if (count >= 1000000) {\n return `${Math.floor(count / 100000) / 10}M`\n }\n if (count >= 1000) {\n return `${Math.floor(count / 1000)}K`\n }\n return count.toString()\n}\n\nexport function normalizeRating(rating: number): number {\n return Math.round(rating * 10) / 10\n}\n\nexport function extractBrandTheme(\n brandSettings?: BrandSettings | null\n): ExtractedBrandTheme {\n if (!brandSettings) {\n return {\n type: 'default',\n backgroundColor: 'white',\n }\n }\n\n if (brandSettings.headerTheme) {\n const coverImageUrl =\n brandSettings.headerTheme.coverImage?.url ??\n brandSettings.headerTheme.thumbnailImage?.url\n const coverImageThumbhash =\n brandSettings.headerTheme.coverImage?.thumbhash ??\n brandSettings.headerTheme.thumbnailImage?.thumbhash ??\n undefined\n const coverImageColor = brandSettings.colors?.coverDominant\n\n if (coverImageUrl && coverImageColor) {\n return {\n type: 'coverImage',\n coverImageUrl,\n coverImageThumbhash,\n backgroundColor: coverImageColor,\n }\n }\n }\n\n if (brandSettings.colors?.primary) {\n return {\n type: 'brandColor',\n backgroundColor: brandSettings.colors.primary,\n }\n }\n\n if (brandSettings.colors?.logoDominant) {\n return {\n type: 'logoColor',\n backgroundColor: brandSettings.colors.logoDominant,\n }\n }\n\n return {\n type: 'default',\n backgroundColor: 'white',\n }\n}\n\nexport function getFeaturedImages(visualTheme: Shop['visualTheme'], limit = 4) {\n const images = visualTheme?.featuredImages.slice(0, limit)\n\n if (!images) {\n return []\n }\n\n if (images.length === 3) {\n return visualTheme?.featuredImages.slice(0, 2)\n }\n\n return images\n}\n"],"names":["formatReviewCount","count","normalizeRating","rating","extractBrandTheme","brandSettings","coverImageUrl","coverImageThumbhash","coverImageColor","getFeaturedImages","visualTheme","limit","images"],"mappings":"AAsBO,SAASA,EAAkBC,GAAuB;AACvD,SAAIA,KAAS,MACJ,GAAG,KAAK,MAAMA,IAAQ,GAAM,IAAI,EAAE,MAEvCA,KAAS,MACJ,GAAG,KAAK,MAAMA,IAAQ,GAAI,CAAC,MAE7BA,EAAM,SAAS;AACxB;AAEO,SAASC,EAAgBC,GAAwB;AACtD,SAAO,KAAK,MAAMA,IAAS,EAAE,IAAI;AACnC;AAEO,SAASC,EACdC,GACqB;AACrB,MAAI,CAACA;AACI,WAAA;AAAA,MACL,MAAM;AAAA,MACN,iBAAiB;AAAA,IACnB;AAGF,MAAIA,EAAc,aAAa;AAC7B,UAAMC,IACJD,EAAc,YAAY,YAAY,OACtCA,EAAc,YAAY,gBAAgB,KACtCE,IACJF,EAAc,YAAY,YAAY,aACtCA,EAAc,YAAY,gBAAgB,aAC1C,QACIG,IAAkBH,EAAc,QAAQ;AAE9C,QAAIC,KAAiBE;AACZ,aAAA;AAAA,QACL,MAAM;AAAA,QACN,eAAAF;AAAA,QACA,qBAAAC;AAAA,QACA,iBAAiBC;AAAA,MACnB;AAAA,EACF;AAGE,SAAAH,EAAc,QAAQ,UACjB;AAAA,IACL,MAAM;AAAA,IACN,iBAAiBA,EAAc,OAAO;AAAA,EACxC,IAGEA,EAAc,QAAQ,eACjB;AAAA,IACL,MAAM;AAAA,IACN,iBAAiBA,EAAc,OAAO;AAAA,EACxC,IAGK;AAAA,IACL,MAAM;AAAA,IACN,iBAAiB;AAAA,EACnB;AACF;AAEgB,SAAAI,EAAkBC,GAAkCC,IAAQ,GAAG;AAC7E,QAAMC,IAASF,GAAa,eAAe,MAAM,GAAGC,CAAK;AAEzD,SAAKC,IAIDA,EAAO,WAAW,IACbF,GAAa,eAAe,MAAM,GAAG,CAAC,IAGxCE,IAPE,CAAC;AAQZ;"}
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.0.21",
4
+ "version": "0.0.22",
5
5
  "sideEffects": false,
6
6
  "type": "module",
7
7
  "engines": {
@@ -44,6 +44,7 @@
44
44
  "@vitejs/plugin-react": "4.5.1",
45
45
  "class-variance-authority": "0.7.1",
46
46
  "clsx": "2.1.1",
47
+ "color": "^4.2.3",
47
48
  "embla-carousel-react": "8.6.0",
48
49
  "js-base64": "^3.7.7",
49
50
  "lodash": "4.17.21",
@@ -67,16 +68,19 @@
67
68
  "@shopify/generate-docs": "^0.16.6",
68
69
  "@storybook/addon-docs": "^9.0.16",
69
70
  "@storybook/react-vite": "^9.0.16",
71
+ "@types/color": "^3.0.6",
70
72
  "@types/node": "^20.12.1",
71
73
  "@types/react": "^19.1.6",
72
74
  "@types/react-dom": "^19.1.2",
73
75
  "@types/react-window": "^1.8.8",
76
+ "@types/video.js": "^7.3.58",
74
77
  "eslint": "^8.57.0",
75
78
  "eslint-plugin-storybook": "^9.0.16",
76
79
  "react": "^19.1.0",
77
80
  "react-dom": "^19.1.0",
78
81
  "storybook": "^9.0.16",
79
82
  "typescript": "^5.8.3",
83
+ "video.js": "^8.23.3",
80
84
  "vite": "^6.3.3",
81
85
  "vite-plugin-dts": "^4.3.0"
82
86
  },
@@ -0,0 +1,213 @@
1
+ import {
2
+ useRef,
3
+ useEffect,
4
+ useState,
5
+ useCallback,
6
+ forwardRef,
7
+ useImperativeHandle,
8
+ useMemo,
9
+ } from 'react'
10
+
11
+ import videojs from 'video.js'
12
+ import Player from 'video.js/dist/types/player'
13
+ import 'video.js/dist/video-js.css'
14
+
15
+ export interface VideoPlayerRef {
16
+ play: () => Promise<void>
17
+ pause: () => void
18
+ }
19
+
20
+ interface VideoPlayerProps {
21
+ src: string
22
+ /**
23
+ * The format/MIME type of the video.
24
+ * @default 'video/mp4'
25
+ */
26
+ format?: string
27
+ muted?: boolean
28
+ poster?: string
29
+ autoplay?: boolean
30
+ width?: number
31
+ height?: number
32
+ playButtonComponent?: React.ReactNode
33
+ onPlay?: () => void
34
+ onPause?: () => void
35
+ onEnded?: () => void
36
+ onReady?: () => void
37
+ }
38
+
39
+ export const VideoPlayer: React.ForwardRefExoticComponent<
40
+ VideoPlayerProps & React.RefAttributes<VideoPlayerRef>
41
+ > = forwardRef<VideoPlayerRef, VideoPlayerProps>(
42
+ (
43
+ {
44
+ src,
45
+ format = 'video/mp4',
46
+ poster,
47
+ muted,
48
+ autoplay,
49
+ width,
50
+ height,
51
+ playButtonComponent,
52
+ onPlay,
53
+ onPause,
54
+ onEnded,
55
+ onReady,
56
+ },
57
+ ref
58
+ ) => {
59
+ const videoContainerRef = useRef<HTMLDivElement>(null)
60
+ const playerRef = useRef<Player | null>(null)
61
+
62
+ const [isPlaying, setIsPlaying] = useState(false)
63
+
64
+ // Expose imperative methods to parent components
65
+ useImperativeHandle(
66
+ ref,
67
+ () => ({
68
+ play: async () => {
69
+ if (playerRef.current) {
70
+ try {
71
+ await playerRef.current.play()
72
+ setIsPlaying(true)
73
+ } catch (error) {
74
+ console.error('Error playing video:', error)
75
+ }
76
+ }
77
+ },
78
+ pause: () => {
79
+ if (playerRef.current) {
80
+ playerRef.current.pause()
81
+ setIsPlaying(false)
82
+ }
83
+ },
84
+ }),
85
+ []
86
+ )
87
+
88
+ const options = useMemo(
89
+ () => ({
90
+ controls: false,
91
+ controlBar: {
92
+ volumePanel: {
93
+ inline: false,
94
+ },
95
+ },
96
+ preload: 'auto',
97
+ muted,
98
+ // This makes sure that the video player does not take over the whole screen
99
+ preferFullWindow: false,
100
+ playsinline: true,
101
+ poster,
102
+ height,
103
+ width,
104
+ sources: [
105
+ {
106
+ src,
107
+ type: format,
108
+ },
109
+ ],
110
+ }),
111
+ [muted, poster, height, width, src, format]
112
+ )
113
+
114
+ useEffect(() => {
115
+ const player = playerRef.current
116
+
117
+ return () => {
118
+ if (player && !player.isDisposed()) {
119
+ player.dispose()
120
+ playerRef.current = null
121
+ }
122
+ }
123
+ }, [playerRef])
124
+
125
+ useEffect(() => {
126
+ if (!playerRef.current) return
127
+
128
+ const handlePlay = () => {
129
+ onPlay?.()
130
+ setIsPlaying(true)
131
+ }
132
+ const handlePause = () => {
133
+ onPause?.()
134
+ setIsPlaying(false)
135
+ }
136
+ const handleEnded = () => {
137
+ onEnded?.()
138
+ setIsPlaying(false)
139
+ }
140
+
141
+ playerRef.current.on('play', handlePlay)
142
+ playerRef.current.on('pause', handlePause)
143
+ playerRef.current.on('ended', handleEnded)
144
+
145
+ return () => {
146
+ if (playerRef.current) {
147
+ playerRef.current.off('play', handlePlay)
148
+ playerRef.current.off('pause', handlePause)
149
+ playerRef.current.off('ended', handleEnded)
150
+ }
151
+ }
152
+ }, [onEnded, onPause, onPlay])
153
+
154
+ const togglePlayPause = useCallback(() => {
155
+ if (isPlaying) {
156
+ playerRef.current?.pause()
157
+ setIsPlaying(false)
158
+ } else {
159
+ playerRef.current?.play()
160
+ setIsPlaying(true)
161
+ }
162
+ }, [isPlaying])
163
+
164
+ useEffect(() => {
165
+ if (!playerRef.current) {
166
+ const videoElement = document.createElement('video-js')
167
+
168
+ // The Video.js player needs to be _inside_ the component element for React 18 Strict Mode.
169
+ videoContainerRef.current?.appendChild(videoElement)
170
+
171
+ playerRef.current = videojs(videoElement, options, () => {
172
+ onReady && onReady()
173
+
174
+ if (autoplay) {
175
+ togglePlayPause()
176
+ }
177
+ })
178
+ }
179
+ }, [options, videoContainerRef, autoplay, onReady, togglePlayPause])
180
+
181
+ const containerClassName = [
182
+ width ? `w-${width}` : undefined,
183
+ 'relative',
184
+ ].join(' ')
185
+
186
+ return (
187
+ <div className="flex">
188
+ <div data-vjs-player className={containerClassName}>
189
+ <div ref={videoContainerRef} />
190
+ <div
191
+ className="absolute top-0 left-0 w-full h-full flex items-center justify-center cursor-pointer"
192
+ onClick={togglePlayPause}
193
+ aria-hidden
194
+ >
195
+ {isPlaying ? null : (playButtonComponent ?? <DefaultPlayIcon />)}
196
+ </div>
197
+ </div>
198
+ </div>
199
+ )
200
+ }
201
+ )
202
+
203
+ const DefaultPlayIcon = () => (
204
+ <div className="absolute inset-0 flex items-center justify-center bg-black/20 cursor-pointer animate-in fade-in duration-200">
205
+ <svg
206
+ viewBox="0 0 24 24"
207
+ className="w-10 h-10 text-white"
208
+ fill="currentColor"
209
+ >
210
+ <path d="M8 5v14l11-7z" />
211
+ </svg>
212
+ </div>
213
+ )
@@ -7,10 +7,19 @@ import {Slot as SlotPrimitive} from 'radix-ui'
7
7
 
8
8
  import {useShopNavigation} from '../../hooks/navigation/useShopNavigation'
9
9
  import {cn} from '../../lib/utils'
10
+ import {
11
+ type ExtractedBrandTheme,
12
+ extractBrandTheme,
13
+ formatReviewCount,
14
+ getFeaturedImages,
15
+ normalizeRating,
16
+ } from '../../utils'
17
+ import {isDarkColor} from '../../utils/colors'
18
+ import {ThumbhashImage} from '../atoms/thumbhash-image'
10
19
  import {Touchable} from '../atoms/touchable'
11
20
 
12
21
  const merchantCardVariants = cva(
13
- 'relative w-full aspect-square overflow-hidden rounded-xl border border-grayscale-l20 bg-grayscale-l0 flex flex-col',
22
+ 'relative w-full overflow-hidden rounded-xl bg-white flex flex-col border border-gray-200',
14
23
  {
15
24
  variants: {
16
25
  touchable: {
@@ -24,20 +33,6 @@ const merchantCardVariants = cva(
24
33
  }
25
34
  )
26
35
 
27
- function formatReviewCount(count: number): string {
28
- if (count >= 1000000) {
29
- return `${Math.floor(count / 100000) / 10}M`
30
- }
31
- if (count >= 1000) {
32
- return `${Math.floor(count / 1000)}K`
33
- }
34
- return count.toString()
35
- }
36
-
37
- function normalizeRating(rating: number): number {
38
- return Math.round(rating * 10) / 10
39
- }
40
-
41
36
  export interface MerchantCardRootProps
42
37
  extends React.ComponentProps<'div'>,
43
38
  VariantProps<typeof merchantCardVariants> {
@@ -96,12 +91,31 @@ function MerchantCardImage({
96
91
  className,
97
92
  src,
98
93
  alt,
94
+ thumbhash,
99
95
  ...props
100
96
  }: React.ComponentProps<'img'> & {
101
97
  src?: string
102
98
  alt?: string
99
+ thumbhash?: string
103
100
  }) {
104
- return src ? (
101
+ if (!src) {
102
+ return <div className="w-full h-full bg-gray-100" />
103
+ }
104
+
105
+ if (thumbhash) {
106
+ return (
107
+ <ThumbhashImage
108
+ data-slot="merchant-card-image"
109
+ src={src}
110
+ alt={alt}
111
+ thumbhash={thumbhash}
112
+ className={cn('w-full h-full object-cover', className)}
113
+ {...props}
114
+ />
115
+ )
116
+ }
117
+
118
+ return (
105
119
  <img
106
120
  data-slot="merchant-card-image"
107
121
  src={src}
@@ -109,8 +123,6 @@ function MerchantCardImage({
109
123
  className={cn('w-full h-full object-cover', className)}
110
124
  {...props}
111
125
  />
112
- ) : (
113
- <div className="w-full h-full bg-grayscale-l10" />
114
126
  )
115
127
  }
116
128
 
@@ -118,17 +130,19 @@ function MerchantCardLogo({
118
130
  className,
119
131
  src,
120
132
  alt,
133
+ shopName,
121
134
  ...props
122
135
  }: React.ComponentProps<'div'> & {
123
136
  src?: string
124
137
  alt?: string
138
+ shopName?: string
125
139
  }) {
126
140
  return (
127
141
  <div
128
142
  data-slot="merchant-card-logo"
129
143
  className={cn(
130
- 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2',
131
- 'w-14 h-14 rounded-xl',
144
+ 'absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10',
145
+ 'w-16 h-16 rounded-xl bg-white border-2 border-white shadow-sm',
132
146
  'flex items-center justify-center overflow-hidden',
133
147
  className
134
148
  )}
@@ -137,7 +151,11 @@ function MerchantCardLogo({
137
151
  {src ? (
138
152
  <img src={src} alt={alt} className="w-full h-full object-cover" />
139
153
  ) : (
140
- <div className="w-full h-full bg-grayscale-l20" />
154
+ <div className="w-full h-full bg-gray-200 flex items-center justify-center">
155
+ <span className="text-gray-600 font-semibold text-lg">
156
+ {shopName?.slice(0, 1)}
157
+ </span>
158
+ </div>
141
159
  )}
142
160
  </div>
143
161
  )
@@ -147,7 +165,10 @@ function MerchantCardInfo({className, ...props}: React.ComponentProps<'div'>) {
147
165
  return (
148
166
  <div
149
167
  data-slot="merchant-card-info"
150
- className={cn('p-3 space-y-1 flex-shrink-0', className)}
168
+ className={cn(
169
+ 'p-3 space-y-2 flex-shrink-0 flex items-center justify-between',
170
+ className
171
+ )}
151
172
  {...props}
152
173
  />
153
174
  )
@@ -187,28 +208,83 @@ function MerchantCardRating({
187
208
  return (
188
209
  <div
189
210
  data-slot="merchant-card-rating"
190
- className={cn(
191
- 'flex items-center gap-1 text-sm text-grayscale-d100',
192
- className
193
- )}
211
+ className={cn('flex items-center gap-1 text-xs', className)}
194
212
  {...props}
195
213
  >
196
- <Star className="h-3.5 w-3.5 fill-current" />
197
- <>
214
+ <Star className="h-3 w-3 fill-current" />
215
+ <span className="text-xs">
198
216
  {normalizeRating(rating)} ({formatReviewCount(reviewCount)})
199
- </>
217
+ </span>
200
218
  </div>
201
219
  )
202
220
  }
203
221
 
222
+ function MerchantCardBrandedHeader({
223
+ shop,
224
+ cardTheme,
225
+ logoBackgroundClassName,
226
+ }: {
227
+ shop: Shop
228
+ cardTheme: ExtractedBrandTheme
229
+ logoBackgroundClassName?: string
230
+ }) {
231
+ const wordmarkImage = shop.visualTheme?.brandSettings?.headerTheme?.wordmark
232
+
233
+ return (
234
+ <div className="size-full relative">
235
+ {cardTheme.type === 'coverImage' && (
236
+ <>
237
+ <MerchantCardImage
238
+ src={cardTheme.coverImageUrl}
239
+ alt={shop.name}
240
+ thumbhash={cardTheme.coverImageThumbhash ?? undefined}
241
+ className="size-full"
242
+ />
243
+
244
+ <div className="absolute inset-0 z-[1] bg-black/20" />
245
+
246
+ <div
247
+ className="absolute bottom-0 z-[1] size-full"
248
+ style={{
249
+ background: `linear-gradient(to top, ${cardTheme.backgroundColor} 0%, ${cardTheme.backgroundColor}00 40%)`,
250
+ }}
251
+ />
252
+ </>
253
+ )}
254
+
255
+ <div className="absolute inset-0 z-[1] flex items-center justify-center">
256
+ {wordmarkImage ? (
257
+ <img
258
+ src={wordmarkImage.url}
259
+ alt={wordmarkImage.altText || shop.name}
260
+ className="max-h-16 min-h-10 max-w-28 object-contain"
261
+ data-testid="store-data-wordmark"
262
+ />
263
+ ) : (
264
+ <MerchantCardLogo
265
+ src={shop.visualTheme?.logoImage?.url}
266
+ alt={`${shop.name} logo`}
267
+ shopName={shop.name}
268
+ className={logoBackgroundClassName}
269
+ />
270
+ )}
271
+ </div>
272
+ </div>
273
+ )
274
+ }
204
275
  export interface MerchantCardProps {
205
- variant?: 'default' | 'compact'
206
276
  shop: Shop
207
277
  touchable?: boolean
278
+ fixedHeight?: boolean
279
+ featuredImagesLimit?: number
208
280
  }
209
281
 
210
- // Composed MerchantCard component
211
- function MerchantCard({shop, touchable = true}: MerchantCardProps) {
282
+ function MerchantCard({
283
+ shop,
284
+ touchable = true,
285
+ fixedHeight = false,
286
+ featuredImagesLimit = 4,
287
+ }: MerchantCardProps) {
212
288
  const {navigateToShop} = useShopNavigation()
213
289
 
214
290
  const {
@@ -223,24 +299,117 @@ function MerchantCard({shop, touchable = true}: MerchantCardProps) {
223
299
  navigateToShop({shopId: id})
224
300
  }, [navigateToShop, id, touchable])
225
301
 
302
+ const featuredImages = React.useMemo(
303
+ () => getFeaturedImages(visualTheme, featuredImagesLimit),
304
+ [visualTheme, featuredImagesLimit]
305
+ )
306
+
307
+ const numberOfFeaturedImages = featuredImages?.length ?? 0
308
+
309
+ const logoAverageColor = visualTheme?.brandSettings?.colors?.logoAverage
310
+ const logoDominantColor = visualTheme?.brandSettings?.colors?.logoDominant
311
+ const logoColor = logoAverageColor || logoDominantColor
312
+
313
+ const logoBackgroundClassName = React.useMemo(
314
+ () => (logoColor && isDarkColor(logoColor) ? 'bg-white' : 'bg-gray-800'),
315
+ [logoColor]
316
+ )
317
+
318
+ const cardTheme = React.useMemo(
319
+ () => extractBrandTheme(visualTheme?.brandSettings),
320
+ [visualTheme?.brandSettings]
321
+ )
322
+
323
+ const isDarkTheme = React.useMemo(() => {
324
+ return (
325
+ cardTheme.backgroundColor !== 'white' &&
326
+ isDarkColor(cardTheme.backgroundColor)
327
+ )
328
+ }, [cardTheme.backgroundColor])
329
+
330
+ const textColor = isDarkTheme ? 'text-primary-foreground' : 'text-foreground'
331
+
332
+ const hasBrandedHeader =
333
+ cardTheme.type === 'coverImage' || cardTheme.type === 'brandColor'
334
+
226
335
  return (
227
- <MerchantCardRoot touchable={touchable} onPress={handlePress}>
228
- <MerchantCardImageContainer>
229
- <MerchantCardImage
230
- src={visualTheme?.featuredImages?.[0]?.url}
231
- alt={`${name} featured image`}
232
- />
233
- <MerchantCardLogo
234
- src={visualTheme?.logoImage?.url}
235
- alt={`${name} logo`}
236
- />
237
- </MerchantCardImageContainer>
238
-
239
- <MerchantCardInfo>
240
- <MerchantCardName>{name}</MerchantCardName>
241
- <MerchantCardRating rating={averageRating} reviewCount={reviewCount} />
242
- </MerchantCardInfo>
243
- </MerchantCardRoot>
336
+ <div className={cn('flex', fixedHeight ? '' : 'aspect-square')}>
337
+ <MerchantCardRoot
338
+ touchable={touchable}
339
+ onPress={handlePress}
340
+ style={{
341
+ backgroundColor: cardTheme.backgroundColor,
342
+ }}
343
+ >
344
+ <MerchantCardImageContainer
345
+ className={cn(
346
+ 'relative overflow-hidden w-full',
347
+ fixedHeight ? 'h-[120px]' : 'flex-1'
348
+ )}
349
+ >
350
+ {hasBrandedHeader ? (
351
+ <MerchantCardBrandedHeader
352
+ shop={shop}
353
+ cardTheme={cardTheme}
354
+ logoBackgroundClassName={logoBackgroundClassName}
355
+ />
356
+ ) : (
357
+ <>
358
+ <div className="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-10">
359
+ <MerchantCardLogo
360
+ src={visualTheme?.logoImage?.url}
361
+ alt={`${name} logo`}
362
+ shopName={name}
363
+ className={logoBackgroundClassName}
364
+ data-testid="merchant-logo"
365
+ />
366
+ </div>
367
+
368
+ {numberOfFeaturedImages > 0 ? (
369
+ featuredImages?.map((image, index) => (
370
+ <div
371
+ className="z-0 flex h-full flex-1"
372
+ key={image.url || index}
373
+ >
374
+ <MerchantCardImage
375
+ src={image.url}
376
+ alt={image.altText || ''}
377
+ thumbhash={image.thumbhash ?? undefined}
378
+ />
379
+ </div>
380
+ ))
381
+ ) : (
382
+ <div
383
+ className="h-20 bg-gray-100"
384
+ data-testid="image-fallback"
385
+ />
386
+ )}
387
+ </>
388
+ )}
389
+ </MerchantCardImageContainer>
390
+
391
+ <MerchantCardInfo className="flex items-center justify-between p-3">
392
+ <div className="flex flex-col items-start gap-2">
393
+ <div className="flex min-w-0 flex-1 flex-col">
394
+ <MerchantCardName
395
+ className={cn(
396
+ 'line-clamp-1 overflow-hidden text-ellipsis',
397
+ textColor
398
+ )}
399
+ >
400
+ {name}
401
+ </MerchantCardName>
402
+
403
+ <MerchantCardRating
404
+ rating={averageRating}
405
+ reviewCount={reviewCount}
406
+ className={textColor}
407
+ />
408
+ </div>
409
+ </div>
410
+ </MerchantCardInfo>
411
+ </MerchantCardRoot>
412
+ </div>
244
413
  )
245
414
  }
246
415
 
@@ -251,6 +420,7 @@ export const MerchantCardPrimitive = Object.assign(MerchantCardRoot, {
251
420
  Info: MerchantCardInfo,
252
421
  Name: MerchantCardName,
253
422
  Rating: MerchantCardRating,
423
+ BrandedHeader: MerchantCardBrandedHeader,
254
424
  })
255
425
 
256
426
  export {MerchantCard}
@@ -16,6 +16,7 @@ export * from './atoms/thumbhash-image'
16
16
  export * from './atoms/touchable'
17
17
  export * from './atoms/alert-dialog'
18
18
  export * from './atoms/list'
19
+ export * from './atoms/video-player'
19
20
 
20
21
  export * from './ui/accordion'
21
22
  export * from './ui/alert'