@shopify/hydrogen-react 2025.1.2 → 2025.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/dist/browser-dev/AddToCartButton.mjs.map +1 -1
  2. package/dist/browser-dev/BaseButton.mjs.map +1 -1
  3. package/dist/browser-dev/BuyNowButton.mjs.map +1 -1
  4. package/dist/browser-dev/CartCheckoutButton.mjs.map +1 -1
  5. package/dist/browser-dev/CartCost.mjs.map +1 -1
  6. package/dist/browser-dev/CartLineProvider.mjs.map +1 -1
  7. package/dist/browser-dev/CartLineQuantity.mjs.map +1 -1
  8. package/dist/browser-dev/CartLineQuantityAdjustButton.mjs.map +1 -1
  9. package/dist/browser-dev/CartProvider.mjs +4 -7
  10. package/dist/browser-dev/CartProvider.mjs.map +1 -1
  11. package/dist/browser-dev/ExternalVideo.mjs.map +1 -1
  12. package/dist/browser-dev/Image.mjs +2 -4
  13. package/dist/browser-dev/Image.mjs.map +1 -1
  14. package/dist/browser-dev/MediaFile.mjs.map +1 -1
  15. package/dist/browser-dev/ModelViewer.mjs.map +1 -1
  16. package/dist/browser-dev/Money.mjs.map +1 -1
  17. package/dist/browser-dev/ProductPrice.mjs.map +1 -1
  18. package/dist/browser-dev/ProductProvider.mjs +15 -12
  19. package/dist/browser-dev/ProductProvider.mjs.map +1 -1
  20. package/dist/browser-dev/RichText.components.mjs +2 -4
  21. package/dist/browser-dev/RichText.components.mjs.map +1 -1
  22. package/dist/browser-dev/RichText.mjs.map +1 -1
  23. package/dist/browser-dev/ShopPayButton.mjs.map +1 -1
  24. package/dist/browser-dev/ShopifyProvider.mjs +19 -2
  25. package/dist/browser-dev/ShopifyProvider.mjs.map +1 -1
  26. package/dist/browser-dev/Video.mjs.map +1 -1
  27. package/dist/browser-dev/_virtual/with-selector.mjs +3 -2
  28. package/dist/browser-dev/_virtual/with-selector.mjs.map +1 -1
  29. package/dist/browser-dev/_virtual/with-selector2.mjs +5 -0
  30. package/dist/browser-dev/_virtual/with-selector2.mjs.map +1 -0
  31. package/dist/browser-dev/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  32. package/dist/browser-dev/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  33. package/dist/browser-dev/analytics-utils.mjs.map +1 -1
  34. package/dist/browser-dev/analytics.mjs +6 -9
  35. package/dist/browser-dev/analytics.mjs.map +1 -1
  36. package/dist/browser-dev/cart-hooks.mjs +25 -7
  37. package/dist/browser-dev/cart-hooks.mjs.map +1 -1
  38. package/dist/browser-dev/cookies-utils.mjs +5 -5
  39. package/dist/browser-dev/cookies-utils.mjs.map +1 -1
  40. package/dist/browser-dev/flatten-connection.mjs.map +1 -1
  41. package/dist/browser-dev/getProductOptions.mjs +2 -4
  42. package/dist/browser-dev/getProductOptions.mjs.map +1 -1
  43. package/dist/browser-dev/index.mjs +4 -0
  44. package/dist/browser-dev/index.mjs.map +1 -1
  45. package/dist/browser-dev/load-script.mjs.map +1 -1
  46. package/dist/browser-dev/node_modules/@xstate/fsm/es/index.mjs +10 -18
  47. package/dist/browser-dev/node_modules/@xstate/fsm/es/index.mjs.map +1 -1
  48. package/dist/browser-dev/node_modules/@xstate/react/es/fsm.mjs +5 -9
  49. package/dist/browser-dev/node_modules/@xstate/react/es/fsm.mjs.map +1 -1
  50. package/dist/browser-dev/node_modules/@xstate/react/es/useConstant.mjs.map +1 -1
  51. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs +1 -2
  52. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs.map +1 -1
  53. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs +5 -10
  54. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs.map +1 -1
  55. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs +1 -2
  56. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs.map +1 -1
  57. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs +1 -2
  58. package/dist/browser-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs.map +1 -1
  59. package/dist/browser-dev/node_modules/use-sync-external-store/shim/index.mjs +1 -2
  60. package/dist/browser-dev/node_modules/use-sync-external-store/shim/index.mjs.map +1 -1
  61. package/dist/browser-dev/node_modules/use-sync-external-store/shim/with-selector.mjs +12 -7
  62. package/dist/browser-dev/node_modules/use-sync-external-store/shim/with-selector.mjs.map +1 -1
  63. package/dist/browser-dev/optionValueDecoder.mjs +1 -2
  64. package/dist/browser-dev/optionValueDecoder.mjs.map +1 -1
  65. package/dist/browser-dev/packages/hydrogen-react/package.json.mjs +1 -1
  66. package/dist/browser-dev/parse-metafield.mjs +2 -2
  67. package/dist/browser-dev/parse-metafield.mjs.map +1 -1
  68. package/dist/browser-dev/storefront-client.mjs +1 -2
  69. package/dist/browser-dev/storefront-client.mjs.map +1 -1
  70. package/dist/browser-dev/tracking-utils.mjs +88 -0
  71. package/dist/browser-dev/tracking-utils.mjs.map +1 -0
  72. package/dist/browser-dev/useCartAPIStateMachine.mjs +6 -12
  73. package/dist/browser-dev/useCartAPIStateMachine.mjs.map +1 -1
  74. package/dist/browser-dev/useCartActions.mjs.map +1 -1
  75. package/dist/browser-dev/useMoney.mjs.map +1 -1
  76. package/dist/browser-dev/useSelectedOptionInUrlParam.mjs.map +1 -1
  77. package/dist/browser-dev/useShopifyCookies.mjs +97 -11
  78. package/dist/browser-dev/useShopifyCookies.mjs.map +1 -1
  79. package/dist/browser-prod/AddToCartButton.mjs.map +1 -1
  80. package/dist/browser-prod/BaseButton.mjs.map +1 -1
  81. package/dist/browser-prod/BuyNowButton.mjs.map +1 -1
  82. package/dist/browser-prod/CartCheckoutButton.mjs.map +1 -1
  83. package/dist/browser-prod/CartCost.mjs.map +1 -1
  84. package/dist/browser-prod/CartLineProvider.mjs.map +1 -1
  85. package/dist/browser-prod/CartLineQuantity.mjs.map +1 -1
  86. package/dist/browser-prod/CartLineQuantityAdjustButton.mjs.map +1 -1
  87. package/dist/browser-prod/CartProvider.mjs +4 -7
  88. package/dist/browser-prod/CartProvider.mjs.map +1 -1
  89. package/dist/browser-prod/ExternalVideo.mjs.map +1 -1
  90. package/dist/browser-prod/Image.mjs +2 -4
  91. package/dist/browser-prod/Image.mjs.map +1 -1
  92. package/dist/browser-prod/MediaFile.mjs.map +1 -1
  93. package/dist/browser-prod/ModelViewer.mjs.map +1 -1
  94. package/dist/browser-prod/Money.mjs.map +1 -1
  95. package/dist/browser-prod/ProductPrice.mjs.map +1 -1
  96. package/dist/browser-prod/ProductProvider.mjs +15 -12
  97. package/dist/browser-prod/ProductProvider.mjs.map +1 -1
  98. package/dist/browser-prod/RichText.components.mjs +2 -4
  99. package/dist/browser-prod/RichText.components.mjs.map +1 -1
  100. package/dist/browser-prod/RichText.mjs.map +1 -1
  101. package/dist/browser-prod/ShopPayButton.mjs.map +1 -1
  102. package/dist/browser-prod/ShopifyProvider.mjs +19 -2
  103. package/dist/browser-prod/ShopifyProvider.mjs.map +1 -1
  104. package/dist/browser-prod/Video.mjs.map +1 -1
  105. package/dist/browser-prod/_virtual/with-selector.mjs +3 -2
  106. package/dist/browser-prod/_virtual/with-selector.mjs.map +1 -1
  107. package/dist/browser-prod/_virtual/with-selector2.mjs +5 -0
  108. package/dist/browser-prod/_virtual/with-selector2.mjs.map +1 -0
  109. package/dist/browser-prod/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  110. package/dist/browser-prod/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  111. package/dist/browser-prod/analytics-utils.mjs.map +1 -1
  112. package/dist/browser-prod/analytics.mjs +7 -11
  113. package/dist/browser-prod/analytics.mjs.map +1 -1
  114. package/dist/browser-prod/cart-hooks.mjs +25 -7
  115. package/dist/browser-prod/cart-hooks.mjs.map +1 -1
  116. package/dist/browser-prod/cookies-utils.mjs +5 -5
  117. package/dist/browser-prod/cookies-utils.mjs.map +1 -1
  118. package/dist/browser-prod/flatten-connection.mjs.map +1 -1
  119. package/dist/browser-prod/getProductOptions.mjs +2 -4
  120. package/dist/browser-prod/getProductOptions.mjs.map +1 -1
  121. package/dist/browser-prod/index.mjs +4 -0
  122. package/dist/browser-prod/index.mjs.map +1 -1
  123. package/dist/browser-prod/load-script.mjs.map +1 -1
  124. package/dist/browser-prod/node_modules/@xstate/fsm/es/index.mjs +10 -18
  125. package/dist/browser-prod/node_modules/@xstate/fsm/es/index.mjs.map +1 -1
  126. package/dist/browser-prod/node_modules/@xstate/react/es/fsm.mjs +5 -9
  127. package/dist/browser-prod/node_modules/@xstate/react/es/fsm.mjs.map +1 -1
  128. package/dist/browser-prod/node_modules/@xstate/react/es/useConstant.mjs.map +1 -1
  129. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs +1 -2
  130. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs.map +1 -1
  131. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs +5 -10
  132. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs.map +1 -1
  133. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs +1 -2
  134. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs.map +1 -1
  135. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs +1 -2
  136. package/dist/browser-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs.map +1 -1
  137. package/dist/browser-prod/node_modules/use-sync-external-store/shim/index.mjs +1 -2
  138. package/dist/browser-prod/node_modules/use-sync-external-store/shim/index.mjs.map +1 -1
  139. package/dist/browser-prod/node_modules/use-sync-external-store/shim/with-selector.mjs +12 -7
  140. package/dist/browser-prod/node_modules/use-sync-external-store/shim/with-selector.mjs.map +1 -1
  141. package/dist/browser-prod/optionValueDecoder.mjs +1 -2
  142. package/dist/browser-prod/optionValueDecoder.mjs.map +1 -1
  143. package/dist/browser-prod/packages/hydrogen-react/package.json.mjs +1 -1
  144. package/dist/browser-prod/parse-metafield.mjs +2 -2
  145. package/dist/browser-prod/parse-metafield.mjs.map +1 -1
  146. package/dist/browser-prod/storefront-client.mjs +1 -2
  147. package/dist/browser-prod/storefront-client.mjs.map +1 -1
  148. package/dist/browser-prod/tracking-utils.mjs +88 -0
  149. package/dist/browser-prod/tracking-utils.mjs.map +1 -0
  150. package/dist/browser-prod/useCartAPIStateMachine.mjs +6 -12
  151. package/dist/browser-prod/useCartAPIStateMachine.mjs.map +1 -1
  152. package/dist/browser-prod/useCartActions.mjs.map +1 -1
  153. package/dist/browser-prod/useMoney.mjs.map +1 -1
  154. package/dist/browser-prod/useSelectedOptionInUrlParam.mjs.map +1 -1
  155. package/dist/browser-prod/useShopifyCookies.mjs +97 -11
  156. package/dist/browser-prod/useShopifyCookies.mjs.map +1 -1
  157. package/dist/node-dev/AddToCartButton.js.map +1 -1
  158. package/dist/node-dev/AddToCartButton.mjs.map +1 -1
  159. package/dist/node-dev/BaseButton.js.map +1 -1
  160. package/dist/node-dev/BaseButton.mjs.map +1 -1
  161. package/dist/node-dev/BuyNowButton.js.map +1 -1
  162. package/dist/node-dev/BuyNowButton.mjs.map +1 -1
  163. package/dist/node-dev/CartCheckoutButton.js.map +1 -1
  164. package/dist/node-dev/CartCheckoutButton.mjs.map +1 -1
  165. package/dist/node-dev/CartCost.js.map +1 -1
  166. package/dist/node-dev/CartCost.mjs.map +1 -1
  167. package/dist/node-dev/CartLineProvider.js.map +1 -1
  168. package/dist/node-dev/CartLineProvider.mjs.map +1 -1
  169. package/dist/node-dev/CartLineQuantity.js.map +1 -1
  170. package/dist/node-dev/CartLineQuantity.mjs.map +1 -1
  171. package/dist/node-dev/CartLineQuantityAdjustButton.js.map +1 -1
  172. package/dist/node-dev/CartLineQuantityAdjustButton.mjs.map +1 -1
  173. package/dist/node-dev/CartProvider.js +3 -6
  174. package/dist/node-dev/CartProvider.js.map +1 -1
  175. package/dist/node-dev/CartProvider.mjs +4 -7
  176. package/dist/node-dev/CartProvider.mjs.map +1 -1
  177. package/dist/node-dev/ExternalVideo.js.map +1 -1
  178. package/dist/node-dev/ExternalVideo.mjs.map +1 -1
  179. package/dist/node-dev/Image.js +2 -4
  180. package/dist/node-dev/Image.js.map +1 -1
  181. package/dist/node-dev/Image.mjs +2 -4
  182. package/dist/node-dev/Image.mjs.map +1 -1
  183. package/dist/node-dev/MediaFile.js.map +1 -1
  184. package/dist/node-dev/MediaFile.mjs.map +1 -1
  185. package/dist/node-dev/ModelViewer.js.map +1 -1
  186. package/dist/node-dev/ModelViewer.mjs.map +1 -1
  187. package/dist/node-dev/Money.js.map +1 -1
  188. package/dist/node-dev/Money.mjs.map +1 -1
  189. package/dist/node-dev/ProductPrice.js.map +1 -1
  190. package/dist/node-dev/ProductPrice.mjs.map +1 -1
  191. package/dist/node-dev/ProductProvider.js +14 -11
  192. package/dist/node-dev/ProductProvider.js.map +1 -1
  193. package/dist/node-dev/ProductProvider.mjs +15 -12
  194. package/dist/node-dev/ProductProvider.mjs.map +1 -1
  195. package/dist/node-dev/RichText.components.js +2 -4
  196. package/dist/node-dev/RichText.components.js.map +1 -1
  197. package/dist/node-dev/RichText.components.mjs +2 -4
  198. package/dist/node-dev/RichText.components.mjs.map +1 -1
  199. package/dist/node-dev/RichText.js.map +1 -1
  200. package/dist/node-dev/RichText.mjs.map +1 -1
  201. package/dist/node-dev/ShopPayButton.js.map +1 -1
  202. package/dist/node-dev/ShopPayButton.mjs.map +1 -1
  203. package/dist/node-dev/ShopifyProvider.js +18 -1
  204. package/dist/node-dev/ShopifyProvider.js.map +1 -1
  205. package/dist/node-dev/ShopifyProvider.mjs +19 -2
  206. package/dist/node-dev/ShopifyProvider.mjs.map +1 -1
  207. package/dist/node-dev/Video.js.map +1 -1
  208. package/dist/node-dev/Video.mjs.map +1 -1
  209. package/dist/node-dev/_virtual/with-selector.js +3 -2
  210. package/dist/node-dev/_virtual/with-selector.js.map +1 -1
  211. package/dist/node-dev/_virtual/with-selector.mjs +3 -2
  212. package/dist/node-dev/_virtual/with-selector.mjs.map +1 -1
  213. package/dist/node-dev/_virtual/with-selector2.js +5 -0
  214. package/dist/node-dev/_virtual/with-selector2.js.map +1 -0
  215. package/dist/node-dev/_virtual/with-selector2.mjs +5 -0
  216. package/dist/node-dev/_virtual/with-selector2.mjs.map +1 -0
  217. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.js.map +1 -1
  218. package/dist/node-dev/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  219. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.js.map +1 -1
  220. package/dist/node-dev/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  221. package/dist/node-dev/analytics-utils.js.map +1 -1
  222. package/dist/node-dev/analytics-utils.mjs.map +1 -1
  223. package/dist/node-dev/analytics.js +6 -9
  224. package/dist/node-dev/analytics.js.map +1 -1
  225. package/dist/node-dev/analytics.mjs +6 -9
  226. package/dist/node-dev/analytics.mjs.map +1 -1
  227. package/dist/node-dev/cart-hooks.js +24 -6
  228. package/dist/node-dev/cart-hooks.js.map +1 -1
  229. package/dist/node-dev/cart-hooks.mjs +25 -7
  230. package/dist/node-dev/cart-hooks.mjs.map +1 -1
  231. package/dist/node-dev/cookies-utils.js +4 -4
  232. package/dist/node-dev/cookies-utils.js.map +1 -1
  233. package/dist/node-dev/cookies-utils.mjs +5 -5
  234. package/dist/node-dev/cookies-utils.mjs.map +1 -1
  235. package/dist/node-dev/flatten-connection.js.map +1 -1
  236. package/dist/node-dev/flatten-connection.mjs.map +1 -1
  237. package/dist/node-dev/getProductOptions.js +2 -4
  238. package/dist/node-dev/getProductOptions.js.map +1 -1
  239. package/dist/node-dev/getProductOptions.mjs +2 -4
  240. package/dist/node-dev/getProductOptions.mjs.map +1 -1
  241. package/dist/node-dev/index.js +4 -0
  242. package/dist/node-dev/index.js.map +1 -1
  243. package/dist/node-dev/index.mjs +4 -0
  244. package/dist/node-dev/index.mjs.map +1 -1
  245. package/dist/node-dev/load-script.js.map +1 -1
  246. package/dist/node-dev/load-script.mjs.map +1 -1
  247. package/dist/node-dev/node_modules/@xstate/fsm/es/index.js +10 -18
  248. package/dist/node-dev/node_modules/@xstate/fsm/es/index.js.map +1 -1
  249. package/dist/node-dev/node_modules/@xstate/fsm/es/index.mjs +10 -18
  250. package/dist/node-dev/node_modules/@xstate/fsm/es/index.mjs.map +1 -1
  251. package/dist/node-dev/node_modules/@xstate/react/es/fsm.js +5 -9
  252. package/dist/node-dev/node_modules/@xstate/react/es/fsm.js.map +1 -1
  253. package/dist/node-dev/node_modules/@xstate/react/es/fsm.mjs +5 -9
  254. package/dist/node-dev/node_modules/@xstate/react/es/fsm.mjs.map +1 -1
  255. package/dist/node-dev/node_modules/@xstate/react/es/useConstant.js.map +1 -1
  256. package/dist/node-dev/node_modules/@xstate/react/es/useConstant.mjs.map +1 -1
  257. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js +1 -2
  258. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +1 -1
  259. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs +1 -2
  260. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs.map +1 -1
  261. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js +5 -10
  262. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js.map +1 -1
  263. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs +5 -10
  264. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs.map +1 -1
  265. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js +1 -2
  266. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +1 -1
  267. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs +1 -2
  268. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs.map +1 -1
  269. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.js +1 -2
  270. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.js.map +1 -1
  271. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs +1 -2
  272. package/dist/node-dev/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs.map +1 -1
  273. package/dist/node-dev/node_modules/use-sync-external-store/shim/index.js +1 -2
  274. package/dist/node-dev/node_modules/use-sync-external-store/shim/index.js.map +1 -1
  275. package/dist/node-dev/node_modules/use-sync-external-store/shim/index.mjs +1 -2
  276. package/dist/node-dev/node_modules/use-sync-external-store/shim/index.mjs.map +1 -1
  277. package/dist/node-dev/node_modules/use-sync-external-store/shim/with-selector.js +12 -7
  278. package/dist/node-dev/node_modules/use-sync-external-store/shim/with-selector.js.map +1 -1
  279. package/dist/node-dev/node_modules/use-sync-external-store/shim/with-selector.mjs +12 -7
  280. package/dist/node-dev/node_modules/use-sync-external-store/shim/with-selector.mjs.map +1 -1
  281. package/dist/node-dev/optionValueDecoder.js +1 -2
  282. package/dist/node-dev/optionValueDecoder.js.map +1 -1
  283. package/dist/node-dev/optionValueDecoder.mjs +1 -2
  284. package/dist/node-dev/optionValueDecoder.mjs.map +1 -1
  285. package/dist/node-dev/packages/hydrogen-react/package.json.js +1 -1
  286. package/dist/node-dev/packages/hydrogen-react/package.json.mjs +1 -1
  287. package/dist/node-dev/parse-metafield.js +2 -2
  288. package/dist/node-dev/parse-metafield.js.map +1 -1
  289. package/dist/node-dev/parse-metafield.mjs +2 -2
  290. package/dist/node-dev/parse-metafield.mjs.map +1 -1
  291. package/dist/node-dev/storefront-client.js +1 -2
  292. package/dist/node-dev/storefront-client.js.map +1 -1
  293. package/dist/node-dev/storefront-client.mjs +1 -2
  294. package/dist/node-dev/storefront-client.mjs.map +1 -1
  295. package/dist/node-dev/tracking-utils.js +88 -0
  296. package/dist/node-dev/tracking-utils.js.map +1 -0
  297. package/dist/node-dev/tracking-utils.mjs +88 -0
  298. package/dist/node-dev/tracking-utils.mjs.map +1 -0
  299. package/dist/node-dev/useCartAPIStateMachine.js +6 -12
  300. package/dist/node-dev/useCartAPIStateMachine.js.map +1 -1
  301. package/dist/node-dev/useCartAPIStateMachine.mjs +6 -12
  302. package/dist/node-dev/useCartAPIStateMachine.mjs.map +1 -1
  303. package/dist/node-dev/useCartActions.js.map +1 -1
  304. package/dist/node-dev/useCartActions.mjs.map +1 -1
  305. package/dist/node-dev/useMoney.js.map +1 -1
  306. package/dist/node-dev/useMoney.mjs.map +1 -1
  307. package/dist/node-dev/useSelectedOptionInUrlParam.js.map +1 -1
  308. package/dist/node-dev/useSelectedOptionInUrlParam.mjs.map +1 -1
  309. package/dist/node-dev/useShopifyCookies.js +95 -9
  310. package/dist/node-dev/useShopifyCookies.js.map +1 -1
  311. package/dist/node-dev/useShopifyCookies.mjs +97 -11
  312. package/dist/node-dev/useShopifyCookies.mjs.map +1 -1
  313. package/dist/node-prod/AddToCartButton.js.map +1 -1
  314. package/dist/node-prod/AddToCartButton.mjs.map +1 -1
  315. package/dist/node-prod/BaseButton.js.map +1 -1
  316. package/dist/node-prod/BaseButton.mjs.map +1 -1
  317. package/dist/node-prod/BuyNowButton.js.map +1 -1
  318. package/dist/node-prod/BuyNowButton.mjs.map +1 -1
  319. package/dist/node-prod/CartCheckoutButton.js.map +1 -1
  320. package/dist/node-prod/CartCheckoutButton.mjs.map +1 -1
  321. package/dist/node-prod/CartCost.js.map +1 -1
  322. package/dist/node-prod/CartCost.mjs.map +1 -1
  323. package/dist/node-prod/CartLineProvider.js.map +1 -1
  324. package/dist/node-prod/CartLineProvider.mjs.map +1 -1
  325. package/dist/node-prod/CartLineQuantity.js.map +1 -1
  326. package/dist/node-prod/CartLineQuantity.mjs.map +1 -1
  327. package/dist/node-prod/CartLineQuantityAdjustButton.js.map +1 -1
  328. package/dist/node-prod/CartLineQuantityAdjustButton.mjs.map +1 -1
  329. package/dist/node-prod/CartProvider.js +3 -6
  330. package/dist/node-prod/CartProvider.js.map +1 -1
  331. package/dist/node-prod/CartProvider.mjs +4 -7
  332. package/dist/node-prod/CartProvider.mjs.map +1 -1
  333. package/dist/node-prod/ExternalVideo.js.map +1 -1
  334. package/dist/node-prod/ExternalVideo.mjs.map +1 -1
  335. package/dist/node-prod/Image.js +2 -4
  336. package/dist/node-prod/Image.js.map +1 -1
  337. package/dist/node-prod/Image.mjs +2 -4
  338. package/dist/node-prod/Image.mjs.map +1 -1
  339. package/dist/node-prod/MediaFile.js.map +1 -1
  340. package/dist/node-prod/MediaFile.mjs.map +1 -1
  341. package/dist/node-prod/ModelViewer.js.map +1 -1
  342. package/dist/node-prod/ModelViewer.mjs.map +1 -1
  343. package/dist/node-prod/Money.js.map +1 -1
  344. package/dist/node-prod/Money.mjs.map +1 -1
  345. package/dist/node-prod/ProductPrice.js.map +1 -1
  346. package/dist/node-prod/ProductPrice.mjs.map +1 -1
  347. package/dist/node-prod/ProductProvider.js +14 -11
  348. package/dist/node-prod/ProductProvider.js.map +1 -1
  349. package/dist/node-prod/ProductProvider.mjs +15 -12
  350. package/dist/node-prod/ProductProvider.mjs.map +1 -1
  351. package/dist/node-prod/RichText.components.js +2 -4
  352. package/dist/node-prod/RichText.components.js.map +1 -1
  353. package/dist/node-prod/RichText.components.mjs +2 -4
  354. package/dist/node-prod/RichText.components.mjs.map +1 -1
  355. package/dist/node-prod/RichText.js.map +1 -1
  356. package/dist/node-prod/RichText.mjs.map +1 -1
  357. package/dist/node-prod/ShopPayButton.js.map +1 -1
  358. package/dist/node-prod/ShopPayButton.mjs.map +1 -1
  359. package/dist/node-prod/ShopifyProvider.js +18 -1
  360. package/dist/node-prod/ShopifyProvider.js.map +1 -1
  361. package/dist/node-prod/ShopifyProvider.mjs +19 -2
  362. package/dist/node-prod/ShopifyProvider.mjs.map +1 -1
  363. package/dist/node-prod/Video.js.map +1 -1
  364. package/dist/node-prod/Video.mjs.map +1 -1
  365. package/dist/node-prod/_virtual/with-selector.js +3 -2
  366. package/dist/node-prod/_virtual/with-selector.js.map +1 -1
  367. package/dist/node-prod/_virtual/with-selector.mjs +3 -2
  368. package/dist/node-prod/_virtual/with-selector.mjs.map +1 -1
  369. package/dist/node-prod/_virtual/with-selector2.js +5 -0
  370. package/dist/node-prod/_virtual/with-selector2.js.map +1 -0
  371. package/dist/node-prod/_virtual/with-selector2.mjs +5 -0
  372. package/dist/node-prod/_virtual/with-selector2.mjs.map +1 -0
  373. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.js.map +1 -1
  374. package/dist/node-prod/analytics-schema-custom-storefront-customer-tracking.mjs.map +1 -1
  375. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.js.map +1 -1
  376. package/dist/node-prod/analytics-schema-trekkie-storefront-page-view.mjs.map +1 -1
  377. package/dist/node-prod/analytics-utils.js.map +1 -1
  378. package/dist/node-prod/analytics-utils.mjs.map +1 -1
  379. package/dist/node-prod/analytics.js +7 -11
  380. package/dist/node-prod/analytics.js.map +1 -1
  381. package/dist/node-prod/analytics.mjs +7 -11
  382. package/dist/node-prod/analytics.mjs.map +1 -1
  383. package/dist/node-prod/cart-hooks.js +24 -6
  384. package/dist/node-prod/cart-hooks.js.map +1 -1
  385. package/dist/node-prod/cart-hooks.mjs +25 -7
  386. package/dist/node-prod/cart-hooks.mjs.map +1 -1
  387. package/dist/node-prod/cookies-utils.js +4 -4
  388. package/dist/node-prod/cookies-utils.js.map +1 -1
  389. package/dist/node-prod/cookies-utils.mjs +5 -5
  390. package/dist/node-prod/cookies-utils.mjs.map +1 -1
  391. package/dist/node-prod/flatten-connection.js.map +1 -1
  392. package/dist/node-prod/flatten-connection.mjs.map +1 -1
  393. package/dist/node-prod/getProductOptions.js +2 -4
  394. package/dist/node-prod/getProductOptions.js.map +1 -1
  395. package/dist/node-prod/getProductOptions.mjs +2 -4
  396. package/dist/node-prod/getProductOptions.mjs.map +1 -1
  397. package/dist/node-prod/index.js +4 -0
  398. package/dist/node-prod/index.js.map +1 -1
  399. package/dist/node-prod/index.mjs +4 -0
  400. package/dist/node-prod/index.mjs.map +1 -1
  401. package/dist/node-prod/load-script.js.map +1 -1
  402. package/dist/node-prod/load-script.mjs.map +1 -1
  403. package/dist/node-prod/node_modules/@xstate/fsm/es/index.js +10 -18
  404. package/dist/node-prod/node_modules/@xstate/fsm/es/index.js.map +1 -1
  405. package/dist/node-prod/node_modules/@xstate/fsm/es/index.mjs +10 -18
  406. package/dist/node-prod/node_modules/@xstate/fsm/es/index.mjs.map +1 -1
  407. package/dist/node-prod/node_modules/@xstate/react/es/fsm.js +5 -9
  408. package/dist/node-prod/node_modules/@xstate/react/es/fsm.js.map +1 -1
  409. package/dist/node-prod/node_modules/@xstate/react/es/fsm.mjs +5 -9
  410. package/dist/node-prod/node_modules/@xstate/react/es/fsm.mjs.map +1 -1
  411. package/dist/node-prod/node_modules/@xstate/react/es/useConstant.js.map +1 -1
  412. package/dist/node-prod/node_modules/@xstate/react/es/useConstant.mjs.map +1 -1
  413. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js +1 -2
  414. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.js.map +1 -1
  415. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs +1 -2
  416. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.development.mjs.map +1 -1
  417. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js +5 -10
  418. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.js.map +1 -1
  419. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs +5 -10
  420. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim/with-selector.production.min.mjs.map +1 -1
  421. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js +1 -2
  422. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.js.map +1 -1
  423. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs +1 -2
  424. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.development.mjs.map +1 -1
  425. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.js +1 -2
  426. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.js.map +1 -1
  427. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs +1 -2
  428. package/dist/node-prod/node_modules/use-sync-external-store/cjs/use-sync-external-store-shim.production.min.mjs.map +1 -1
  429. package/dist/node-prod/node_modules/use-sync-external-store/shim/index.js +1 -2
  430. package/dist/node-prod/node_modules/use-sync-external-store/shim/index.js.map +1 -1
  431. package/dist/node-prod/node_modules/use-sync-external-store/shim/index.mjs +1 -2
  432. package/dist/node-prod/node_modules/use-sync-external-store/shim/index.mjs.map +1 -1
  433. package/dist/node-prod/node_modules/use-sync-external-store/shim/with-selector.js +12 -7
  434. package/dist/node-prod/node_modules/use-sync-external-store/shim/with-selector.js.map +1 -1
  435. package/dist/node-prod/node_modules/use-sync-external-store/shim/with-selector.mjs +12 -7
  436. package/dist/node-prod/node_modules/use-sync-external-store/shim/with-selector.mjs.map +1 -1
  437. package/dist/node-prod/optionValueDecoder.js +1 -2
  438. package/dist/node-prod/optionValueDecoder.js.map +1 -1
  439. package/dist/node-prod/optionValueDecoder.mjs +1 -2
  440. package/dist/node-prod/optionValueDecoder.mjs.map +1 -1
  441. package/dist/node-prod/packages/hydrogen-react/package.json.js +1 -1
  442. package/dist/node-prod/packages/hydrogen-react/package.json.mjs +1 -1
  443. package/dist/node-prod/parse-metafield.js +2 -2
  444. package/dist/node-prod/parse-metafield.js.map +1 -1
  445. package/dist/node-prod/parse-metafield.mjs +2 -2
  446. package/dist/node-prod/parse-metafield.mjs.map +1 -1
  447. package/dist/node-prod/storefront-client.js +1 -2
  448. package/dist/node-prod/storefront-client.js.map +1 -1
  449. package/dist/node-prod/storefront-client.mjs +1 -2
  450. package/dist/node-prod/storefront-client.mjs.map +1 -1
  451. package/dist/node-prod/tracking-utils.js +88 -0
  452. package/dist/node-prod/tracking-utils.js.map +1 -0
  453. package/dist/node-prod/tracking-utils.mjs +88 -0
  454. package/dist/node-prod/tracking-utils.mjs.map +1 -0
  455. package/dist/node-prod/useCartAPIStateMachine.js +6 -12
  456. package/dist/node-prod/useCartAPIStateMachine.js.map +1 -1
  457. package/dist/node-prod/useCartAPIStateMachine.mjs +6 -12
  458. package/dist/node-prod/useCartAPIStateMachine.mjs.map +1 -1
  459. package/dist/node-prod/useCartActions.js.map +1 -1
  460. package/dist/node-prod/useCartActions.mjs.map +1 -1
  461. package/dist/node-prod/useMoney.js.map +1 -1
  462. package/dist/node-prod/useMoney.mjs.map +1 -1
  463. package/dist/node-prod/useSelectedOptionInUrlParam.js.map +1 -1
  464. package/dist/node-prod/useSelectedOptionInUrlParam.mjs.map +1 -1
  465. package/dist/node-prod/useShopifyCookies.js +95 -9
  466. package/dist/node-prod/useShopifyCookies.js.map +1 -1
  467. package/dist/node-prod/useShopifyCookies.mjs +97 -11
  468. package/dist/node-prod/useShopifyCookies.mjs.map +1 -1
  469. package/dist/types/Image.d.ts +1 -1
  470. package/dist/types/ShopifyProvider.d.ts +5 -0
  471. package/dist/types/cookies-utils.d.ts +4 -0
  472. package/dist/types/index.d.cts +1 -0
  473. package/dist/types/index.d.ts +1 -0
  474. package/dist/types/tracking-utils.d.ts +22 -0
  475. package/dist/types/useShopifyCookies.d.ts +28 -2
  476. package/dist/umd/hydrogen-react.dev.js +543 -367
  477. package/dist/umd/hydrogen-react.dev.js.map +1 -1
  478. package/dist/umd/hydrogen-react.prod.js +18 -18
  479. package/dist/umd/hydrogen-react.prod.js.map +1 -1
  480. package/package.json +35 -22
@@ -1 +1 @@
1
- {"version":3,"file":"Money.js","sources":["../../src/Money.tsx"],"sourcesContent":["import {type ReactNode} from 'react';\nimport {useMoney} from './useMoney.js';\nimport type {MoneyV2, UnitPriceMeasurement} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\n\nexport interface MoneyPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** An object with fields that correspond to the Storefront API's [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2). */\n data: PartialDeep<MoneyV2, {recurseIntoArrays: true}>;\n /** Whether to remove the currency symbol from the output. */\n withoutCurrency?: boolean;\n /** Whether to remove trailing zeros (fractional money) from the output. */\n withoutTrailingZeros?: boolean;\n /** A [UnitPriceMeasurement object](https://shopify.dev/api/storefront/2025-01/objects/unitpricemeasurement). */\n measurement?: PartialDeep<UnitPriceMeasurement, {recurseIntoArrays: true}>;\n /** Customizes the separator between the money output and the measurement output. Used with the `measurement` prop. Defaults to `'/'`. */\n measurementSeparator?: ReactNode;\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type MoneyProps<ComponentGeneric extends React.ElementType> =\n MoneyPropsBase<ComponentGeneric> &\n (ComponentGeneric extends keyof React.JSX.IntrinsicElements\n ? Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof MoneyPropsBase<ComponentGeneric>\n >\n : React.ComponentPropsWithoutRef<ComponentGeneric>);\n\n/**\n * The `Money` component renders a string of the Storefront API's\n * [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2)\n * according to the `locale` in the `ShopifyProvider` component.\n * &nbsp;\n * @see {@link https://shopify.dev/api/hydrogen/components/money}\n * @example basic usage, outputs: $100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} />\n * ```\n * &nbsp;\n *\n * @example without currency, outputs: 100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutCurrency />\n * ```\n * &nbsp;\n *\n * @example without trailing zeros, outputs: $100\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutTrailingZeros />\n * ```\n * &nbsp;\n *\n * @example with per-unit measurement, outputs: $100.00 per G\n * ```ts\n * <Money\n * data={{amount: '100.00', currencyCode: 'USD'}}\n * measurement={{referenceUnit: 'G'}}\n * measurementSeparator=\" per \"\n * />\n * ```\n */\nexport function Money<ComponentGeneric extends React.ElementType = 'div'>({\n data,\n as,\n withoutCurrency,\n withoutTrailingZeros,\n measurement,\n measurementSeparator = '/',\n ...passthroughProps\n}: MoneyProps<ComponentGeneric>): JSX.Element {\n if (!isMoney(data)) {\n throw new Error(\n `<Money/> needs a valid 'data' prop that has 'amount' and 'currencyCode'`,\n );\n }\n const moneyObject = useMoney(data);\n const Wrapper = as ?? 'div';\n\n let output = moneyObject.localizedString;\n\n if (withoutCurrency || withoutTrailingZeros) {\n if (withoutCurrency && !withoutTrailingZeros) {\n output = moneyObject.amount;\n } else if (!withoutCurrency && withoutTrailingZeros) {\n output = moneyObject.withoutTrailingZeros;\n } else {\n // both\n output = moneyObject.withoutTrailingZerosAndCurrency;\n }\n }\n\n return (\n <Wrapper {...passthroughProps}>\n {output}\n {measurement && measurement.referenceUnit && (\n <>\n {measurementSeparator}\n {measurement.referenceUnit}\n </>\n )}\n </Wrapper>\n );\n}\n\n// required in order to narrow the money object down and make TS happy\nfunction isMoney(\n maybeMoney: PartialDeep<MoneyV2, {recurseIntoArrays: true}>,\n): maybeMoney is MoneyV2 {\n return (\n typeof maybeMoney.amount === 'string' &&\n !!maybeMoney.amount &&\n typeof maybeMoney.currencyCode === 'string' &&\n !!maybeMoney.currencyCode\n );\n}\n"],"names":["useMoney","jsxs","Fragment"],"mappings":";;;;AA+DO,SAAS,MAA0D;AAAA,EACxE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,GAAG;AACL,GAA8C;AACxC,MAAA,CAAC,QAAQ,IAAI,GAAG;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACM,QAAA,cAAcA,kBAAS,IAAI;AACjC,QAAM,UAAU,MAAM;AAEtB,MAAI,SAAS,YAAY;AAEzB,MAAI,mBAAmB,sBAAsB;AACvC,QAAA,mBAAmB,CAAC,sBAAsB;AAC5C,eAAS,YAAY;AAAA,IAAA,WACZ,CAAC,mBAAmB,sBAAsB;AACnD,eAAS,YAAY;AAAA,IAAA,OAChB;AAEL,eAAS,YAAY;AAAA,IACvB;AAAA,EACF;AAGE,SAAAC,2BAAA,KAAC,SAAS,EAAA,GAAG,kBACV,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,iBAEvBA,2BAAA,KAAAC,WAAA,UAAA,EAAA,UAAA;AAAA,MAAA;AAAA,MACA,YAAY;AAAA,IAAA,GACf;AAAA,EAEJ,EAAA,CAAA;AAEJ;AAGA,SAAS,QACP,YACuB;AACvB,SACE,OAAO,WAAW,WAAW,YAC7B,CAAC,CAAC,WAAW,UACb,OAAO,WAAW,iBAAiB,YACnC,CAAC,CAAC,WAAW;AAEjB;;"}
1
+ {"version":3,"file":"Money.js","sources":["../../src/Money.tsx"],"sourcesContent":["import {type ReactNode} from 'react';\nimport {useMoney} from './useMoney.js';\nimport type {MoneyV2, UnitPriceMeasurement} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\n\nexport interface MoneyPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** An object with fields that correspond to the Storefront API's [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2). */\n data: PartialDeep<MoneyV2, {recurseIntoArrays: true}>;\n /** Whether to remove the currency symbol from the output. */\n withoutCurrency?: boolean;\n /** Whether to remove trailing zeros (fractional money) from the output. */\n withoutTrailingZeros?: boolean;\n /** A [UnitPriceMeasurement object](https://shopify.dev/api/storefront/2025-01/objects/unitpricemeasurement). */\n measurement?: PartialDeep<UnitPriceMeasurement, {recurseIntoArrays: true}>;\n /** Customizes the separator between the money output and the measurement output. Used with the `measurement` prop. Defaults to `'/'`. */\n measurementSeparator?: ReactNode;\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type MoneyProps<ComponentGeneric extends React.ElementType> =\n MoneyPropsBase<ComponentGeneric> &\n (ComponentGeneric extends keyof React.JSX.IntrinsicElements\n ? Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof MoneyPropsBase<ComponentGeneric>\n >\n : React.ComponentPropsWithoutRef<ComponentGeneric>);\n\n/**\n * The `Money` component renders a string of the Storefront API's\n * [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2)\n * according to the `locale` in the `ShopifyProvider` component.\n * &nbsp;\n * @see {@link https://shopify.dev/api/hydrogen/components/money}\n * @example basic usage, outputs: $100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} />\n * ```\n * &nbsp;\n *\n * @example without currency, outputs: 100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutCurrency />\n * ```\n * &nbsp;\n *\n * @example without trailing zeros, outputs: $100\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutTrailingZeros />\n * ```\n * &nbsp;\n *\n * @example with per-unit measurement, outputs: $100.00 per G\n * ```ts\n * <Money\n * data={{amount: '100.00', currencyCode: 'USD'}}\n * measurement={{referenceUnit: 'G'}}\n * measurementSeparator=\" per \"\n * />\n * ```\n */\nexport function Money<ComponentGeneric extends React.ElementType = 'div'>({\n data,\n as,\n withoutCurrency,\n withoutTrailingZeros,\n measurement,\n measurementSeparator = '/',\n ...passthroughProps\n}: MoneyProps<ComponentGeneric>): JSX.Element {\n if (!isMoney(data)) {\n throw new Error(\n `<Money/> needs a valid 'data' prop that has 'amount' and 'currencyCode'`,\n );\n }\n const moneyObject = useMoney(data);\n const Wrapper = as ?? 'div';\n\n let output = moneyObject.localizedString;\n\n if (withoutCurrency || withoutTrailingZeros) {\n if (withoutCurrency && !withoutTrailingZeros) {\n output = moneyObject.amount;\n } else if (!withoutCurrency && withoutTrailingZeros) {\n output = moneyObject.withoutTrailingZeros;\n } else {\n // both\n output = moneyObject.withoutTrailingZerosAndCurrency;\n }\n }\n\n return (\n <Wrapper {...passthroughProps}>\n {output}\n {measurement && measurement.referenceUnit && (\n <>\n {measurementSeparator}\n {measurement.referenceUnit}\n </>\n )}\n </Wrapper>\n );\n}\n\n// required in order to narrow the money object down and make TS happy\nfunction isMoney(\n maybeMoney: PartialDeep<MoneyV2, {recurseIntoArrays: true}>,\n): maybeMoney is MoneyV2 {\n return (\n typeof maybeMoney.amount === 'string' &&\n !!maybeMoney.amount &&\n typeof maybeMoney.currencyCode === 'string' &&\n !!maybeMoney.currencyCode\n );\n}\n"],"names":["useMoney","jsxs","Fragment"],"mappings":";;;;AA+DO,SAAS,MAA0D;AAAA,EACxE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,GAAG;AACL,GAA8C;AACxC,MAAA,CAAC,QAAQ,IAAI,GAAG;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAEI,QAAA,cAAcA,kBAAS,IAAI;AACjC,QAAM,UAAU,MAAM;AAEtB,MAAI,SAAS,YAAY;AAEzB,MAAI,mBAAmB,sBAAsB;AACvC,QAAA,mBAAmB,CAAC,sBAAsB;AAC5C,eAAS,YAAY;AAAA,IAAA,WACZ,CAAC,mBAAmB,sBAAsB;AACnD,eAAS,YAAY;AAAA,IAAA,OAChB;AAEL,eAAS,YAAY;AAAA,IAAA;AAAA,EACvB;AAIA,SAAAC,2BAAA,KAAC,SAAS,EAAA,GAAG,kBACV,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,iBAEvBA,2BAAA,KAAAC,WAAA,UAAA,EAAA,UAAA;AAAA,MAAA;AAAA,MACA,YAAY;AAAA,IAAA,EACf,CAAA;AAAA,EAAA,GAEJ;AAEJ;AAGA,SAAS,QACP,YACuB;AACvB,SACE,OAAO,WAAW,WAAW,YAC7B,CAAC,CAAC,WAAW,UACb,OAAO,WAAW,iBAAiB,YACnC,CAAC,CAAC,WAAW;AAEjB;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"Money.mjs","sources":["../../src/Money.tsx"],"sourcesContent":["import {type ReactNode} from 'react';\nimport {useMoney} from './useMoney.js';\nimport type {MoneyV2, UnitPriceMeasurement} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\n\nexport interface MoneyPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** An object with fields that correspond to the Storefront API's [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2). */\n data: PartialDeep<MoneyV2, {recurseIntoArrays: true}>;\n /** Whether to remove the currency symbol from the output. */\n withoutCurrency?: boolean;\n /** Whether to remove trailing zeros (fractional money) from the output. */\n withoutTrailingZeros?: boolean;\n /** A [UnitPriceMeasurement object](https://shopify.dev/api/storefront/2025-01/objects/unitpricemeasurement). */\n measurement?: PartialDeep<UnitPriceMeasurement, {recurseIntoArrays: true}>;\n /** Customizes the separator between the money output and the measurement output. Used with the `measurement` prop. Defaults to `'/'`. */\n measurementSeparator?: ReactNode;\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type MoneyProps<ComponentGeneric extends React.ElementType> =\n MoneyPropsBase<ComponentGeneric> &\n (ComponentGeneric extends keyof React.JSX.IntrinsicElements\n ? Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof MoneyPropsBase<ComponentGeneric>\n >\n : React.ComponentPropsWithoutRef<ComponentGeneric>);\n\n/**\n * The `Money` component renders a string of the Storefront API's\n * [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2)\n * according to the `locale` in the `ShopifyProvider` component.\n * &nbsp;\n * @see {@link https://shopify.dev/api/hydrogen/components/money}\n * @example basic usage, outputs: $100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} />\n * ```\n * &nbsp;\n *\n * @example without currency, outputs: 100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutCurrency />\n * ```\n * &nbsp;\n *\n * @example without trailing zeros, outputs: $100\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutTrailingZeros />\n * ```\n * &nbsp;\n *\n * @example with per-unit measurement, outputs: $100.00 per G\n * ```ts\n * <Money\n * data={{amount: '100.00', currencyCode: 'USD'}}\n * measurement={{referenceUnit: 'G'}}\n * measurementSeparator=\" per \"\n * />\n * ```\n */\nexport function Money<ComponentGeneric extends React.ElementType = 'div'>({\n data,\n as,\n withoutCurrency,\n withoutTrailingZeros,\n measurement,\n measurementSeparator = '/',\n ...passthroughProps\n}: MoneyProps<ComponentGeneric>): JSX.Element {\n if (!isMoney(data)) {\n throw new Error(\n `<Money/> needs a valid 'data' prop that has 'amount' and 'currencyCode'`,\n );\n }\n const moneyObject = useMoney(data);\n const Wrapper = as ?? 'div';\n\n let output = moneyObject.localizedString;\n\n if (withoutCurrency || withoutTrailingZeros) {\n if (withoutCurrency && !withoutTrailingZeros) {\n output = moneyObject.amount;\n } else if (!withoutCurrency && withoutTrailingZeros) {\n output = moneyObject.withoutTrailingZeros;\n } else {\n // both\n output = moneyObject.withoutTrailingZerosAndCurrency;\n }\n }\n\n return (\n <Wrapper {...passthroughProps}>\n {output}\n {measurement && measurement.referenceUnit && (\n <>\n {measurementSeparator}\n {measurement.referenceUnit}\n </>\n )}\n </Wrapper>\n );\n}\n\n// required in order to narrow the money object down and make TS happy\nfunction isMoney(\n maybeMoney: PartialDeep<MoneyV2, {recurseIntoArrays: true}>,\n): maybeMoney is MoneyV2 {\n return (\n typeof maybeMoney.amount === 'string' &&\n !!maybeMoney.amount &&\n typeof maybeMoney.currencyCode === 'string' &&\n !!maybeMoney.currencyCode\n );\n}\n"],"names":[],"mappings":";;AA+DO,SAAS,MAA0D;AAAA,EACxE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,GAAG;AACL,GAA8C;AACxC,MAAA,CAAC,QAAQ,IAAI,GAAG;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AACM,QAAA,cAAc,SAAS,IAAI;AACjC,QAAM,UAAU,MAAM;AAEtB,MAAI,SAAS,YAAY;AAEzB,MAAI,mBAAmB,sBAAsB;AACvC,QAAA,mBAAmB,CAAC,sBAAsB;AAC5C,eAAS,YAAY;AAAA,IAAA,WACZ,CAAC,mBAAmB,sBAAsB;AACnD,eAAS,YAAY;AAAA,IAAA,OAChB;AAEL,eAAS,YAAY;AAAA,IACvB;AAAA,EACF;AAGE,SAAA,qBAAC,SAAS,EAAA,GAAG,kBACV,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,iBAEvB,qBAAA,UAAA,EAAA,UAAA;AAAA,MAAA;AAAA,MACA,YAAY;AAAA,IAAA,GACf;AAAA,EAEJ,EAAA,CAAA;AAEJ;AAGA,SAAS,QACP,YACuB;AACvB,SACE,OAAO,WAAW,WAAW,YAC7B,CAAC,CAAC,WAAW,UACb,OAAO,WAAW,iBAAiB,YACnC,CAAC,CAAC,WAAW;AAEjB;"}
1
+ {"version":3,"file":"Money.mjs","sources":["../../src/Money.tsx"],"sourcesContent":["import {type ReactNode} from 'react';\nimport {useMoney} from './useMoney.js';\nimport type {MoneyV2, UnitPriceMeasurement} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\n\nexport interface MoneyPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** An object with fields that correspond to the Storefront API's [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2). */\n data: PartialDeep<MoneyV2, {recurseIntoArrays: true}>;\n /** Whether to remove the currency symbol from the output. */\n withoutCurrency?: boolean;\n /** Whether to remove trailing zeros (fractional money) from the output. */\n withoutTrailingZeros?: boolean;\n /** A [UnitPriceMeasurement object](https://shopify.dev/api/storefront/2025-01/objects/unitpricemeasurement). */\n measurement?: PartialDeep<UnitPriceMeasurement, {recurseIntoArrays: true}>;\n /** Customizes the separator between the money output and the measurement output. Used with the `measurement` prop. Defaults to `'/'`. */\n measurementSeparator?: ReactNode;\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type MoneyProps<ComponentGeneric extends React.ElementType> =\n MoneyPropsBase<ComponentGeneric> &\n (ComponentGeneric extends keyof React.JSX.IntrinsicElements\n ? Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof MoneyPropsBase<ComponentGeneric>\n >\n : React.ComponentPropsWithoutRef<ComponentGeneric>);\n\n/**\n * The `Money` component renders a string of the Storefront API's\n * [MoneyV2 object](https://shopify.dev/api/storefront/reference/common-objects/moneyv2)\n * according to the `locale` in the `ShopifyProvider` component.\n * &nbsp;\n * @see {@link https://shopify.dev/api/hydrogen/components/money}\n * @example basic usage, outputs: $100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} />\n * ```\n * &nbsp;\n *\n * @example without currency, outputs: 100.00\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutCurrency />\n * ```\n * &nbsp;\n *\n * @example without trailing zeros, outputs: $100\n * ```ts\n * <Money data={{amount: '100.00', currencyCode: 'USD'}} withoutTrailingZeros />\n * ```\n * &nbsp;\n *\n * @example with per-unit measurement, outputs: $100.00 per G\n * ```ts\n * <Money\n * data={{amount: '100.00', currencyCode: 'USD'}}\n * measurement={{referenceUnit: 'G'}}\n * measurementSeparator=\" per \"\n * />\n * ```\n */\nexport function Money<ComponentGeneric extends React.ElementType = 'div'>({\n data,\n as,\n withoutCurrency,\n withoutTrailingZeros,\n measurement,\n measurementSeparator = '/',\n ...passthroughProps\n}: MoneyProps<ComponentGeneric>): JSX.Element {\n if (!isMoney(data)) {\n throw new Error(\n `<Money/> needs a valid 'data' prop that has 'amount' and 'currencyCode'`,\n );\n }\n const moneyObject = useMoney(data);\n const Wrapper = as ?? 'div';\n\n let output = moneyObject.localizedString;\n\n if (withoutCurrency || withoutTrailingZeros) {\n if (withoutCurrency && !withoutTrailingZeros) {\n output = moneyObject.amount;\n } else if (!withoutCurrency && withoutTrailingZeros) {\n output = moneyObject.withoutTrailingZeros;\n } else {\n // both\n output = moneyObject.withoutTrailingZerosAndCurrency;\n }\n }\n\n return (\n <Wrapper {...passthroughProps}>\n {output}\n {measurement && measurement.referenceUnit && (\n <>\n {measurementSeparator}\n {measurement.referenceUnit}\n </>\n )}\n </Wrapper>\n );\n}\n\n// required in order to narrow the money object down and make TS happy\nfunction isMoney(\n maybeMoney: PartialDeep<MoneyV2, {recurseIntoArrays: true}>,\n): maybeMoney is MoneyV2 {\n return (\n typeof maybeMoney.amount === 'string' &&\n !!maybeMoney.amount &&\n typeof maybeMoney.currencyCode === 'string' &&\n !!maybeMoney.currencyCode\n );\n}\n"],"names":[],"mappings":";;AA+DO,SAAS,MAA0D;AAAA,EACxE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,uBAAuB;AAAA,EACvB,GAAG;AACL,GAA8C;AACxC,MAAA,CAAC,QAAQ,IAAI,GAAG;AAClB,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAEI,QAAA,cAAc,SAAS,IAAI;AACjC,QAAM,UAAU,MAAM;AAEtB,MAAI,SAAS,YAAY;AAEzB,MAAI,mBAAmB,sBAAsB;AACvC,QAAA,mBAAmB,CAAC,sBAAsB;AAC5C,eAAS,YAAY;AAAA,IAAA,WACZ,CAAC,mBAAmB,sBAAsB;AACnD,eAAS,YAAY;AAAA,IAAA,OAChB;AAEL,eAAS,YAAY;AAAA,IAAA;AAAA,EACvB;AAIA,SAAA,qBAAC,SAAS,EAAA,GAAG,kBACV,UAAA;AAAA,IAAA;AAAA,IACA,eAAe,YAAY,iBAEvB,qBAAA,UAAA,EAAA,UAAA;AAAA,MAAA;AAAA,MACA,YAAY;AAAA,IAAA,EACf,CAAA;AAAA,EAAA,GAEJ;AAEJ;AAGA,SAAS,QACP,YACuB;AACvB,SACE,OAAO,WAAW,WAAW,YAC7B,CAAC,CAAC,WAAW,UACb,OAAO,WAAW,iBAAiB,YACnC,CAAC,CAAC,WAAW;AAEjB;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductPrice.js","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null\n : null;\n\n const variantPriceProperty =\n valueType === 'max' ? 'maxVariantPrice' : 'minVariantPrice';\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n price = variant.compareAtPrice;\n } else {\n price = product?.compareAtPriceRange?.[variantPriceProperty];\n }\n\n let priceAsNumber: number;\n if (variantId && variant) {\n priceAsNumber = parseFloat(variant.price?.amount ?? '0');\n } else {\n priceAsNumber = parseFloat(\n product?.priceRange?.[variantPriceProperty]?.amount ?? '0',\n );\n }\n\n const compareAtPriceAsNumber = parseFloat(price?.amount ?? '0');\n\n if (priceAsNumber >= compareAtPriceAsNumber) {\n return null;\n }\n } else {\n if (variantId && variant) {\n price = variant.price;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["flattenConnection","variant","Money","jsx"],"mappings":";;;;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EACD,IAAA;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEI,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACZA,kBAAA,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IACzC,CAACC,cAAYA,qCAAS,QAAO;AAAA,EAAA,KAC1B,OACL;AAEE,QAAA,uBACJ,cAAc,QAAQ,oBAAoB;AAE5C,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAAA,IAAA,OACX;AACG,eAAA,wCAAS,wBAAT,mBAA+B;AAAA,IACzC;AAEI,QAAA;AACJ,QAAI,aAAa,SAAS;AACxB,sBAAgB,aAAW,aAAQ,UAAR,mBAAe,WAAU,GAAG;AAAA,IAAA,OAClD;AACW,sBAAA;AAAA,UACd,8CAAS,eAAT,mBAAsB,0BAAtB,mBAA6C,WAAU;AAAA,MAAA;AAAA,IAE3D;AAEA,UAAM,yBAAyB,YAAW,+BAAO,WAAU,GAAG;AAE9D,QAAI,iBAAiB,wBAAwB;AACpC,aAAA;AAAA,IACT;AAAA,EAAA,OACK;AACL,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAChB,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MACxB;AAAA,IAAA,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EACT;AAEA,MAAI,aAAa;AACf,0CACGC,MAAO,OAAA,EAAA,GAAG,kBAAkB,MAAM,OAAO,YAA0B,CAAA;AAAA,EAExE;AAEA,SAAQC,2BAAAA,IAAAD,MAAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,MAAO,CAAA;AACnD;;"}
1
+ {"version":3,"file":"ProductPrice.js","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? (flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null)\n : null;\n\n const variantPriceProperty =\n valueType === 'max' ? 'maxVariantPrice' : 'minVariantPrice';\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n price = variant.compareAtPrice;\n } else {\n price = product?.compareAtPriceRange?.[variantPriceProperty];\n }\n\n let priceAsNumber: number;\n if (variantId && variant) {\n priceAsNumber = parseFloat(variant.price?.amount ?? '0');\n } else {\n priceAsNumber = parseFloat(\n product?.priceRange?.[variantPriceProperty]?.amount ?? '0',\n );\n }\n\n const compareAtPriceAsNumber = parseFloat(price?.amount ?? '0');\n\n if (priceAsNumber >= compareAtPriceAsNumber) {\n return null;\n }\n } else {\n if (variantId && variant) {\n price = variant.price;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["flattenConnection","variant","Money","jsx"],"mappings":";;;;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,IACD;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EAAA;AAGrE,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACXA,kBAAA,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IAC1C,CAACC,cAAYA,qCAAS,QAAO;AAAA,OAC1B,OACL;AAEE,QAAA,uBACJ,cAAc,QAAQ,oBAAoB;AAE5C,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAAA,IAAA,OACX;AACG,eAAA,wCAAS,wBAAT,mBAA+B;AAAA,IAAoB;AAGzD,QAAA;AACJ,QAAI,aAAa,SAAS;AACxB,sBAAgB,aAAW,aAAQ,UAAR,mBAAe,WAAU,GAAG;AAAA,IAAA,OAClD;AACW,sBAAA;AAAA,UACd,8CAAS,eAAT,mBAAsB,0BAAtB,mBAA6C,WAAU;AAAA,MACzD;AAAA,IAAA;AAGF,UAAM,yBAAyB,YAAW,+BAAO,WAAU,GAAG;AAE9D,QAAI,iBAAiB,wBAAwB;AACpC,aAAA;AAAA,IAAA;AAAA,EACT,OACK;AACL,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAChB,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MAAA;AAAA,IACxB,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA;AAAA,EAC9B;AAGF,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EAAA;AAGT,MAAI,aAAa;AACf,0CACGC,MAAAA,OAAO,EAAA,GAAG,kBAAkB,MAAM,OAAO,aAA0B;AAAA,EAAA;AAIxE,SAAQC,2BAAAA,IAAAD,MAAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,OAAO;AACnD;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductPrice.mjs","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null\n : null;\n\n const variantPriceProperty =\n valueType === 'max' ? 'maxVariantPrice' : 'minVariantPrice';\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n price = variant.compareAtPrice;\n } else {\n price = product?.compareAtPriceRange?.[variantPriceProperty];\n }\n\n let priceAsNumber: number;\n if (variantId && variant) {\n priceAsNumber = parseFloat(variant.price?.amount ?? '0');\n } else {\n priceAsNumber = parseFloat(\n product?.priceRange?.[variantPriceProperty]?.amount ?? '0',\n );\n }\n\n const compareAtPriceAsNumber = parseFloat(price?.amount ?? '0');\n\n if (priceAsNumber >= compareAtPriceAsNumber) {\n return null;\n }\n } else {\n if (variantId && variant) {\n price = variant.price;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["variant"],"mappings":";;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EACD,IAAA;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EACzE;AAEI,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACZ,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IACzC,CAACA,cAAYA,qCAAS,QAAO;AAAA,EAAA,KAC1B,OACL;AAEE,QAAA,uBACJ,cAAc,QAAQ,oBAAoB;AAE5C,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAAA,IAAA,OACX;AACG,eAAA,wCAAS,wBAAT,mBAA+B;AAAA,IACzC;AAEI,QAAA;AACJ,QAAI,aAAa,SAAS;AACxB,sBAAgB,aAAW,aAAQ,UAAR,mBAAe,WAAU,GAAG;AAAA,IAAA,OAClD;AACW,sBAAA;AAAA,UACd,8CAAS,eAAT,mBAAsB,0BAAtB,mBAA6C,WAAU;AAAA,MAAA;AAAA,IAE3D;AAEA,UAAM,yBAAyB,YAAW,+BAAO,WAAU,GAAG;AAE9D,QAAI,iBAAiB,wBAAwB;AACpC,aAAA;AAAA,IACT;AAAA,EAAA,OACK;AACL,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAChB,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MACxB;AAAA,IAAA,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAC9B;AAAA,EACF;AAEA,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EACT;AAEA,MAAI,aAAa;AACf,+BACG,OAAO,EAAA,GAAG,kBAAkB,MAAM,OAAO,YAA0B,CAAA;AAAA,EAExE;AAEA,SAAQ,oBAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,MAAO,CAAA;AACnD;"}
1
+ {"version":3,"file":"ProductPrice.mjs","sources":["../../src/ProductPrice.tsx"],"sourcesContent":["import type {\n MoneyV2,\n UnitPriceMeasurement,\n Product,\n} from './storefront-api-types.js';\nimport {Money, type MoneyProps, type MoneyPropsBase} from './Money.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nexport interface ProductPriceProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** The type of price. Valid values: `regular` (default) or `compareAt`. */\n priceType?: 'regular' | 'compareAt';\n /** The type of value. Valid values: `min` (default), `max` or `unit`. */\n valueType?: 'max' | 'min' | 'unit';\n /** The ID of the variant. */\n variantId?: string;\n}\n\n/**\n * The `ProductPrice` component renders a `Money` component with the product\n * [`priceRange`](https://shopify.dev/api/storefront/reference/products/productpricerange)'s `maxVariantPrice` or `minVariantPrice`, for either the regular price or compare at price range.\n */\nexport function ProductPrice<\n ComponentGeneric extends React.ElementType = 'div',\n>(\n props: ProductPriceProps &\n Omit<MoneyProps<ComponentGeneric>, 'data' | 'measurement'>,\n): JSX.Element | null {\n const {\n priceType = 'regular',\n variantId,\n valueType = 'min',\n data: product,\n ...passthroughProps\n } = props;\n\n if (product == null) {\n throw new Error(`<ProductPrice/> requires a product as the 'data' prop`);\n }\n\n let price: Partial<MoneyV2> | undefined | null;\n let measurement: Partial<UnitPriceMeasurement> | undefined | null;\n\n const variant = variantId\n ? (flattenConnection(product?.variants ?? {}).find(\n (variant) => variant?.id === variantId,\n ) ?? null)\n : null;\n\n const variantPriceProperty =\n valueType === 'max' ? 'maxVariantPrice' : 'minVariantPrice';\n\n if (priceType === 'compareAt') {\n if (variantId && variant) {\n price = variant.compareAtPrice;\n } else {\n price = product?.compareAtPriceRange?.[variantPriceProperty];\n }\n\n let priceAsNumber: number;\n if (variantId && variant) {\n priceAsNumber = parseFloat(variant.price?.amount ?? '0');\n } else {\n priceAsNumber = parseFloat(\n product?.priceRange?.[variantPriceProperty]?.amount ?? '0',\n );\n }\n\n const compareAtPriceAsNumber = parseFloat(price?.amount ?? '0');\n\n if (priceAsNumber >= compareAtPriceAsNumber) {\n return null;\n }\n } else {\n if (variantId && variant) {\n price = variant.price;\n if (valueType === 'unit') {\n price = variant.unitPrice;\n measurement = variant.unitPriceMeasurement;\n }\n } else if (valueType === 'max') {\n price = product.priceRange?.maxVariantPrice;\n } else {\n price = product.priceRange?.minVariantPrice;\n }\n }\n\n if (!price) {\n return null;\n }\n\n if (measurement) {\n return (\n <Money {...passthroughProps} data={price} measurement={measurement} />\n );\n }\n\n return <Money {...passthroughProps} data={price} />;\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport interface ProductPricePropsForDocs<\n AsType extends React.ElementType = 'div',\n> extends Omit<MoneyPropsBase<AsType>, 'data' | 'measurement'>,\n ProductPriceProps {}\n"],"names":["variant"],"mappings":";;;AAwBO,SAAS,aAGd,OAEoB;;AACd,QAAA;AAAA,IACJ,YAAY;AAAA,IACZ;AAAA,IACA,YAAY;AAAA,IACZ,MAAM;AAAA,IACN,GAAG;AAAA,EAAA,IACD;AAEJ,MAAI,WAAW,MAAM;AACb,UAAA,IAAI,MAAM,uDAAuD;AAAA,EAAA;AAGrE,MAAA;AACA,MAAA;AAEJ,QAAM,UAAU,YACX,mBAAkB,mCAAS,aAAY,CAAE,CAAA,EAAE;AAAA,IAC1C,CAACA,cAAYA,qCAAS,QAAO;AAAA,OAC1B,OACL;AAEE,QAAA,uBACJ,cAAc,QAAQ,oBAAoB;AAE5C,MAAI,cAAc,aAAa;AAC7B,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAAA,IAAA,OACX;AACG,eAAA,wCAAS,wBAAT,mBAA+B;AAAA,IAAoB;AAGzD,QAAA;AACJ,QAAI,aAAa,SAAS;AACxB,sBAAgB,aAAW,aAAQ,UAAR,mBAAe,WAAU,GAAG;AAAA,IAAA,OAClD;AACW,sBAAA;AAAA,UACd,8CAAS,eAAT,mBAAsB,0BAAtB,mBAA6C,WAAU;AAAA,MACzD;AAAA,IAAA;AAGF,UAAM,yBAAyB,YAAW,+BAAO,WAAU,GAAG;AAE9D,QAAI,iBAAiB,wBAAwB;AACpC,aAAA;AAAA,IAAA;AAAA,EACT,OACK;AACL,QAAI,aAAa,SAAS;AACxB,cAAQ,QAAQ;AAChB,UAAI,cAAc,QAAQ;AACxB,gBAAQ,QAAQ;AAChB,sBAAc,QAAQ;AAAA,MAAA;AAAA,IACxB,WACS,cAAc,OAAO;AAC9B,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA,OACvB;AACL,eAAQ,aAAQ,eAAR,mBAAoB;AAAA,IAAA;AAAA,EAC9B;AAGF,MAAI,CAAC,OAAO;AACH,WAAA;AAAA,EAAA;AAGT,MAAI,aAAa;AACf,+BACG,OAAO,EAAA,GAAG,kBAAkB,MAAM,OAAO,aAA0B;AAAA,EAAA;AAIxE,SAAQ,oBAAA,OAAA,EAAO,GAAG,kBAAkB,MAAM,OAAO;AACnD;"}
@@ -133,17 +133,20 @@ function getSelectedVariant(variants, choices) {
133
133
  });
134
134
  }
135
135
  function getOptions(variants) {
136
- const map = variants.reduce((memo, variant) => {
137
- var _a;
138
- if (!variant.selectedOptions) {
139
- throw new Error(`'getOptions' requires 'variant.selectedOptions'`);
140
- }
141
- (_a = variant == null ? void 0 : variant.selectedOptions) == null ? void 0 : _a.forEach((opt) => {
142
- memo[(opt == null ? void 0 : opt.name) ?? ""] = memo[(opt == null ? void 0 : opt.name) ?? ""] || /* @__PURE__ */ new Set();
143
- memo[(opt == null ? void 0 : opt.name) ?? ""].add((opt == null ? void 0 : opt.value) ?? "");
144
- });
145
- return memo;
146
- }, {});
136
+ const map = variants.reduce(
137
+ (memo, variant) => {
138
+ var _a;
139
+ if (!variant.selectedOptions) {
140
+ throw new Error(`'getOptions' requires 'variant.selectedOptions'`);
141
+ }
142
+ (_a = variant == null ? void 0 : variant.selectedOptions) == null ? void 0 : _a.forEach((opt) => {
143
+ memo[(opt == null ? void 0 : opt.name) ?? ""] = memo[(opt == null ? void 0 : opt.name) ?? ""] || /* @__PURE__ */ new Set();
144
+ memo[(opt == null ? void 0 : opt.name) ?? ""].add((opt == null ? void 0 : opt.value) ?? "");
145
+ });
146
+ return memo;
147
+ },
148
+ {}
149
+ );
147
150
  return Object.keys(map).map((option) => {
148
151
  return {
149
152
  name: option,
@@ -1 +1 @@
1
- {"version":3,"file":"ProductProvider.js","sources":["../../src/ProductProvider.tsx"],"sourcesContent":["import {\n useMemo,\n useState,\n useEffect,\n useCallback,\n createContext,\n useContext,\n} from 'react';\nimport type {\n SelectedOption as SelectedOptionType,\n SellingPlan,\n SellingPlanAllocation,\n Product,\n ProductVariant as ProductVariantType,\n ProductVariantConnection,\n SellingPlan as SellingPlanType,\n SellingPlanAllocation as SellingPlanAllocationType,\n SellingPlanGroup as SellingPlanGroupType,\n SellingPlanGroupConnection,\n} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nconst ProductOptionsContext = createContext<ProductHookValue | null>(null);\n\ntype InitialVariantId = ProductVariantType['id'] | null;\n\ninterface ProductProviderProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** A `ReactNode` element. */\n children: React.ReactNode;\n /**\n * The initially selected variant.\n * The following logic applies to `initialVariantId`:\n * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n */\n initialVariantId?: InitialVariantId;\n}\n\n/**\n * `<ProductProvider />` is a context provider that enables use of the `useProduct()` hook.\n *\n * It helps manage selected options and variants for a product.\n */\nexport function ProductProvider({\n children,\n data: product,\n initialVariantId: explicitVariantId,\n}: ProductProviderProps): JSX.Element {\n // The flattened variants\n const variants = useMemo(\n () => flattenConnection(product.variants ?? {}),\n [product.variants],\n );\n\n if (!isProductVariantArray(variants)) {\n throw new Error(\n `<ProductProvider/> requires 'product.variants.nodes' or 'product.variants.edges'`,\n );\n }\n\n // All the options available for a product, based on all the variants\n const options = useMemo(() => getOptions(variants), [variants]);\n\n /**\n * Track the selectedVariant within the provider.\n */\n const [selectedVariant, setSelectedVariant] = useState<\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null\n >(() => getVariantBasedOnIdProp(explicitVariantId, variants));\n\n /**\n * Track the selectedOptions within the provider. If a `initialVariantId`\n * is passed, use that to select initial options.\n */\n const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>(() =>\n getSelectedOptions(selectedVariant),\n );\n\n /**\n * When the initialVariantId changes, we need to make sure we\n * update the selected variant and selected options. If not,\n * then the selected variant and options will reference incorrect\n * values.\n */\n useEffect(() => {\n const newSelectedVariant = getVariantBasedOnIdProp(\n explicitVariantId,\n variants,\n );\n setSelectedVariant(newSelectedVariant);\n setSelectedOptions(getSelectedOptions(newSelectedVariant));\n }, [explicitVariantId, variants]);\n\n /**\n * Allow the developer to select an option.\n */\n const setSelectedOption = useCallback(\n (name: string, value: string) => {\n setSelectedOptions((selectedOptions) => {\n const opts = {...selectedOptions, [name]: value};\n setSelectedVariant(getSelectedVariant(variants, opts));\n return opts;\n });\n },\n [setSelectedOptions, variants],\n );\n\n const isOptionInStock = useCallback(\n (option: string, value: string) => {\n const proposedVariant = getSelectedVariant(variants, {\n ...selectedOptions,\n ...{[option]: value},\n });\n\n return proposedVariant?.availableForSale ?? true;\n },\n [selectedOptions, variants],\n );\n\n const sellingPlanGroups = useMemo(\n () =>\n flattenConnection(product.sellingPlanGroups ?? {}).map(\n (sellingPlanGroup) => ({\n ...sellingPlanGroup,\n sellingPlans: flattenConnection(sellingPlanGroup?.sellingPlans ?? {}),\n }),\n ),\n [product.sellingPlanGroups],\n );\n\n /**\n * Track the selectedSellingPlan within the hook. If `initialSellingPlanId`\n * is passed, use that as an initial value. Look it up from the `selectedVariant`, since\n * that is also a requirement.\n */\n const [selectedSellingPlan, setSelectedSellingPlan] = useState<\n PartialDeep<SellingPlan, {recurseIntoArrays: true}> | undefined\n >(undefined);\n\n const selectedSellingPlanAllocation = useMemo<\n PartialDeep<SellingPlanAllocation, {recurseIntoArrays: true}> | undefined\n >(() => {\n if (!selectedVariant || !selectedSellingPlan) {\n return;\n }\n\n if (\n !selectedVariant.sellingPlanAllocations?.nodes &&\n !selectedVariant.sellingPlanAllocations?.edges\n ) {\n throw new Error(\n `<ProductProvider/>: You must include 'sellingPlanAllocations.nodes' or 'sellingPlanAllocations.edges' in your variants in order to calculate selectedSellingPlanAllocation`,\n );\n }\n\n return flattenConnection(selectedVariant.sellingPlanAllocations).find(\n (allocation) => allocation?.sellingPlan?.id === selectedSellingPlan.id,\n );\n }, [selectedVariant, selectedSellingPlan]);\n\n const value = useMemo<ProductHookValue>(\n () => ({\n product,\n variants,\n variantsConnection: product.variants,\n options,\n selectedVariant,\n setSelectedVariant,\n selectedOptions,\n setSelectedOption,\n setSelectedOptions,\n isOptionInStock,\n selectedSellingPlan,\n setSelectedSellingPlan,\n selectedSellingPlanAllocation,\n sellingPlanGroups,\n sellingPlanGroupsConnection: product.sellingPlanGroups,\n }),\n [\n product,\n isOptionInStock,\n options,\n selectedOptions,\n selectedSellingPlan,\n selectedSellingPlanAllocation,\n selectedVariant,\n sellingPlanGroups,\n setSelectedOption,\n variants,\n ],\n );\n\n return (\n <ProductOptionsContext.Provider value={value}>\n {children}\n </ProductOptionsContext.Provider>\n );\n}\n\n/**\n * Provides access to the context value provided by `<ProductProvider />`. Must be a descendent of `<ProductProvider />`.\n */\nexport function useProduct(): ProductHookValue {\n const context = useContext(ProductOptionsContext);\n\n if (!context) {\n throw new Error(`'useProduct' must be a child of <ProductProvider />`);\n }\n\n return context;\n}\n\nfunction getSelectedVariant(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n choices: SelectedOptions,\n): PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined {\n /**\n * Ensure the user has selected all the required options, not just some.\n */\n if (\n !variants.length ||\n variants?.[0]?.selectedOptions?.length !== Object.keys(choices).length\n ) {\n return;\n }\n\n return variants?.find((variant) => {\n return Object.entries(choices).every(([name, value]) => {\n return variant?.selectedOptions?.some(\n (option) => option?.name === name && option?.value === value,\n );\n });\n });\n}\n\nfunction getOptions(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n): OptionWithValues[] {\n const map = variants.reduce((memo, variant) => {\n if (!variant.selectedOptions) {\n throw new Error(`'getOptions' requires 'variant.selectedOptions'`);\n }\n variant?.selectedOptions?.forEach((opt) => {\n memo[opt?.name ?? ''] = memo[opt?.name ?? ''] || new Set();\n memo[opt?.name ?? ''].add(opt?.value ?? '');\n });\n\n return memo;\n }, {} as Record<string, Set<string>>);\n\n return Object.keys(map).map((option) => {\n return {\n name: option,\n values: Array.from(map[option]),\n };\n });\n}\n\nfunction getVariantBasedOnIdProp(\n explicitVariantId: InitialVariantId | undefined,\n variants: Array<\n PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined\n >,\n):\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null {\n // get the initial variant based on the logic outlined in the comments for 'initialVariantId' above\n // * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n if (explicitVariantId) {\n const foundVariant = variants.find(\n (variant) => variant?.id === explicitVariantId,\n );\n if (!foundVariant) {\n console.warn(\n `<ProductProvider/> received a 'initialVariantId' prop, but could not actually find a variant with that ID`,\n );\n }\n return foundVariant;\n }\n // * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n if (explicitVariantId === null) {\n return null;\n }\n // * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n // * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n if (explicitVariantId === undefined) {\n return variants.find((variant) => variant?.availableForSale) || variants[0];\n }\n}\n\nfunction getSelectedOptions(\n selectedVariant:\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null,\n): SelectedOptions {\n return selectedVariant?.selectedOptions\n ? selectedVariant.selectedOptions.reduce<SelectedOptions>(\n (memo, optionSet) => {\n memo[optionSet?.name ?? ''] = optionSet?.value ?? '';\n return memo;\n },\n {},\n )\n : {};\n}\n\nfunction isProductVariantArray(\n maybeVariantArray:\n | (PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined)[]\n | undefined,\n): maybeVariantArray is PartialDeep<\n ProductVariantType,\n {recurseIntoArrays: true}\n>[] {\n if (!maybeVariantArray || !Array.isArray(maybeVariantArray)) {\n return false;\n }\n\n return true;\n}\n\nexport interface OptionWithValues {\n name: SelectedOptionType['name'];\n values: SelectedOptionType['value'][];\n}\n\ntype UseProductObjects = {\n /** The raw product from the Storefront API */\n product: Product;\n /** An array of the variant `nodes` from the `VariantConnection`. */\n variants: ProductVariantType[];\n variantsConnection?: ProductVariantConnection;\n /** An array of the product's options and values. */\n options: OptionWithValues[];\n /** The selected variant. */\n selectedVariant?: ProductVariantType | null;\n selectedOptions: SelectedOptions;\n /** The selected selling plan. */\n selectedSellingPlan?: SellingPlanType;\n /** The selected selling plan allocation. */\n selectedSellingPlanAllocation?: SellingPlanAllocationType;\n /** The selling plan groups. */\n sellingPlanGroups?: (Omit<SellingPlanGroupType, 'sellingPlans'> & {\n sellingPlans: SellingPlanType[];\n })[];\n sellingPlanGroupsConnection?: SellingPlanGroupConnection;\n};\n\ntype UseProductFunctions = {\n /** A callback to set the selected variant to the variant passed as an argument. */\n setSelectedVariant: (\n variant: PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | null,\n ) => void;\n /** A callback to set the selected option. */\n setSelectedOption: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => void;\n /** A callback to set multiple selected options at once. */\n setSelectedOptions: (options: SelectedOptions) => void;\n /** A callback to set the selected selling plan to the one passed as an argument. */\n setSelectedSellingPlan: (\n sellingPlan: PartialDeep<SellingPlanType, {recurseIntoArrays: true}>,\n ) => void;\n /** A callback that returns a boolean indicating if the option is in stock. */\n isOptionInStock: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => boolean;\n};\n\ntype ProductHookValue = PartialDeep<\n UseProductObjects,\n {recurseIntoArrays: true}\n> &\n UseProductFunctions;\n\nexport type SelectedOptions = {\n [key: string]: string;\n};\n"],"names":["createContext","useMemo","flattenConnection","useState","useEffect","useCallback","value","selectedOptions","_a","jsx","useContext"],"mappings":"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"}
1
+ {"version":3,"file":"ProductProvider.js","sources":["../../src/ProductProvider.tsx"],"sourcesContent":["import {\n useMemo,\n useState,\n useEffect,\n useCallback,\n createContext,\n useContext,\n} from 'react';\nimport type {\n SelectedOption as SelectedOptionType,\n SellingPlan,\n SellingPlanAllocation,\n Product,\n ProductVariant as ProductVariantType,\n ProductVariantConnection,\n SellingPlan as SellingPlanType,\n SellingPlanAllocation as SellingPlanAllocationType,\n SellingPlanGroup as SellingPlanGroupType,\n SellingPlanGroupConnection,\n} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nconst ProductOptionsContext = createContext<ProductHookValue | null>(null);\n\ntype InitialVariantId = ProductVariantType['id'] | null;\n\ninterface ProductProviderProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** A `ReactNode` element. */\n children: React.ReactNode;\n /**\n * The initially selected variant.\n * The following logic applies to `initialVariantId`:\n * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n */\n initialVariantId?: InitialVariantId;\n}\n\n/**\n * `<ProductProvider />` is a context provider that enables use of the `useProduct()` hook.\n *\n * It helps manage selected options and variants for a product.\n */\nexport function ProductProvider({\n children,\n data: product,\n initialVariantId: explicitVariantId,\n}: ProductProviderProps): JSX.Element {\n // The flattened variants\n const variants = useMemo(\n () => flattenConnection(product.variants ?? {}),\n [product.variants],\n );\n\n if (!isProductVariantArray(variants)) {\n throw new Error(\n `<ProductProvider/> requires 'product.variants.nodes' or 'product.variants.edges'`,\n );\n }\n\n // All the options available for a product, based on all the variants\n const options = useMemo(() => getOptions(variants), [variants]);\n\n /**\n * Track the selectedVariant within the provider.\n */\n const [selectedVariant, setSelectedVariant] = useState<\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null\n >(() => getVariantBasedOnIdProp(explicitVariantId, variants));\n\n /**\n * Track the selectedOptions within the provider. If a `initialVariantId`\n * is passed, use that to select initial options.\n */\n const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>(() =>\n getSelectedOptions(selectedVariant),\n );\n\n /**\n * When the initialVariantId changes, we need to make sure we\n * update the selected variant and selected options. If not,\n * then the selected variant and options will reference incorrect\n * values.\n */\n useEffect(() => {\n const newSelectedVariant = getVariantBasedOnIdProp(\n explicitVariantId,\n variants,\n );\n setSelectedVariant(newSelectedVariant);\n setSelectedOptions(getSelectedOptions(newSelectedVariant));\n }, [explicitVariantId, variants]);\n\n /**\n * Allow the developer to select an option.\n */\n const setSelectedOption = useCallback(\n (name: string, value: string) => {\n setSelectedOptions((selectedOptions) => {\n const opts = {...selectedOptions, [name]: value};\n setSelectedVariant(getSelectedVariant(variants, opts));\n return opts;\n });\n },\n [setSelectedOptions, variants],\n );\n\n const isOptionInStock = useCallback(\n (option: string, value: string) => {\n const proposedVariant = getSelectedVariant(variants, {\n ...selectedOptions,\n ...{[option]: value},\n });\n\n return proposedVariant?.availableForSale ?? true;\n },\n [selectedOptions, variants],\n );\n\n const sellingPlanGroups = useMemo(\n () =>\n flattenConnection(product.sellingPlanGroups ?? {}).map(\n (sellingPlanGroup) => ({\n ...sellingPlanGroup,\n sellingPlans: flattenConnection(sellingPlanGroup?.sellingPlans ?? {}),\n }),\n ),\n [product.sellingPlanGroups],\n );\n\n /**\n * Track the selectedSellingPlan within the hook. If `initialSellingPlanId`\n * is passed, use that as an initial value. Look it up from the `selectedVariant`, since\n * that is also a requirement.\n */\n const [selectedSellingPlan, setSelectedSellingPlan] = useState<\n PartialDeep<SellingPlan, {recurseIntoArrays: true}> | undefined\n >(undefined);\n\n const selectedSellingPlanAllocation = useMemo<\n PartialDeep<SellingPlanAllocation, {recurseIntoArrays: true}> | undefined\n >(() => {\n if (!selectedVariant || !selectedSellingPlan) {\n return;\n }\n\n if (\n !selectedVariant.sellingPlanAllocations?.nodes &&\n !selectedVariant.sellingPlanAllocations?.edges\n ) {\n throw new Error(\n `<ProductProvider/>: You must include 'sellingPlanAllocations.nodes' or 'sellingPlanAllocations.edges' in your variants in order to calculate selectedSellingPlanAllocation`,\n );\n }\n\n return flattenConnection(selectedVariant.sellingPlanAllocations).find(\n (allocation) => allocation?.sellingPlan?.id === selectedSellingPlan.id,\n );\n }, [selectedVariant, selectedSellingPlan]);\n\n const value = useMemo<ProductHookValue>(\n () => ({\n product,\n variants,\n variantsConnection: product.variants,\n options,\n selectedVariant,\n setSelectedVariant,\n selectedOptions,\n setSelectedOption,\n setSelectedOptions,\n isOptionInStock,\n selectedSellingPlan,\n setSelectedSellingPlan,\n selectedSellingPlanAllocation,\n sellingPlanGroups,\n sellingPlanGroupsConnection: product.sellingPlanGroups,\n }),\n [\n product,\n isOptionInStock,\n options,\n selectedOptions,\n selectedSellingPlan,\n selectedSellingPlanAllocation,\n selectedVariant,\n sellingPlanGroups,\n setSelectedOption,\n variants,\n ],\n );\n\n return (\n <ProductOptionsContext.Provider value={value}>\n {children}\n </ProductOptionsContext.Provider>\n );\n}\n\n/**\n * Provides access to the context value provided by `<ProductProvider />`. Must be a descendent of `<ProductProvider />`.\n */\nexport function useProduct(): ProductHookValue {\n const context = useContext(ProductOptionsContext);\n\n if (!context) {\n throw new Error(`'useProduct' must be a child of <ProductProvider />`);\n }\n\n return context;\n}\n\nfunction getSelectedVariant(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n choices: SelectedOptions,\n): PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined {\n /**\n * Ensure the user has selected all the required options, not just some.\n */\n if (\n !variants.length ||\n variants?.[0]?.selectedOptions?.length !== Object.keys(choices).length\n ) {\n return;\n }\n\n return variants?.find((variant) => {\n return Object.entries(choices).every(([name, value]) => {\n return variant?.selectedOptions?.some(\n (option) => option?.name === name && option?.value === value,\n );\n });\n });\n}\n\nfunction getOptions(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n): OptionWithValues[] {\n const map = variants.reduce(\n (memo, variant) => {\n if (!variant.selectedOptions) {\n throw new Error(`'getOptions' requires 'variant.selectedOptions'`);\n }\n variant?.selectedOptions?.forEach((opt) => {\n memo[opt?.name ?? ''] = memo[opt?.name ?? ''] || new Set();\n memo[opt?.name ?? ''].add(opt?.value ?? '');\n });\n\n return memo;\n },\n {} as Record<string, Set<string>>,\n );\n\n return Object.keys(map).map((option) => {\n return {\n name: option,\n values: Array.from(map[option]),\n };\n });\n}\n\nfunction getVariantBasedOnIdProp(\n explicitVariantId: InitialVariantId | undefined,\n variants: Array<\n PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined\n >,\n):\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null {\n // get the initial variant based on the logic outlined in the comments for 'initialVariantId' above\n // * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n if (explicitVariantId) {\n const foundVariant = variants.find(\n (variant) => variant?.id === explicitVariantId,\n );\n if (!foundVariant) {\n console.warn(\n `<ProductProvider/> received a 'initialVariantId' prop, but could not actually find a variant with that ID`,\n );\n }\n return foundVariant;\n }\n // * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n if (explicitVariantId === null) {\n return null;\n }\n // * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n // * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n if (explicitVariantId === undefined) {\n return variants.find((variant) => variant?.availableForSale) || variants[0];\n }\n}\n\nfunction getSelectedOptions(\n selectedVariant:\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null,\n): SelectedOptions {\n return selectedVariant?.selectedOptions\n ? selectedVariant.selectedOptions.reduce<SelectedOptions>(\n (memo, optionSet) => {\n memo[optionSet?.name ?? ''] = optionSet?.value ?? '';\n return memo;\n },\n {},\n )\n : {};\n}\n\nfunction isProductVariantArray(\n maybeVariantArray:\n | (PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined)[]\n | undefined,\n): maybeVariantArray is PartialDeep<\n ProductVariantType,\n {recurseIntoArrays: true}\n>[] {\n if (!maybeVariantArray || !Array.isArray(maybeVariantArray)) {\n return false;\n }\n\n return true;\n}\n\nexport interface OptionWithValues {\n name: SelectedOptionType['name'];\n values: SelectedOptionType['value'][];\n}\n\ntype UseProductObjects = {\n /** The raw product from the Storefront API */\n product: Product;\n /** An array of the variant `nodes` from the `VariantConnection`. */\n variants: ProductVariantType[];\n variantsConnection?: ProductVariantConnection;\n /** An array of the product's options and values. */\n options: OptionWithValues[];\n /** The selected variant. */\n selectedVariant?: ProductVariantType | null;\n selectedOptions: SelectedOptions;\n /** The selected selling plan. */\n selectedSellingPlan?: SellingPlanType;\n /** The selected selling plan allocation. */\n selectedSellingPlanAllocation?: SellingPlanAllocationType;\n /** The selling plan groups. */\n sellingPlanGroups?: (Omit<SellingPlanGroupType, 'sellingPlans'> & {\n sellingPlans: SellingPlanType[];\n })[];\n sellingPlanGroupsConnection?: SellingPlanGroupConnection;\n};\n\ntype UseProductFunctions = {\n /** A callback to set the selected variant to the variant passed as an argument. */\n setSelectedVariant: (\n variant: PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | null,\n ) => void;\n /** A callback to set the selected option. */\n setSelectedOption: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => void;\n /** A callback to set multiple selected options at once. */\n setSelectedOptions: (options: SelectedOptions) => void;\n /** A callback to set the selected selling plan to the one passed as an argument. */\n setSelectedSellingPlan: (\n sellingPlan: PartialDeep<SellingPlanType, {recurseIntoArrays: true}>,\n ) => void;\n /** A callback that returns a boolean indicating if the option is in stock. */\n isOptionInStock: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => boolean;\n};\n\ntype ProductHookValue = PartialDeep<\n UseProductObjects,\n {recurseIntoArrays: true}\n> &\n UseProductFunctions;\n\nexport type SelectedOptions = {\n [key: string]: string;\n};\n"],"names":["createContext","useMemo","flattenConnection","useState","useEffect","useCallback","value","selectedOptions","_a","jsx","useContext"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { createContext, useMemo, useState, useEffect, useCallback, useContext } from "react";
2
+ import { useMemo, useState, useEffect, useCallback, createContext, useContext } from "react";
3
3
  import { flattenConnection } from "./flatten-connection.mjs";
4
4
  const ProductOptionsContext = createContext(null);
5
5
  function ProductProvider({
@@ -131,17 +131,20 @@ function getSelectedVariant(variants, choices) {
131
131
  });
132
132
  }
133
133
  function getOptions(variants) {
134
- const map = variants.reduce((memo, variant) => {
135
- var _a;
136
- if (!variant.selectedOptions) {
137
- throw new Error(`'getOptions' requires 'variant.selectedOptions'`);
138
- }
139
- (_a = variant == null ? void 0 : variant.selectedOptions) == null ? void 0 : _a.forEach((opt) => {
140
- memo[(opt == null ? void 0 : opt.name) ?? ""] = memo[(opt == null ? void 0 : opt.name) ?? ""] || /* @__PURE__ */ new Set();
141
- memo[(opt == null ? void 0 : opt.name) ?? ""].add((opt == null ? void 0 : opt.value) ?? "");
142
- });
143
- return memo;
144
- }, {});
134
+ const map = variants.reduce(
135
+ (memo, variant) => {
136
+ var _a;
137
+ if (!variant.selectedOptions) {
138
+ throw new Error(`'getOptions' requires 'variant.selectedOptions'`);
139
+ }
140
+ (_a = variant == null ? void 0 : variant.selectedOptions) == null ? void 0 : _a.forEach((opt) => {
141
+ memo[(opt == null ? void 0 : opt.name) ?? ""] = memo[(opt == null ? void 0 : opt.name) ?? ""] || /* @__PURE__ */ new Set();
142
+ memo[(opt == null ? void 0 : opt.name) ?? ""].add((opt == null ? void 0 : opt.value) ?? "");
143
+ });
144
+ return memo;
145
+ },
146
+ {}
147
+ );
145
148
  return Object.keys(map).map((option) => {
146
149
  return {
147
150
  name: option,
@@ -1 +1 @@
1
- {"version":3,"file":"ProductProvider.mjs","sources":["../../src/ProductProvider.tsx"],"sourcesContent":["import {\n useMemo,\n useState,\n useEffect,\n useCallback,\n createContext,\n useContext,\n} from 'react';\nimport type {\n SelectedOption as SelectedOptionType,\n SellingPlan,\n SellingPlanAllocation,\n Product,\n ProductVariant as ProductVariantType,\n ProductVariantConnection,\n SellingPlan as SellingPlanType,\n SellingPlanAllocation as SellingPlanAllocationType,\n SellingPlanGroup as SellingPlanGroupType,\n SellingPlanGroupConnection,\n} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nconst ProductOptionsContext = createContext<ProductHookValue | null>(null);\n\ntype InitialVariantId = ProductVariantType['id'] | null;\n\ninterface ProductProviderProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** A `ReactNode` element. */\n children: React.ReactNode;\n /**\n * The initially selected variant.\n * The following logic applies to `initialVariantId`:\n * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n */\n initialVariantId?: InitialVariantId;\n}\n\n/**\n * `<ProductProvider />` is a context provider that enables use of the `useProduct()` hook.\n *\n * It helps manage selected options and variants for a product.\n */\nexport function ProductProvider({\n children,\n data: product,\n initialVariantId: explicitVariantId,\n}: ProductProviderProps): JSX.Element {\n // The flattened variants\n const variants = useMemo(\n () => flattenConnection(product.variants ?? {}),\n [product.variants],\n );\n\n if (!isProductVariantArray(variants)) {\n throw new Error(\n `<ProductProvider/> requires 'product.variants.nodes' or 'product.variants.edges'`,\n );\n }\n\n // All the options available for a product, based on all the variants\n const options = useMemo(() => getOptions(variants), [variants]);\n\n /**\n * Track the selectedVariant within the provider.\n */\n const [selectedVariant, setSelectedVariant] = useState<\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null\n >(() => getVariantBasedOnIdProp(explicitVariantId, variants));\n\n /**\n * Track the selectedOptions within the provider. If a `initialVariantId`\n * is passed, use that to select initial options.\n */\n const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>(() =>\n getSelectedOptions(selectedVariant),\n );\n\n /**\n * When the initialVariantId changes, we need to make sure we\n * update the selected variant and selected options. If not,\n * then the selected variant and options will reference incorrect\n * values.\n */\n useEffect(() => {\n const newSelectedVariant = getVariantBasedOnIdProp(\n explicitVariantId,\n variants,\n );\n setSelectedVariant(newSelectedVariant);\n setSelectedOptions(getSelectedOptions(newSelectedVariant));\n }, [explicitVariantId, variants]);\n\n /**\n * Allow the developer to select an option.\n */\n const setSelectedOption = useCallback(\n (name: string, value: string) => {\n setSelectedOptions((selectedOptions) => {\n const opts = {...selectedOptions, [name]: value};\n setSelectedVariant(getSelectedVariant(variants, opts));\n return opts;\n });\n },\n [setSelectedOptions, variants],\n );\n\n const isOptionInStock = useCallback(\n (option: string, value: string) => {\n const proposedVariant = getSelectedVariant(variants, {\n ...selectedOptions,\n ...{[option]: value},\n });\n\n return proposedVariant?.availableForSale ?? true;\n },\n [selectedOptions, variants],\n );\n\n const sellingPlanGroups = useMemo(\n () =>\n flattenConnection(product.sellingPlanGroups ?? {}).map(\n (sellingPlanGroup) => ({\n ...sellingPlanGroup,\n sellingPlans: flattenConnection(sellingPlanGroup?.sellingPlans ?? {}),\n }),\n ),\n [product.sellingPlanGroups],\n );\n\n /**\n * Track the selectedSellingPlan within the hook. If `initialSellingPlanId`\n * is passed, use that as an initial value. Look it up from the `selectedVariant`, since\n * that is also a requirement.\n */\n const [selectedSellingPlan, setSelectedSellingPlan] = useState<\n PartialDeep<SellingPlan, {recurseIntoArrays: true}> | undefined\n >(undefined);\n\n const selectedSellingPlanAllocation = useMemo<\n PartialDeep<SellingPlanAllocation, {recurseIntoArrays: true}> | undefined\n >(() => {\n if (!selectedVariant || !selectedSellingPlan) {\n return;\n }\n\n if (\n !selectedVariant.sellingPlanAllocations?.nodes &&\n !selectedVariant.sellingPlanAllocations?.edges\n ) {\n throw new Error(\n `<ProductProvider/>: You must include 'sellingPlanAllocations.nodes' or 'sellingPlanAllocations.edges' in your variants in order to calculate selectedSellingPlanAllocation`,\n );\n }\n\n return flattenConnection(selectedVariant.sellingPlanAllocations).find(\n (allocation) => allocation?.sellingPlan?.id === selectedSellingPlan.id,\n );\n }, [selectedVariant, selectedSellingPlan]);\n\n const value = useMemo<ProductHookValue>(\n () => ({\n product,\n variants,\n variantsConnection: product.variants,\n options,\n selectedVariant,\n setSelectedVariant,\n selectedOptions,\n setSelectedOption,\n setSelectedOptions,\n isOptionInStock,\n selectedSellingPlan,\n setSelectedSellingPlan,\n selectedSellingPlanAllocation,\n sellingPlanGroups,\n sellingPlanGroupsConnection: product.sellingPlanGroups,\n }),\n [\n product,\n isOptionInStock,\n options,\n selectedOptions,\n selectedSellingPlan,\n selectedSellingPlanAllocation,\n selectedVariant,\n sellingPlanGroups,\n setSelectedOption,\n variants,\n ],\n );\n\n return (\n <ProductOptionsContext.Provider value={value}>\n {children}\n </ProductOptionsContext.Provider>\n );\n}\n\n/**\n * Provides access to the context value provided by `<ProductProvider />`. Must be a descendent of `<ProductProvider />`.\n */\nexport function useProduct(): ProductHookValue {\n const context = useContext(ProductOptionsContext);\n\n if (!context) {\n throw new Error(`'useProduct' must be a child of <ProductProvider />`);\n }\n\n return context;\n}\n\nfunction getSelectedVariant(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n choices: SelectedOptions,\n): PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined {\n /**\n * Ensure the user has selected all the required options, not just some.\n */\n if (\n !variants.length ||\n variants?.[0]?.selectedOptions?.length !== Object.keys(choices).length\n ) {\n return;\n }\n\n return variants?.find((variant) => {\n return Object.entries(choices).every(([name, value]) => {\n return variant?.selectedOptions?.some(\n (option) => option?.name === name && option?.value === value,\n );\n });\n });\n}\n\nfunction getOptions(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n): OptionWithValues[] {\n const map = variants.reduce((memo, variant) => {\n if (!variant.selectedOptions) {\n throw new Error(`'getOptions' requires 'variant.selectedOptions'`);\n }\n variant?.selectedOptions?.forEach((opt) => {\n memo[opt?.name ?? ''] = memo[opt?.name ?? ''] || new Set();\n memo[opt?.name ?? ''].add(opt?.value ?? '');\n });\n\n return memo;\n }, {} as Record<string, Set<string>>);\n\n return Object.keys(map).map((option) => {\n return {\n name: option,\n values: Array.from(map[option]),\n };\n });\n}\n\nfunction getVariantBasedOnIdProp(\n explicitVariantId: InitialVariantId | undefined,\n variants: Array<\n PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined\n >,\n):\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null {\n // get the initial variant based on the logic outlined in the comments for 'initialVariantId' above\n // * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n if (explicitVariantId) {\n const foundVariant = variants.find(\n (variant) => variant?.id === explicitVariantId,\n );\n if (!foundVariant) {\n console.warn(\n `<ProductProvider/> received a 'initialVariantId' prop, but could not actually find a variant with that ID`,\n );\n }\n return foundVariant;\n }\n // * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n if (explicitVariantId === null) {\n return null;\n }\n // * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n // * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n if (explicitVariantId === undefined) {\n return variants.find((variant) => variant?.availableForSale) || variants[0];\n }\n}\n\nfunction getSelectedOptions(\n selectedVariant:\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null,\n): SelectedOptions {\n return selectedVariant?.selectedOptions\n ? selectedVariant.selectedOptions.reduce<SelectedOptions>(\n (memo, optionSet) => {\n memo[optionSet?.name ?? ''] = optionSet?.value ?? '';\n return memo;\n },\n {},\n )\n : {};\n}\n\nfunction isProductVariantArray(\n maybeVariantArray:\n | (PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined)[]\n | undefined,\n): maybeVariantArray is PartialDeep<\n ProductVariantType,\n {recurseIntoArrays: true}\n>[] {\n if (!maybeVariantArray || !Array.isArray(maybeVariantArray)) {\n return false;\n }\n\n return true;\n}\n\nexport interface OptionWithValues {\n name: SelectedOptionType['name'];\n values: SelectedOptionType['value'][];\n}\n\ntype UseProductObjects = {\n /** The raw product from the Storefront API */\n product: Product;\n /** An array of the variant `nodes` from the `VariantConnection`. */\n variants: ProductVariantType[];\n variantsConnection?: ProductVariantConnection;\n /** An array of the product's options and values. */\n options: OptionWithValues[];\n /** The selected variant. */\n selectedVariant?: ProductVariantType | null;\n selectedOptions: SelectedOptions;\n /** The selected selling plan. */\n selectedSellingPlan?: SellingPlanType;\n /** The selected selling plan allocation. */\n selectedSellingPlanAllocation?: SellingPlanAllocationType;\n /** The selling plan groups. */\n sellingPlanGroups?: (Omit<SellingPlanGroupType, 'sellingPlans'> & {\n sellingPlans: SellingPlanType[];\n })[];\n sellingPlanGroupsConnection?: SellingPlanGroupConnection;\n};\n\ntype UseProductFunctions = {\n /** A callback to set the selected variant to the variant passed as an argument. */\n setSelectedVariant: (\n variant: PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | null,\n ) => void;\n /** A callback to set the selected option. */\n setSelectedOption: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => void;\n /** A callback to set multiple selected options at once. */\n setSelectedOptions: (options: SelectedOptions) => void;\n /** A callback to set the selected selling plan to the one passed as an argument. */\n setSelectedSellingPlan: (\n sellingPlan: PartialDeep<SellingPlanType, {recurseIntoArrays: true}>,\n ) => void;\n /** A callback that returns a boolean indicating if the option is in stock. */\n isOptionInStock: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => boolean;\n};\n\ntype ProductHookValue = PartialDeep<\n UseProductObjects,\n {recurseIntoArrays: true}\n> &\n UseProductFunctions;\n\nexport type SelectedOptions = {\n [key: string]: string;\n};\n"],"names":["value","selectedOptions","_a"],"mappings":";;;AAuBA,MAAM,wBAAwB,cAAuC,IAAI;AAyBlE,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,MAAM;AAAA,EACN,kBAAkB;AACpB,GAAsC;AAEpC,QAAM,WAAW;AAAA,IACf,MAAM,kBAAkB,QAAQ,YAAY,EAAE;AAAA,IAC9C,CAAC,QAAQ,QAAQ;AAAA,EAAA;AAGf,MAAA,CAAC,sBAAsB,QAAQ,GAAG;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAAA,EAEJ;AAGM,QAAA,UAAU,QAAQ,MAAM,WAAW,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAKxD,QAAA,CAAC,iBAAiB,kBAAkB,IAAI,SAI5C,MAAM,wBAAwB,mBAAmB,QAAQ,CAAC;AAMtD,QAAA,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAA0B,MACtE,mBAAmB,eAAe;AAAA,EAAA;AASpC,YAAU,MAAM;AACd,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA;AAAA,IAAA;AAEF,uBAAmB,kBAAkB;AAClB,uBAAA,mBAAmB,kBAAkB,CAAC;AAAA,EAAA,GACxD,CAAC,mBAAmB,QAAQ,CAAC;AAKhC,QAAM,oBAAoB;AAAA,IACxB,CAAC,MAAcA,WAAkB;AAC/B,yBAAmB,CAACC,qBAAoB;AACtC,cAAM,OAAO,EAAC,GAAGA,kBAAiB,CAAC,IAAI,GAAGD,OAAK;AAC5B,2BAAA,mBAAmB,UAAU,IAAI,CAAC;AAC9C,eAAA;AAAA,MAAA,CACR;AAAA,IACH;AAAA,IACA,CAAC,oBAAoB,QAAQ;AAAA,EAAA;AAG/B,QAAM,kBAAkB;AAAA,IACtB,CAAC,QAAgBA,WAAkB;AAC3B,YAAA,kBAAkB,mBAAmB,UAAU;AAAA,QACnD,GAAG;AAAA,QACH,GAAG,EAAC,CAAC,MAAM,GAAGA,OAAK;AAAA,MAAA,CACpB;AAED,cAAO,mDAAiB,qBAAoB;AAAA,IAC9C;AAAA,IACA,CAAC,iBAAiB,QAAQ;AAAA,EAAA;AAG5B,QAAM,oBAAoB;AAAA,IACxB,MACE,kBAAkB,QAAQ,qBAAqB,CAAA,CAAE,EAAE;AAAA,MACjD,CAAC,sBAAsB;AAAA,QACrB,GAAG;AAAA,QACH,cAAc,mBAAkB,qDAAkB,iBAAgB,CAAA,CAAE;AAAA,MAAA;AAAA,IAExE;AAAA,IACF,CAAC,QAAQ,iBAAiB;AAAA,EAAA;AAQ5B,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAEpD,MAAS;AAEL,QAAA,gCAAgC,QAEpC,MAAM;;AACF,QAAA,CAAC,mBAAmB,CAAC,qBAAqB;AAC5C;AAAA,IACF;AAEA,QACE,GAAC,qBAAgB,2BAAhB,mBAAwC,UACzC,GAAC,qBAAgB,2BAAhB,mBAAwC,QACzC;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MAAA;AAAA,IAEJ;AAEO,WAAA,kBAAkB,gBAAgB,sBAAsB,EAAE;AAAA,MAC/D,CAAC,eAAA;;AAAe,iBAAAE,MAAA,yCAAY,gBAAZ,gBAAAA,IAAyB,QAAO,oBAAoB;AAAA;AAAA,IAAA;AAAA,EACtE,GACC,CAAC,iBAAiB,mBAAmB,CAAC;AAEzC,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,oBAAoB,QAAQ;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,6BAA6B,QAAQ;AAAA,IAAA;AAAA,IAEvC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EAAA;AAGF,SACG,oBAAA,sBAAsB,UAAtB,EAA+B,OAC7B,SACH,CAAA;AAEJ;AAKO,SAAS,aAA+B;AACvC,QAAA,UAAU,WAAW,qBAAqB;AAEhD,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEO,SAAA;AACT;AAEA,SAAS,mBACP,UACA,SACwE;;AAIxE,MACE,CAAC,SAAS,YACV,gDAAW,OAAX,mBAAe,oBAAf,mBAAgC,YAAW,OAAO,KAAK,OAAO,EAAE,QAChE;AACA;AAAA,EACF;AAEO,SAAA,qCAAU,KAAK,CAAC,YAAY;AAC1B,WAAA,OAAO,QAAQ,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,KAAK,MAAM;;AACtD,cAAOA,MAAA,mCAAS,oBAAT,gBAAAA,IAA0B;AAAA,QAC/B,CAAC,YAAW,iCAAQ,UAAS,SAAQ,iCAAQ,WAAU;AAAA;AAAA,IACzD,CACD;AAAA,EAAA;AAEL;AAEA,SAAS,WACP,UACoB;AACpB,QAAM,MAAM,SAAS,OAAO,CAAC,MAAM,YAAY;;AACzC,QAAA,CAAC,QAAQ,iBAAiB;AACtB,YAAA,IAAI,MAAM,iDAAiD;AAAA,IACnE;AACS,6CAAA,oBAAA,mBAAiB,QAAQ,CAAC,QAAQ;AACpC,YAAA,2BAAK,SAAQ,EAAE,IAAI,MAAK,2BAAK,SAAQ,EAAE,KAAK,oBAAI,IAAI;AACzD,YAAK,2BAAK,SAAQ,EAAE,EAAE,KAAI,2BAAK,UAAS,EAAE;AAAA,IAAA;AAGrC,WAAA;AAAA,EACT,GAAG,CAAiC,CAAA;AAEpC,SAAO,OAAO,KAAK,GAAG,EAAE,IAAI,CAAC,WAAW;AAC/B,WAAA;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,MAAM,KAAK,IAAI,MAAM,CAAC;AAAA,IAAA;AAAA,EAChC,CACD;AACH;AAEA,SAAS,wBACP,mBACA,UAMO;AAGP,MAAI,mBAAmB;AACrB,UAAM,eAAe,SAAS;AAAA,MAC5B,CAAC,aAAY,mCAAS,QAAO;AAAA,IAAA;AAE/B,QAAI,CAAC,cAAc;AACT,cAAA;AAAA,QACN;AAAA,MAAA;AAAA,IAEJ;AACO,WAAA;AAAA,EACT;AAEA,MAAI,sBAAsB,MAAM;AACvB,WAAA;AAAA,EACT;AAGA,MAAI,sBAAsB,QAAW;AAC5B,WAAA,SAAS,KAAK,CAAC,YAAY,mCAAS,gBAAgB,KAAK,SAAS,CAAC;AAAA,EAC5E;AACF;AAEA,SAAS,mBACP,iBAIiB;AACV,UAAA,mDAAiB,mBACpB,gBAAgB,gBAAgB;AAAA,IAC9B,CAAC,MAAM,cAAc;AACnB,YAAK,uCAAW,SAAQ,EAAE,KAAI,uCAAW,UAAS;AAC3C,aAAA;AAAA,IACT;AAAA,IACA,CAAC;AAAA,MAEH;AACN;AAEA,SAAS,sBACP,mBAME;AACF,MAAI,CAAC,qBAAqB,CAAC,MAAM,QAAQ,iBAAiB,GAAG;AACpD,WAAA;AAAA,EACT;AAEO,SAAA;AACT;"}
1
+ {"version":3,"file":"ProductProvider.mjs","sources":["../../src/ProductProvider.tsx"],"sourcesContent":["import {\n useMemo,\n useState,\n useEffect,\n useCallback,\n createContext,\n useContext,\n} from 'react';\nimport type {\n SelectedOption as SelectedOptionType,\n SellingPlan,\n SellingPlanAllocation,\n Product,\n ProductVariant as ProductVariantType,\n ProductVariantConnection,\n SellingPlan as SellingPlanType,\n SellingPlanAllocation as SellingPlanAllocationType,\n SellingPlanGroup as SellingPlanGroupType,\n SellingPlanGroupConnection,\n} from './storefront-api-types.js';\nimport type {PartialDeep} from 'type-fest';\nimport {flattenConnection} from './flatten-connection.js';\n\nconst ProductOptionsContext = createContext<ProductHookValue | null>(null);\n\ntype InitialVariantId = ProductVariantType['id'] | null;\n\ninterface ProductProviderProps {\n /** A Storefront API [Product object](https://shopify.dev/api/storefront/reference/products/product). */\n data: PartialDeep<Product, {recurseIntoArrays: true}>;\n /** A `ReactNode` element. */\n children: React.ReactNode;\n /**\n * The initially selected variant.\n * The following logic applies to `initialVariantId`:\n * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n */\n initialVariantId?: InitialVariantId;\n}\n\n/**\n * `<ProductProvider />` is a context provider that enables use of the `useProduct()` hook.\n *\n * It helps manage selected options and variants for a product.\n */\nexport function ProductProvider({\n children,\n data: product,\n initialVariantId: explicitVariantId,\n}: ProductProviderProps): JSX.Element {\n // The flattened variants\n const variants = useMemo(\n () => flattenConnection(product.variants ?? {}),\n [product.variants],\n );\n\n if (!isProductVariantArray(variants)) {\n throw new Error(\n `<ProductProvider/> requires 'product.variants.nodes' or 'product.variants.edges'`,\n );\n }\n\n // All the options available for a product, based on all the variants\n const options = useMemo(() => getOptions(variants), [variants]);\n\n /**\n * Track the selectedVariant within the provider.\n */\n const [selectedVariant, setSelectedVariant] = useState<\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null\n >(() => getVariantBasedOnIdProp(explicitVariantId, variants));\n\n /**\n * Track the selectedOptions within the provider. If a `initialVariantId`\n * is passed, use that to select initial options.\n */\n const [selectedOptions, setSelectedOptions] = useState<SelectedOptions>(() =>\n getSelectedOptions(selectedVariant),\n );\n\n /**\n * When the initialVariantId changes, we need to make sure we\n * update the selected variant and selected options. If not,\n * then the selected variant and options will reference incorrect\n * values.\n */\n useEffect(() => {\n const newSelectedVariant = getVariantBasedOnIdProp(\n explicitVariantId,\n variants,\n );\n setSelectedVariant(newSelectedVariant);\n setSelectedOptions(getSelectedOptions(newSelectedVariant));\n }, [explicitVariantId, variants]);\n\n /**\n * Allow the developer to select an option.\n */\n const setSelectedOption = useCallback(\n (name: string, value: string) => {\n setSelectedOptions((selectedOptions) => {\n const opts = {...selectedOptions, [name]: value};\n setSelectedVariant(getSelectedVariant(variants, opts));\n return opts;\n });\n },\n [setSelectedOptions, variants],\n );\n\n const isOptionInStock = useCallback(\n (option: string, value: string) => {\n const proposedVariant = getSelectedVariant(variants, {\n ...selectedOptions,\n ...{[option]: value},\n });\n\n return proposedVariant?.availableForSale ?? true;\n },\n [selectedOptions, variants],\n );\n\n const sellingPlanGroups = useMemo(\n () =>\n flattenConnection(product.sellingPlanGroups ?? {}).map(\n (sellingPlanGroup) => ({\n ...sellingPlanGroup,\n sellingPlans: flattenConnection(sellingPlanGroup?.sellingPlans ?? {}),\n }),\n ),\n [product.sellingPlanGroups],\n );\n\n /**\n * Track the selectedSellingPlan within the hook. If `initialSellingPlanId`\n * is passed, use that as an initial value. Look it up from the `selectedVariant`, since\n * that is also a requirement.\n */\n const [selectedSellingPlan, setSelectedSellingPlan] = useState<\n PartialDeep<SellingPlan, {recurseIntoArrays: true}> | undefined\n >(undefined);\n\n const selectedSellingPlanAllocation = useMemo<\n PartialDeep<SellingPlanAllocation, {recurseIntoArrays: true}> | undefined\n >(() => {\n if (!selectedVariant || !selectedSellingPlan) {\n return;\n }\n\n if (\n !selectedVariant.sellingPlanAllocations?.nodes &&\n !selectedVariant.sellingPlanAllocations?.edges\n ) {\n throw new Error(\n `<ProductProvider/>: You must include 'sellingPlanAllocations.nodes' or 'sellingPlanAllocations.edges' in your variants in order to calculate selectedSellingPlanAllocation`,\n );\n }\n\n return flattenConnection(selectedVariant.sellingPlanAllocations).find(\n (allocation) => allocation?.sellingPlan?.id === selectedSellingPlan.id,\n );\n }, [selectedVariant, selectedSellingPlan]);\n\n const value = useMemo<ProductHookValue>(\n () => ({\n product,\n variants,\n variantsConnection: product.variants,\n options,\n selectedVariant,\n setSelectedVariant,\n selectedOptions,\n setSelectedOption,\n setSelectedOptions,\n isOptionInStock,\n selectedSellingPlan,\n setSelectedSellingPlan,\n selectedSellingPlanAllocation,\n sellingPlanGroups,\n sellingPlanGroupsConnection: product.sellingPlanGroups,\n }),\n [\n product,\n isOptionInStock,\n options,\n selectedOptions,\n selectedSellingPlan,\n selectedSellingPlanAllocation,\n selectedVariant,\n sellingPlanGroups,\n setSelectedOption,\n variants,\n ],\n );\n\n return (\n <ProductOptionsContext.Provider value={value}>\n {children}\n </ProductOptionsContext.Provider>\n );\n}\n\n/**\n * Provides access to the context value provided by `<ProductProvider />`. Must be a descendent of `<ProductProvider />`.\n */\nexport function useProduct(): ProductHookValue {\n const context = useContext(ProductOptionsContext);\n\n if (!context) {\n throw new Error(`'useProduct' must be a child of <ProductProvider />`);\n }\n\n return context;\n}\n\nfunction getSelectedVariant(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n choices: SelectedOptions,\n): PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined {\n /**\n * Ensure the user has selected all the required options, not just some.\n */\n if (\n !variants.length ||\n variants?.[0]?.selectedOptions?.length !== Object.keys(choices).length\n ) {\n return;\n }\n\n return variants?.find((variant) => {\n return Object.entries(choices).every(([name, value]) => {\n return variant?.selectedOptions?.some(\n (option) => option?.name === name && option?.value === value,\n );\n });\n });\n}\n\nfunction getOptions(\n variants: PartialDeep<ProductVariantType, {recurseIntoArrays: true}>[],\n): OptionWithValues[] {\n const map = variants.reduce(\n (memo, variant) => {\n if (!variant.selectedOptions) {\n throw new Error(`'getOptions' requires 'variant.selectedOptions'`);\n }\n variant?.selectedOptions?.forEach((opt) => {\n memo[opt?.name ?? ''] = memo[opt?.name ?? ''] || new Set();\n memo[opt?.name ?? ''].add(opt?.value ?? '');\n });\n\n return memo;\n },\n {} as Record<string, Set<string>>,\n );\n\n return Object.keys(map).map((option) => {\n return {\n name: option,\n values: Array.from(map[option]),\n };\n });\n}\n\nfunction getVariantBasedOnIdProp(\n explicitVariantId: InitialVariantId | undefined,\n variants: Array<\n PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined\n >,\n):\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null {\n // get the initial variant based on the logic outlined in the comments for 'initialVariantId' above\n // * 1. If `initialVariantId` is provided, then it's used even if it's out of stock.\n if (explicitVariantId) {\n const foundVariant = variants.find(\n (variant) => variant?.id === explicitVariantId,\n );\n if (!foundVariant) {\n console.warn(\n `<ProductProvider/> received a 'initialVariantId' prop, but could not actually find a variant with that ID`,\n );\n }\n return foundVariant;\n }\n // * 2. If `initialVariantId` is provided but is `null`, then no variant is used.\n if (explicitVariantId === null) {\n return null;\n }\n // * 3. If nothing is passed to `initialVariantId` then the first available / in-stock variant is used.\n // * 4. If nothing is passed to `initialVariantId` and no variants are in stock, then the first variant is used.\n if (explicitVariantId === undefined) {\n return variants.find((variant) => variant?.availableForSale) || variants[0];\n }\n}\n\nfunction getSelectedOptions(\n selectedVariant:\n | PartialDeep<ProductVariantType, {recurseIntoArrays: true}>\n | undefined\n | null,\n): SelectedOptions {\n return selectedVariant?.selectedOptions\n ? selectedVariant.selectedOptions.reduce<SelectedOptions>(\n (memo, optionSet) => {\n memo[optionSet?.name ?? ''] = optionSet?.value ?? '';\n return memo;\n },\n {},\n )\n : {};\n}\n\nfunction isProductVariantArray(\n maybeVariantArray:\n | (PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | undefined)[]\n | undefined,\n): maybeVariantArray is PartialDeep<\n ProductVariantType,\n {recurseIntoArrays: true}\n>[] {\n if (!maybeVariantArray || !Array.isArray(maybeVariantArray)) {\n return false;\n }\n\n return true;\n}\n\nexport interface OptionWithValues {\n name: SelectedOptionType['name'];\n values: SelectedOptionType['value'][];\n}\n\ntype UseProductObjects = {\n /** The raw product from the Storefront API */\n product: Product;\n /** An array of the variant `nodes` from the `VariantConnection`. */\n variants: ProductVariantType[];\n variantsConnection?: ProductVariantConnection;\n /** An array of the product's options and values. */\n options: OptionWithValues[];\n /** The selected variant. */\n selectedVariant?: ProductVariantType | null;\n selectedOptions: SelectedOptions;\n /** The selected selling plan. */\n selectedSellingPlan?: SellingPlanType;\n /** The selected selling plan allocation. */\n selectedSellingPlanAllocation?: SellingPlanAllocationType;\n /** The selling plan groups. */\n sellingPlanGroups?: (Omit<SellingPlanGroupType, 'sellingPlans'> & {\n sellingPlans: SellingPlanType[];\n })[];\n sellingPlanGroupsConnection?: SellingPlanGroupConnection;\n};\n\ntype UseProductFunctions = {\n /** A callback to set the selected variant to the variant passed as an argument. */\n setSelectedVariant: (\n variant: PartialDeep<ProductVariantType, {recurseIntoArrays: true}> | null,\n ) => void;\n /** A callback to set the selected option. */\n setSelectedOption: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => void;\n /** A callback to set multiple selected options at once. */\n setSelectedOptions: (options: SelectedOptions) => void;\n /** A callback to set the selected selling plan to the one passed as an argument. */\n setSelectedSellingPlan: (\n sellingPlan: PartialDeep<SellingPlanType, {recurseIntoArrays: true}>,\n ) => void;\n /** A callback that returns a boolean indicating if the option is in stock. */\n isOptionInStock: (\n name: SelectedOptionType['name'],\n value: SelectedOptionType['value'],\n ) => boolean;\n};\n\ntype ProductHookValue = PartialDeep<\n UseProductObjects,\n {recurseIntoArrays: true}\n> &\n UseProductFunctions;\n\nexport type SelectedOptions = {\n [key: string]: string;\n};\n"],"names":["value","selectedOptions","_a"],"mappings":";;;AAuBA,MAAM,wBAAwB,cAAuC,IAAI;AAyBlE,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA,MAAM;AAAA,EACN,kBAAkB;AACpB,GAAsC;AAEpC,QAAM,WAAW;AAAA,IACf,MAAM,kBAAkB,QAAQ,YAAY,EAAE;AAAA,IAC9C,CAAC,QAAQ,QAAQ;AAAA,EACnB;AAEI,MAAA,CAAC,sBAAsB,QAAQ,GAAG;AACpC,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EAAA;AAII,QAAA,UAAU,QAAQ,MAAM,WAAW,QAAQ,GAAG,CAAC,QAAQ,CAAC;AAKxD,QAAA,CAAC,iBAAiB,kBAAkB,IAAI,SAI5C,MAAM,wBAAwB,mBAAmB,QAAQ,CAAC;AAMtD,QAAA,CAAC,iBAAiB,kBAAkB,IAAI;AAAA,IAA0B,MACtE,mBAAmB,eAAe;AAAA,EACpC;AAQA,YAAU,MAAM;AACd,UAAM,qBAAqB;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AACA,uBAAmB,kBAAkB;AAClB,uBAAA,mBAAmB,kBAAkB,CAAC;AAAA,EAAA,GACxD,CAAC,mBAAmB,QAAQ,CAAC;AAKhC,QAAM,oBAAoB;AAAA,IACxB,CAAC,MAAcA,WAAkB;AAC/B,yBAAmB,CAACC,qBAAoB;AACtC,cAAM,OAAO,EAAC,GAAGA,kBAAiB,CAAC,IAAI,GAAGD,OAAK;AAC5B,2BAAA,mBAAmB,UAAU,IAAI,CAAC;AAC9C,eAAA;AAAA,MAAA,CACR;AAAA,IACH;AAAA,IACA,CAAC,oBAAoB,QAAQ;AAAA,EAC/B;AAEA,QAAM,kBAAkB;AAAA,IACtB,CAAC,QAAgBA,WAAkB;AAC3B,YAAA,kBAAkB,mBAAmB,UAAU;AAAA,QACnD,GAAG;AAAA,QACH,GAAG,EAAC,CAAC,MAAM,GAAGA,OAAK;AAAA,MAAA,CACpB;AAED,cAAO,mDAAiB,qBAAoB;AAAA,IAC9C;AAAA,IACA,CAAC,iBAAiB,QAAQ;AAAA,EAC5B;AAEA,QAAM,oBAAoB;AAAA,IACxB,MACE,kBAAkB,QAAQ,qBAAqB,CAAA,CAAE,EAAE;AAAA,MACjD,CAAC,sBAAsB;AAAA,QACrB,GAAG;AAAA,QACH,cAAc,mBAAkB,qDAAkB,iBAAgB,CAAE,CAAA;AAAA,MACtE;AAAA,IACF;AAAA,IACF,CAAC,QAAQ,iBAAiB;AAAA,EAC5B;AAOA,QAAM,CAAC,qBAAqB,sBAAsB,IAAI,SAEpD,MAAS;AAEL,QAAA,gCAAgC,QAEpC,MAAM;;AACF,QAAA,CAAC,mBAAmB,CAAC,qBAAqB;AAC5C;AAAA,IAAA;AAGF,QACE,GAAC,qBAAgB,2BAAhB,mBAAwC,UACzC,GAAC,qBAAgB,2BAAhB,mBAAwC,QACzC;AACA,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IAAA;AAGK,WAAA,kBAAkB,gBAAgB,sBAAsB,EAAE;AAAA,MAC/D,CAAC,eAAA;;AAAe,iBAAAE,MAAA,yCAAY,gBAAZ,gBAAAA,IAAyB,QAAO,oBAAoB;AAAA;AAAA,IACtE;AAAA,EAAA,GACC,CAAC,iBAAiB,mBAAmB,CAAC;AAEzC,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA,oBAAoB,QAAQ;AAAA,MAC5B;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,6BAA6B,QAAQ;AAAA,IAAA;AAAA,IAEvC;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,SACG,oBAAA,sBAAsB,UAAtB,EAA+B,OAC7B,SACH,CAAA;AAEJ;AAKO,SAAS,aAA+B;AACvC,QAAA,UAAU,WAAW,qBAAqB;AAEhD,MAAI,CAAC,SAAS;AACN,UAAA,IAAI,MAAM,qDAAqD;AAAA,EAAA;AAGhE,SAAA;AACT;AAEA,SAAS,mBACP,UACA,SACwE;;AAIxE,MACE,CAAC,SAAS,YACV,gDAAW,OAAX,mBAAe,oBAAf,mBAAgC,YAAW,OAAO,KAAK,OAAO,EAAE,QAChE;AACA;AAAA,EAAA;AAGK,SAAA,qCAAU,KAAK,CAAC,YAAY;AAC1B,WAAA,OAAO,QAAQ,OAAO,EAAE,MAAM,CAAC,CAAC,MAAM,KAAK,MAAM;;AACtD,cAAOA,MAAA,mCAAS,oBAAT,gBAAAA,IAA0B;AAAA,QAC/B,CAAC,YAAW,iCAAQ,UAAS,SAAQ,iCAAQ,WAAU;AAAA;AAAA,IACzD,CACD;AAAA,EAAA;AAEL;AAEA,SAAS,WACP,UACoB;AACpB,QAAM,MAAM,SAAS;AAAA,IACnB,CAAC,MAAM,YAAY;;AACb,UAAA,CAAC,QAAQ,iBAAiB;AACtB,cAAA,IAAI,MAAM,iDAAiD;AAAA,MAAA;AAE1D,+CAAA,oBAAA,mBAAiB,QAAQ,CAAC,QAAQ;AACpC,cAAA,2BAAK,SAAQ,EAAE,IAAI,MAAK,2BAAK,SAAQ,EAAE,KAAK,oBAAI,IAAI;AACzD,cAAK,2BAAK,SAAQ,EAAE,EAAE,KAAI,2BAAK,UAAS,EAAE;AAAA,MAAA;AAGrC,aAAA;AAAA,IACT;AAAA,IACA,CAAA;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,GAAG,EAAE,IAAI,CAAC,WAAW;AAC/B,WAAA;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,MAAM,KAAK,IAAI,MAAM,CAAC;AAAA,IAChC;AAAA,EAAA,CACD;AACH;AAEA,SAAS,wBACP,mBACA,UAMO;AAGP,MAAI,mBAAmB;AACrB,UAAM,eAAe,SAAS;AAAA,MAC5B,CAAC,aAAY,mCAAS,QAAO;AAAA,IAC/B;AACA,QAAI,CAAC,cAAc;AACT,cAAA;AAAA,QACN;AAAA,MACF;AAAA,IAAA;AAEK,WAAA;AAAA,EAAA;AAGT,MAAI,sBAAsB,MAAM;AACvB,WAAA;AAAA,EAAA;AAIT,MAAI,sBAAsB,QAAW;AAC5B,WAAA,SAAS,KAAK,CAAC,YAAY,mCAAS,gBAAgB,KAAK,SAAS,CAAC;AAAA,EAAA;AAE9E;AAEA,SAAS,mBACP,iBAIiB;AACV,UAAA,mDAAiB,mBACpB,gBAAgB,gBAAgB;AAAA,IAC9B,CAAC,MAAM,cAAc;AACnB,YAAK,uCAAW,SAAQ,EAAE,KAAI,uCAAW,UAAS;AAC3C,aAAA;AAAA,IACT;AAAA,IACA,CAAA;AAAA,EAAC,IAEH,CAAC;AACP;AAEA,SAAS,sBACP,mBAME;AACF,MAAI,CAAC,qBAAqB,CAAC,MAAM,QAAQ,iBAAiB,GAAG;AACpD,WAAA;AAAA,EAAA;AAGF,SAAA;AACT;"}
@@ -31,10 +31,8 @@ function Text({
31
31
  }) {
32
32
  if (node.bold && node.italic)
33
33
  return /* @__PURE__ */ jsxRuntime.jsx("em", { children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: node.value }) });
34
- if (node.bold)
35
- return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: node.value });
36
- if (node.italic)
37
- return /* @__PURE__ */ jsxRuntime.jsx("em", { children: node.value });
34
+ if (node.bold) return /* @__PURE__ */ jsxRuntime.jsx("strong", { children: node.value });
35
+ if (node.italic) return /* @__PURE__ */ jsxRuntime.jsx("em", { children: node.value });
38
36
  return node.value;
39
37
  }
40
38
  function RichTextLink({
@@ -1 +1 @@
1
- {"version":3,"file":"RichText.components.js","sources":["../../src/RichText.components.tsx"],"sourcesContent":["import {createElement, type ReactNode} from 'react';\n\nexport type CustomComponents = {\n /** The root node of the rich text. Defaults to `<div>` */\n root?: typeof Root;\n /** Customize the headings. Each heading has a `level` property from 1-6. Defaults to `<h1>` to `<h6>` */\n heading?: typeof Heading;\n /** Customize paragraphs. Defaults to `<p>` */\n paragraph?: typeof Paragraph;\n /** Customize how text nodes. They can either be bold or italic. Defaults to `<em>`, `<strong>` or text. */\n text?: typeof Text;\n /** Customize links. Defaults to a React Router `<Link>` component in Hydrogen and a `<a>` in Hydrogen React. */\n link?: typeof RichTextLink;\n /** Customize lists. They can be either ordered or unordered. Defaults to `<ol>` or `<ul>` */\n list?: typeof List;\n /** Customize list items. Defaults to `<li>`. */\n listItem?: typeof ListItem;\n};\n\nexport const RichTextComponents = {\n root: Root,\n heading: Heading,\n paragraph: Paragraph,\n text: Text,\n link: RichTextLink,\n list: List,\n 'list-item': ListItem,\n};\n\nfunction Root({\n node,\n}: {\n node: {\n type: 'root';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <div>{node.children}</div>;\n}\n\nfunction Heading({\n node,\n}: {\n node: {\n type: 'heading';\n level: number;\n children?: ReactNode[];\n };\n}): ReactNode {\n return createElement(`h${node.level ?? '1'}`, null, node.children);\n}\n\nfunction Paragraph({\n node,\n}: {\n node: {\n type: 'paragraph';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <p>{node.children}</p>;\n}\n\nfunction Text({\n node,\n}: {\n node: {\n type: 'text';\n italic?: boolean;\n bold?: boolean;\n value?: string;\n };\n}): ReactNode {\n if (node.bold && node.italic)\n return (\n <em>\n <strong>{node.value}</strong>\n </em>\n );\n\n if (node.bold) return <strong>{node.value}</strong>;\n if (node.italic) return <em>{node.value}</em>;\n\n return node.value;\n}\n\nfunction RichTextLink({\n node,\n}: {\n node: {\n type: 'link';\n url: string;\n title?: string;\n target?: string;\n children?: ReactNode[];\n };\n}): ReactNode {\n return (\n <a href={node.url} title={node.title} target={node.target}>\n {node.children}\n </a>\n );\n}\n\nfunction List({\n node,\n}: {\n node: {\n type: 'list';\n listType: 'unordered' | 'ordered';\n children?: ReactNode[];\n };\n}): ReactNode {\n const List = node.listType === 'unordered' ? 'ul' : 'ol';\n return <List>{node.children}</List>;\n}\n\nfunction ListItem({\n node,\n}: {\n node: {\n type: 'list-item';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <li>{node.children}</li>;\n}\n"],"names":["jsx","createElement","List"],"mappings":";;;;AAmBO,MAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AACf;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAKc;AACL,SAAAA,2BAAAA,IAAC,OAAK,EAAA,UAAA,KAAK,SAAS,CAAA;AAC7B;AAEA,SAAS,QAAQ;AAAA,EACf;AACF,GAMc;AACL,SAAAC,oBAAc,IAAI,KAAK,SAAS,GAAG,IAAI,MAAM,KAAK,QAAQ;AACnE;AAEA,SAAS,UAAU;AAAA,EACjB;AACF,GAKc;AACL,SAAAD,2BAAAA,IAAC,KAAG,EAAA,UAAA,KAAK,SAAS,CAAA;AAC3B;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAOc;AACR,MAAA,KAAK,QAAQ,KAAK;AACpB,0CACG,MACC,EAAA,UAAAA,2BAAAA,IAAC,UAAQ,EAAA,UAAA,KAAK,MAAM,CAAA,EACtB,CAAA;AAGJ,MAAI,KAAK;AAAa,WAAAA,2BAAAA,IAAC,UAAQ,EAAA,UAAA,KAAK,MAAM,CAAA;AAC1C,MAAI,KAAK;AAAe,WAAAA,2BAAAA,IAAC,MAAI,EAAA,UAAA,KAAK,MAAM,CAAA;AAExC,SAAO,KAAK;AACd;AAEA,SAAS,aAAa;AAAA,EACpB;AACF,GAQc;AACZ,SACGA,2BAAAA,IAAA,KAAA,EAAE,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,QAChD,UAAA,KAAK,SACR,CAAA;AAEJ;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAMc;AACZ,QAAME,QAAO,KAAK,aAAa,cAAc,OAAO;AACpD,SAAQA,2BAAAA,IAAAA,OAAA,EAAM,UAAA,KAAK,SAAS,CAAA;AAC9B;AAEA,SAAS,SAAS;AAAA,EAChB;AACF,GAKc;AACL,SAAAF,2BAAAA,IAAC,MAAI,EAAA,UAAA,KAAK,SAAS,CAAA;AAC5B;;"}
1
+ {"version":3,"file":"RichText.components.js","sources":["../../src/RichText.components.tsx"],"sourcesContent":["import {createElement, type ReactNode} from 'react';\n\nexport type CustomComponents = {\n /** The root node of the rich text. Defaults to `<div>` */\n root?: typeof Root;\n /** Customize the headings. Each heading has a `level` property from 1-6. Defaults to `<h1>` to `<h6>` */\n heading?: typeof Heading;\n /** Customize paragraphs. Defaults to `<p>` */\n paragraph?: typeof Paragraph;\n /** Customize how text nodes. They can either be bold or italic. Defaults to `<em>`, `<strong>` or text. */\n text?: typeof Text;\n /** Customize links. Defaults to a React Router `<Link>` component in Hydrogen and a `<a>` in Hydrogen React. */\n link?: typeof RichTextLink;\n /** Customize lists. They can be either ordered or unordered. Defaults to `<ol>` or `<ul>` */\n list?: typeof List;\n /** Customize list items. Defaults to `<li>`. */\n listItem?: typeof ListItem;\n};\n\nexport const RichTextComponents = {\n root: Root,\n heading: Heading,\n paragraph: Paragraph,\n text: Text,\n link: RichTextLink,\n list: List,\n 'list-item': ListItem,\n};\n\nfunction Root({\n node,\n}: {\n node: {\n type: 'root';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <div>{node.children}</div>;\n}\n\nfunction Heading({\n node,\n}: {\n node: {\n type: 'heading';\n level: number;\n children?: ReactNode[];\n };\n}): ReactNode {\n return createElement(`h${node.level ?? '1'}`, null, node.children);\n}\n\nfunction Paragraph({\n node,\n}: {\n node: {\n type: 'paragraph';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <p>{node.children}</p>;\n}\n\nfunction Text({\n node,\n}: {\n node: {\n type: 'text';\n italic?: boolean;\n bold?: boolean;\n value?: string;\n };\n}): ReactNode {\n if (node.bold && node.italic)\n return (\n <em>\n <strong>{node.value}</strong>\n </em>\n );\n\n if (node.bold) return <strong>{node.value}</strong>;\n if (node.italic) return <em>{node.value}</em>;\n\n return node.value;\n}\n\nfunction RichTextLink({\n node,\n}: {\n node: {\n type: 'link';\n url: string;\n title?: string;\n target?: string;\n children?: ReactNode[];\n };\n}): ReactNode {\n return (\n <a href={node.url} title={node.title} target={node.target}>\n {node.children}\n </a>\n );\n}\n\nfunction List({\n node,\n}: {\n node: {\n type: 'list';\n listType: 'unordered' | 'ordered';\n children?: ReactNode[];\n };\n}): ReactNode {\n const List = node.listType === 'unordered' ? 'ul' : 'ol';\n return <List>{node.children}</List>;\n}\n\nfunction ListItem({\n node,\n}: {\n node: {\n type: 'list-item';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <li>{node.children}</li>;\n}\n"],"names":["jsx","createElement","List"],"mappings":";;;;AAmBO,MAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AACf;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAKc;AACL,SAAAA,2BAAAA,IAAC,OAAK,EAAA,UAAA,KAAK,SAAS,CAAA;AAC7B;AAEA,SAAS,QAAQ;AAAA,EACf;AACF,GAMc;AACL,SAAAC,MAAA,cAAc,IAAI,KAAK,SAAS,GAAG,IAAI,MAAM,KAAK,QAAQ;AACnE;AAEA,SAAS,UAAU;AAAA,EACjB;AACF,GAKc;AACL,SAAAD,2BAAAA,IAAC,KAAG,EAAA,UAAA,KAAK,SAAS,CAAA;AAC3B;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAOc;AACR,MAAA,KAAK,QAAQ,KAAK;AACpB,0CACG,MACC,EAAA,UAAAA,2BAAA,IAAC,UAAQ,EAAA,UAAA,KAAK,OAAM,EACtB,CAAA;AAGJ,MAAI,KAAK,KAAM,QAAQA,2BAAA,IAAA,UAAA,EAAQ,eAAK,OAAM;AAC1C,MAAI,KAAK,OAAQ,QAAQA,2BAAA,IAAA,MAAA,EAAI,eAAK,OAAM;AAExC,SAAO,KAAK;AACd;AAEA,SAAS,aAAa;AAAA,EACpB;AACF,GAQc;AACZ,SACGA,2BAAAA,IAAA,KAAA,EAAE,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,QAChD,UAAA,KAAK,UACR;AAEJ;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAMc;AACZ,QAAME,QAAO,KAAK,aAAa,cAAc,OAAO;AACpD,SAAQA,2BAAAA,IAAAA,OAAA,EAAM,UAAA,KAAK,SAAS,CAAA;AAC9B;AAEA,SAAS,SAAS;AAAA,EAChB;AACF,GAKc;AACL,SAAAF,2BAAAA,IAAC,MAAI,EAAA,UAAA,KAAK,SAAS,CAAA;AAC5B;;"}
@@ -29,10 +29,8 @@ function Text({
29
29
  }) {
30
30
  if (node.bold && node.italic)
31
31
  return /* @__PURE__ */ jsx("em", { children: /* @__PURE__ */ jsx("strong", { children: node.value }) });
32
- if (node.bold)
33
- return /* @__PURE__ */ jsx("strong", { children: node.value });
34
- if (node.italic)
35
- return /* @__PURE__ */ jsx("em", { children: node.value });
32
+ if (node.bold) return /* @__PURE__ */ jsx("strong", { children: node.value });
33
+ if (node.italic) return /* @__PURE__ */ jsx("em", { children: node.value });
36
34
  return node.value;
37
35
  }
38
36
  function RichTextLink({
@@ -1 +1 @@
1
- {"version":3,"file":"RichText.components.mjs","sources":["../../src/RichText.components.tsx"],"sourcesContent":["import {createElement, type ReactNode} from 'react';\n\nexport type CustomComponents = {\n /** The root node of the rich text. Defaults to `<div>` */\n root?: typeof Root;\n /** Customize the headings. Each heading has a `level` property from 1-6. Defaults to `<h1>` to `<h6>` */\n heading?: typeof Heading;\n /** Customize paragraphs. Defaults to `<p>` */\n paragraph?: typeof Paragraph;\n /** Customize how text nodes. They can either be bold or italic. Defaults to `<em>`, `<strong>` or text. */\n text?: typeof Text;\n /** Customize links. Defaults to a React Router `<Link>` component in Hydrogen and a `<a>` in Hydrogen React. */\n link?: typeof RichTextLink;\n /** Customize lists. They can be either ordered or unordered. Defaults to `<ol>` or `<ul>` */\n list?: typeof List;\n /** Customize list items. Defaults to `<li>`. */\n listItem?: typeof ListItem;\n};\n\nexport const RichTextComponents = {\n root: Root,\n heading: Heading,\n paragraph: Paragraph,\n text: Text,\n link: RichTextLink,\n list: List,\n 'list-item': ListItem,\n};\n\nfunction Root({\n node,\n}: {\n node: {\n type: 'root';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <div>{node.children}</div>;\n}\n\nfunction Heading({\n node,\n}: {\n node: {\n type: 'heading';\n level: number;\n children?: ReactNode[];\n };\n}): ReactNode {\n return createElement(`h${node.level ?? '1'}`, null, node.children);\n}\n\nfunction Paragraph({\n node,\n}: {\n node: {\n type: 'paragraph';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <p>{node.children}</p>;\n}\n\nfunction Text({\n node,\n}: {\n node: {\n type: 'text';\n italic?: boolean;\n bold?: boolean;\n value?: string;\n };\n}): ReactNode {\n if (node.bold && node.italic)\n return (\n <em>\n <strong>{node.value}</strong>\n </em>\n );\n\n if (node.bold) return <strong>{node.value}</strong>;\n if (node.italic) return <em>{node.value}</em>;\n\n return node.value;\n}\n\nfunction RichTextLink({\n node,\n}: {\n node: {\n type: 'link';\n url: string;\n title?: string;\n target?: string;\n children?: ReactNode[];\n };\n}): ReactNode {\n return (\n <a href={node.url} title={node.title} target={node.target}>\n {node.children}\n </a>\n );\n}\n\nfunction List({\n node,\n}: {\n node: {\n type: 'list';\n listType: 'unordered' | 'ordered';\n children?: ReactNode[];\n };\n}): ReactNode {\n const List = node.listType === 'unordered' ? 'ul' : 'ol';\n return <List>{node.children}</List>;\n}\n\nfunction ListItem({\n node,\n}: {\n node: {\n type: 'list-item';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <li>{node.children}</li>;\n}\n"],"names":["List"],"mappings":";;AAmBO,MAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AACf;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAKc;AACL,SAAA,oBAAC,OAAK,EAAA,UAAA,KAAK,SAAS,CAAA;AAC7B;AAEA,SAAS,QAAQ;AAAA,EACf;AACF,GAMc;AACL,SAAA,cAAc,IAAI,KAAK,SAAS,GAAG,IAAI,MAAM,KAAK,QAAQ;AACnE;AAEA,SAAS,UAAU;AAAA,EACjB;AACF,GAKc;AACL,SAAA,oBAAC,KAAG,EAAA,UAAA,KAAK,SAAS,CAAA;AAC3B;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAOc;AACR,MAAA,KAAK,QAAQ,KAAK;AACpB,+BACG,MACC,EAAA,UAAA,oBAAC,UAAQ,EAAA,UAAA,KAAK,MAAM,CAAA,EACtB,CAAA;AAGJ,MAAI,KAAK;AAAa,WAAA,oBAAC,UAAQ,EAAA,UAAA,KAAK,MAAM,CAAA;AAC1C,MAAI,KAAK;AAAe,WAAA,oBAAC,MAAI,EAAA,UAAA,KAAK,MAAM,CAAA;AAExC,SAAO,KAAK;AACd;AAEA,SAAS,aAAa;AAAA,EACpB;AACF,GAQc;AACZ,SACG,oBAAA,KAAA,EAAE,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,QAChD,UAAA,KAAK,SACR,CAAA;AAEJ;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAMc;AACZ,QAAMA,QAAO,KAAK,aAAa,cAAc,OAAO;AACpD,SAAQA,oBAAAA,OAAA,EAAM,UAAA,KAAK,SAAS,CAAA;AAC9B;AAEA,SAAS,SAAS;AAAA,EAChB;AACF,GAKc;AACL,SAAA,oBAAC,MAAI,EAAA,UAAA,KAAK,SAAS,CAAA;AAC5B;"}
1
+ {"version":3,"file":"RichText.components.mjs","sources":["../../src/RichText.components.tsx"],"sourcesContent":["import {createElement, type ReactNode} from 'react';\n\nexport type CustomComponents = {\n /** The root node of the rich text. Defaults to `<div>` */\n root?: typeof Root;\n /** Customize the headings. Each heading has a `level` property from 1-6. Defaults to `<h1>` to `<h6>` */\n heading?: typeof Heading;\n /** Customize paragraphs. Defaults to `<p>` */\n paragraph?: typeof Paragraph;\n /** Customize how text nodes. They can either be bold or italic. Defaults to `<em>`, `<strong>` or text. */\n text?: typeof Text;\n /** Customize links. Defaults to a React Router `<Link>` component in Hydrogen and a `<a>` in Hydrogen React. */\n link?: typeof RichTextLink;\n /** Customize lists. They can be either ordered or unordered. Defaults to `<ol>` or `<ul>` */\n list?: typeof List;\n /** Customize list items. Defaults to `<li>`. */\n listItem?: typeof ListItem;\n};\n\nexport const RichTextComponents = {\n root: Root,\n heading: Heading,\n paragraph: Paragraph,\n text: Text,\n link: RichTextLink,\n list: List,\n 'list-item': ListItem,\n};\n\nfunction Root({\n node,\n}: {\n node: {\n type: 'root';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <div>{node.children}</div>;\n}\n\nfunction Heading({\n node,\n}: {\n node: {\n type: 'heading';\n level: number;\n children?: ReactNode[];\n };\n}): ReactNode {\n return createElement(`h${node.level ?? '1'}`, null, node.children);\n}\n\nfunction Paragraph({\n node,\n}: {\n node: {\n type: 'paragraph';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <p>{node.children}</p>;\n}\n\nfunction Text({\n node,\n}: {\n node: {\n type: 'text';\n italic?: boolean;\n bold?: boolean;\n value?: string;\n };\n}): ReactNode {\n if (node.bold && node.italic)\n return (\n <em>\n <strong>{node.value}</strong>\n </em>\n );\n\n if (node.bold) return <strong>{node.value}</strong>;\n if (node.italic) return <em>{node.value}</em>;\n\n return node.value;\n}\n\nfunction RichTextLink({\n node,\n}: {\n node: {\n type: 'link';\n url: string;\n title?: string;\n target?: string;\n children?: ReactNode[];\n };\n}): ReactNode {\n return (\n <a href={node.url} title={node.title} target={node.target}>\n {node.children}\n </a>\n );\n}\n\nfunction List({\n node,\n}: {\n node: {\n type: 'list';\n listType: 'unordered' | 'ordered';\n children?: ReactNode[];\n };\n}): ReactNode {\n const List = node.listType === 'unordered' ? 'ul' : 'ol';\n return <List>{node.children}</List>;\n}\n\nfunction ListItem({\n node,\n}: {\n node: {\n type: 'list-item';\n children?: ReactNode[];\n };\n}): ReactNode {\n return <li>{node.children}</li>;\n}\n"],"names":["List"],"mappings":";;AAmBO,MAAM,qBAAqB;AAAA,EAChC,MAAM;AAAA,EACN,SAAS;AAAA,EACT,WAAW;AAAA,EACX,MAAM;AAAA,EACN,MAAM;AAAA,EACN,MAAM;AAAA,EACN,aAAa;AACf;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAKc;AACL,SAAA,oBAAC,OAAK,EAAA,UAAA,KAAK,SAAS,CAAA;AAC7B;AAEA,SAAS,QAAQ;AAAA,EACf;AACF,GAMc;AACL,SAAA,cAAc,IAAI,KAAK,SAAS,GAAG,IAAI,MAAM,KAAK,QAAQ;AACnE;AAEA,SAAS,UAAU;AAAA,EACjB;AACF,GAKc;AACL,SAAA,oBAAC,KAAG,EAAA,UAAA,KAAK,SAAS,CAAA;AAC3B;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAOc;AACR,MAAA,KAAK,QAAQ,KAAK;AACpB,+BACG,MACC,EAAA,UAAA,oBAAC,UAAQ,EAAA,UAAA,KAAK,OAAM,EACtB,CAAA;AAGJ,MAAI,KAAK,KAAM,QAAQ,oBAAA,UAAA,EAAQ,eAAK,OAAM;AAC1C,MAAI,KAAK,OAAQ,QAAQ,oBAAA,MAAA,EAAI,eAAK,OAAM;AAExC,SAAO,KAAK;AACd;AAEA,SAAS,aAAa;AAAA,EACpB;AACF,GAQc;AACZ,SACG,oBAAA,KAAA,EAAE,MAAM,KAAK,KAAK,OAAO,KAAK,OAAO,QAAQ,KAAK,QAChD,UAAA,KAAK,UACR;AAEJ;AAEA,SAAS,KAAK;AAAA,EACZ;AACF,GAMc;AACZ,QAAMA,QAAO,KAAK,aAAa,cAAc,OAAO;AACpD,SAAQA,oBAAAA,OAAA,EAAM,UAAA,KAAK,SAAS,CAAA;AAC9B;AAEA,SAAS,SAAS;AAAA,EAChB;AACF,GAKc;AACL,SAAA,oBAAC,MAAI,EAAA,UAAA,KAAK,SAAS,CAAA;AAC5B;"}
@@ -1 +1 @@
1
- {"version":3,"file":"RichText.js","sources":["../../src/RichText.tsx"],"sourcesContent":["import {createElement, Fragment, type ReactNode, useMemo} from 'react';\nimport type {RichTextASTNode} from './RichText.types.js';\nimport {\n type CustomComponents,\n RichTextComponents,\n} from './RichText.components.js';\n\nexport interface RichTextPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** The JSON string that correspond to the Storefront API's [RichText format](https://shopify.dev/docs/apps/custom-data/metafields/types#rich-text-formatting). */\n data: string;\n /** Customize how rich text components are rendered */\n components?: CustomComponents;\n /** Remove rich text formatting and render plain text */\n plain?: boolean;\n}\n\nexport function RichText<ComponentGeneric extends React.ElementType = 'div'>({\n as,\n data,\n plain,\n components,\n ...passthroughProps\n}: RichTextProps<ComponentGeneric>): JSX.Element {\n try {\n const Wrapper = as ?? 'div';\n const parsedData = useMemo(\n () => JSON.parse(data) as RichTextASTNode,\n [data],\n );\n\n return (\n <Wrapper {...passthroughProps}>\n {plain\n ? richTextToString(parsedData)\n : serializeRichTextASTNode(components, parsedData)}\n </Wrapper>\n );\n } catch (e) {\n throw new Error(\n '[h2:error:RichText] Parsing error. Make sure to pass a JSON string of rich text metafield',\n {\n cause: e,\n },\n );\n }\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type RichTextProps<ComponentGeneric extends React.ElementType> =\n RichTextPropsBase<ComponentGeneric> &\n Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof RichTextPropsBase<ComponentGeneric>\n >;\n\nfunction serializeRichTextASTNode(\n components: CustomComponents = {},\n node: RichTextASTNode,\n index = 0,\n): ReactNode {\n let children;\n if ('children' in node) {\n children = node.children.map((child, childIndex) =>\n serializeRichTextASTNode(components, child, childIndex),\n );\n }\n\n const Component =\n components[node.type === 'list-item' ? 'listItem' : node.type] ??\n RichTextComponents[node.type];\n\n switch (node.type) {\n case 'root':\n return createElement(\n Component as Exclude<CustomComponents['root'], undefined>,\n {\n key: index,\n node: {\n type: 'root',\n children,\n },\n },\n );\n case 'heading':\n return createElement(\n Component as Exclude<CustomComponents['heading'], undefined>,\n {\n key: index,\n node: {\n type: 'heading',\n level: node.level,\n children,\n },\n },\n );\n case 'paragraph':\n return createElement(\n Component as Exclude<CustomComponents['paragraph'], undefined>,\n {\n key: index,\n node: {\n type: 'paragraph',\n children,\n },\n },\n );\n case 'text': {\n const elements = (node.value ?? '')\n .split('\\n')\n .flatMap((value, subindex) => {\n const key = `${index}-${value}-${subindex}`;\n const textElement = createElement(\n Component as Exclude<CustomComponents['text'], undefined>,\n {\n key,\n node: {\n type: 'text',\n italic: node.italic,\n bold: node.bold,\n value,\n },\n },\n );\n\n // Add a `<br>` before each substring except the first one\n return subindex === 0\n ? textElement\n : [createElement('br', {key: `${key}-br`}), textElement];\n });\n\n return elements.length > 1\n ? createElement(Fragment, {key: index}, elements)\n : elements[0];\n }\n case 'link':\n return createElement(\n Component as Exclude<CustomComponents['link'], undefined>,\n {\n key: index,\n node: {\n type: 'link',\n url: node.url,\n title: node.title,\n target: node.target,\n children,\n },\n },\n );\n case 'list':\n return createElement(\n Component as Exclude<CustomComponents['list'], undefined>,\n {\n key: index,\n node: {\n type: 'list',\n listType: node.listType,\n children,\n },\n },\n );\n case 'list-item':\n return createElement(\n Component as Exclude<CustomComponents['listItem'], undefined>,\n {\n key: index,\n node: {\n type: 'list-item',\n children,\n },\n },\n );\n }\n}\n\nfunction richTextToString(\n node: RichTextASTNode,\n result: string[] = [],\n): string {\n switch (node.type) {\n case 'root':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'heading':\n case 'paragraph':\n node.children.forEach((child) => richTextToString(child, result));\n result.push(' ');\n break;\n case 'text':\n result.push(node.value || '');\n break;\n case 'link':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'list':\n node.children.forEach((item) => {\n if (item.children) {\n item.children.forEach((child) => richTextToString(child, result));\n }\n result.push(' ');\n });\n break;\n default:\n throw new Error(`Unknown node encountered ${node.type}`);\n }\n\n return result.join('').trim();\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport type RichTextPropsForDocs<AsType extends React.ElementType = 'div'> =\n RichTextPropsBase<AsType>;\n"],"names":["useMemo","jsx","RichTextComponents","createElement","Fragment"],"mappings":";;;;;AAkBO,SAAS,SAA6D;AAAA,EAC3E;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,GAAiD;AAC3C,MAAA;AACF,UAAM,UAAU,MAAM;AACtB,UAAM,aAAaA,MAAA;AAAA,MACjB,MAAM,KAAK,MAAM,IAAI;AAAA,MACrB,CAAC,IAAI;AAAA,IAAA;AAIL,WAAAC,2BAAA,IAAC,SAAS,EAAA,GAAG,kBACV,UAAA,QACG,iBAAiB,UAAU,IAC3B,yBAAyB,YAAY,UAAU,EACrD,CAAA;AAAA,WAEK,GAAG;AACV,UAAM,IAAI;AAAA,MACR;AAAA,MACA;AAAA,QACE,OAAO;AAAA,MACT;AAAA,IAAA;AAAA,EAEJ;AACF;AAUA,SAAS,yBACP,aAA+B,CAAA,GAC/B,MACA,QAAQ,GACG;AACP,MAAA;AACJ,MAAI,cAAc,MAAM;AACtB,eAAW,KAAK,SAAS;AAAA,MAAI,CAAC,OAAO,eACnC,yBAAyB,YAAY,OAAO,UAAU;AAAA,IAAA;AAAA,EAE1D;AAEM,QAAA,YACJ,WAAW,KAAK,SAAS,cAAc,aAAa,KAAK,IAAI,KAC7DC,oBAAAA,mBAAmB,KAAK,IAAI;AAE9B,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACI,aAAAC,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ,KAAK;AACI,aAAAA,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,OAAO,KAAK;AAAA,YACZ;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ,KAAK;AACI,aAAAA,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ,KAAK,QAAQ;AACL,YAAA,YAAY,KAAK,SAAS,IAC7B,MAAM,IAAI,EACV,QAAQ,CAAC,OAAO,aAAa;AAC5B,cAAM,MAAM,GAAG,KAAK,IAAI,KAAK,IAAI,QAAQ;AACzC,cAAM,cAAcA,MAAA;AAAA,UAClB;AAAA,UACA;AAAA,YACE;AAAA,YACA,MAAM;AAAA,cACJ,MAAM;AAAA,cACN,QAAQ,KAAK;AAAA,cACb,MAAM,KAAK;AAAA,cACX;AAAA,YACF;AAAA,UACF;AAAA,QAAA;AAIF,eAAO,aAAa,IAChB,cACA,CAACA,MAAAA,cAAc,MAAM,EAAC,KAAK,GAAG,GAAG,MAAK,CAAC,GAAG,WAAW;AAAA,MAAA,CAC1D;AAEH,aAAO,SAAS,SAAS,IACrBA,MAAAA,cAAcC,MAAAA,UAAU,EAAC,KAAK,MAAQ,GAAA,QAAQ,IAC9C,SAAS,CAAC;AAAA,IAChB;AAAA,IACA,KAAK;AACI,aAAAD,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,KAAK,KAAK;AAAA,YACV,OAAO,KAAK;AAAA,YACZ,QAAQ,KAAK;AAAA,YACb;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ,KAAK;AACI,aAAAA,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN,UAAU,KAAK;AAAA,YACf;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,IAEJ,KAAK;AACI,aAAAA,MAAA;AAAA,QACL;AAAA,QACA;AAAA,UACE,KAAK;AAAA,UACL,MAAM;AAAA,YACJ,MAAM;AAAA,YACN;AAAA,UACF;AAAA,QACF;AAAA,MAAA;AAAA,EAEN;AACF;AAEA,SAAS,iBACP,MACA,SAAmB,IACX;AACR,UAAQ,KAAK,MAAM;AAAA,IACjB,KAAK;AACH,WAAK,SAAS,QAAQ,CAAC,UAAU,iBAAiB,OAAO,MAAM,CAAC;AAChE;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,WAAK,SAAS,QAAQ,CAAC,UAAU,iBAAiB,OAAO,MAAM,CAAC;AAChE,aAAO,KAAK,GAAG;AACf;AAAA,IACF,KAAK;AACI,aAAA,KAAK,KAAK,SAAS,EAAE;AAC5B;AAAA,IACF,KAAK;AACH,WAAK,SAAS,QAAQ,CAAC,UAAU,iBAAiB,OAAO,MAAM,CAAC;AAChE;AAAA,IACF,KAAK;AACE,WAAA,SAAS,QAAQ,CAAC,SAAS;AAC9B,YAAI,KAAK,UAAU;AACjB,eAAK,SAAS,QAAQ,CAAC,UAAU,iBAAiB,OAAO,MAAM,CAAC;AAAA,QAClE;AACA,eAAO,KAAK,GAAG;AAAA,MAAA,CAChB;AACD;AAAA,IACF;AACE,YAAM,IAAI,MAAM,4BAA4B,KAAK,IAAI,EAAE;AAAA,EAC3D;AAEA,SAAO,OAAO,KAAK,EAAE,EAAE,KAAK;AAC9B;;"}
1
+ {"version":3,"file":"RichText.js","sources":["../../src/RichText.tsx"],"sourcesContent":["import {createElement, Fragment, type ReactNode, useMemo} from 'react';\nimport type {RichTextASTNode} from './RichText.types.js';\nimport {\n type CustomComponents,\n RichTextComponents,\n} from './RichText.components.js';\n\nexport interface RichTextPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** The JSON string that correspond to the Storefront API's [RichText format](https://shopify.dev/docs/apps/custom-data/metafields/types#rich-text-formatting). */\n data: string;\n /** Customize how rich text components are rendered */\n components?: CustomComponents;\n /** Remove rich text formatting and render plain text */\n plain?: boolean;\n}\n\nexport function RichText<ComponentGeneric extends React.ElementType = 'div'>({\n as,\n data,\n plain,\n components,\n ...passthroughProps\n}: RichTextProps<ComponentGeneric>): JSX.Element {\n try {\n const Wrapper = as ?? 'div';\n const parsedData = useMemo(\n () => JSON.parse(data) as RichTextASTNode,\n [data],\n );\n\n return (\n <Wrapper {...passthroughProps}>\n {plain\n ? richTextToString(parsedData)\n : serializeRichTextASTNode(components, parsedData)}\n </Wrapper>\n );\n } catch (e) {\n throw new Error(\n '[h2:error:RichText] Parsing error. Make sure to pass a JSON string of rich text metafield',\n {\n cause: e,\n },\n );\n }\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type RichTextProps<ComponentGeneric extends React.ElementType> =\n RichTextPropsBase<ComponentGeneric> &\n Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof RichTextPropsBase<ComponentGeneric>\n >;\n\nfunction serializeRichTextASTNode(\n components: CustomComponents = {},\n node: RichTextASTNode,\n index = 0,\n): ReactNode {\n let children;\n if ('children' in node) {\n children = node.children.map((child, childIndex) =>\n serializeRichTextASTNode(components, child, childIndex),\n );\n }\n\n const Component =\n components[node.type === 'list-item' ? 'listItem' : node.type] ??\n RichTextComponents[node.type];\n\n switch (node.type) {\n case 'root':\n return createElement(\n Component as Exclude<CustomComponents['root'], undefined>,\n {\n key: index,\n node: {\n type: 'root',\n children,\n },\n },\n );\n case 'heading':\n return createElement(\n Component as Exclude<CustomComponents['heading'], undefined>,\n {\n key: index,\n node: {\n type: 'heading',\n level: node.level,\n children,\n },\n },\n );\n case 'paragraph':\n return createElement(\n Component as Exclude<CustomComponents['paragraph'], undefined>,\n {\n key: index,\n node: {\n type: 'paragraph',\n children,\n },\n },\n );\n case 'text': {\n const elements = (node.value ?? '')\n .split('\\n')\n .flatMap((value, subindex) => {\n const key = `${index}-${value}-${subindex}`;\n const textElement = createElement(\n Component as Exclude<CustomComponents['text'], undefined>,\n {\n key,\n node: {\n type: 'text',\n italic: node.italic,\n bold: node.bold,\n value,\n },\n },\n );\n\n // Add a `<br>` before each substring except the first one\n return subindex === 0\n ? textElement\n : [createElement('br', {key: `${key}-br`}), textElement];\n });\n\n return elements.length > 1\n ? createElement(Fragment, {key: index}, elements)\n : elements[0];\n }\n case 'link':\n return createElement(\n Component as Exclude<CustomComponents['link'], undefined>,\n {\n key: index,\n node: {\n type: 'link',\n url: node.url,\n title: node.title,\n target: node.target,\n children,\n },\n },\n );\n case 'list':\n return createElement(\n Component as Exclude<CustomComponents['list'], undefined>,\n {\n key: index,\n node: {\n type: 'list',\n listType: node.listType,\n children,\n },\n },\n );\n case 'list-item':\n return createElement(\n Component as Exclude<CustomComponents['listItem'], undefined>,\n {\n key: index,\n node: {\n type: 'list-item',\n children,\n },\n },\n );\n }\n}\n\nfunction richTextToString(\n node: RichTextASTNode,\n result: string[] = [],\n): string {\n switch (node.type) {\n case 'root':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'heading':\n case 'paragraph':\n node.children.forEach((child) => richTextToString(child, result));\n result.push(' ');\n break;\n case 'text':\n result.push(node.value || '');\n break;\n case 'link':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'list':\n node.children.forEach((item) => {\n if (item.children) {\n item.children.forEach((child) => richTextToString(child, result));\n }\n result.push(' ');\n });\n break;\n default:\n throw new Error(`Unknown node encountered ${node.type}`);\n }\n\n return result.join('').trim();\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport type RichTextPropsForDocs<AsType extends React.ElementType = 'div'> =\n RichTextPropsBase<AsType>;\n"],"names":["useMemo","jsx","RichTextComponents","createElement","Fragment"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"RichText.mjs","sources":["../../src/RichText.tsx"],"sourcesContent":["import {createElement, Fragment, type ReactNode, useMemo} from 'react';\nimport type {RichTextASTNode} from './RichText.types.js';\nimport {\n type CustomComponents,\n RichTextComponents,\n} from './RichText.components.js';\n\nexport interface RichTextPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** The JSON string that correspond to the Storefront API's [RichText format](https://shopify.dev/docs/apps/custom-data/metafields/types#rich-text-formatting). */\n data: string;\n /** Customize how rich text components are rendered */\n components?: CustomComponents;\n /** Remove rich text formatting and render plain text */\n plain?: boolean;\n}\n\nexport function RichText<ComponentGeneric extends React.ElementType = 'div'>({\n as,\n data,\n plain,\n components,\n ...passthroughProps\n}: RichTextProps<ComponentGeneric>): JSX.Element {\n try {\n const Wrapper = as ?? 'div';\n const parsedData = useMemo(\n () => JSON.parse(data) as RichTextASTNode,\n [data],\n );\n\n return (\n <Wrapper {...passthroughProps}>\n {plain\n ? richTextToString(parsedData)\n : serializeRichTextASTNode(components, parsedData)}\n </Wrapper>\n );\n } catch (e) {\n throw new Error(\n '[h2:error:RichText] Parsing error. Make sure to pass a JSON string of rich text metafield',\n {\n cause: e,\n },\n );\n }\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type RichTextProps<ComponentGeneric extends React.ElementType> =\n RichTextPropsBase<ComponentGeneric> &\n Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof RichTextPropsBase<ComponentGeneric>\n >;\n\nfunction serializeRichTextASTNode(\n components: CustomComponents = {},\n node: RichTextASTNode,\n index = 0,\n): ReactNode {\n let children;\n if ('children' in node) {\n children = node.children.map((child, childIndex) =>\n serializeRichTextASTNode(components, child, childIndex),\n );\n }\n\n const Component =\n components[node.type === 'list-item' ? 'listItem' : node.type] ??\n RichTextComponents[node.type];\n\n switch (node.type) {\n case 'root':\n return createElement(\n Component as Exclude<CustomComponents['root'], undefined>,\n {\n key: index,\n node: {\n type: 'root',\n children,\n },\n },\n );\n case 'heading':\n return createElement(\n Component as Exclude<CustomComponents['heading'], undefined>,\n {\n key: index,\n node: {\n type: 'heading',\n level: node.level,\n children,\n },\n },\n );\n case 'paragraph':\n return createElement(\n Component as Exclude<CustomComponents['paragraph'], undefined>,\n {\n key: index,\n node: {\n type: 'paragraph',\n children,\n },\n },\n );\n case 'text': {\n const elements = (node.value ?? '')\n .split('\\n')\n .flatMap((value, subindex) => {\n const key = `${index}-${value}-${subindex}`;\n const textElement = createElement(\n Component as Exclude<CustomComponents['text'], undefined>,\n {\n key,\n node: {\n type: 'text',\n italic: node.italic,\n bold: node.bold,\n value,\n },\n },\n );\n\n // Add a `<br>` before each substring except the first one\n return subindex === 0\n ? textElement\n : [createElement('br', {key: `${key}-br`}), textElement];\n });\n\n return elements.length > 1\n ? createElement(Fragment, {key: index}, elements)\n : elements[0];\n }\n case 'link':\n return createElement(\n Component as Exclude<CustomComponents['link'], undefined>,\n {\n key: index,\n node: {\n type: 'link',\n url: node.url,\n title: node.title,\n target: node.target,\n children,\n },\n },\n );\n case 'list':\n return createElement(\n Component as Exclude<CustomComponents['list'], undefined>,\n {\n key: index,\n node: {\n type: 'list',\n listType: node.listType,\n children,\n },\n },\n );\n case 'list-item':\n return createElement(\n Component as Exclude<CustomComponents['listItem'], undefined>,\n {\n key: index,\n node: {\n type: 'list-item',\n children,\n },\n },\n );\n }\n}\n\nfunction richTextToString(\n node: RichTextASTNode,\n result: string[] = [],\n): string {\n switch (node.type) {\n case 'root':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'heading':\n case 'paragraph':\n node.children.forEach((child) => richTextToString(child, result));\n result.push(' ');\n break;\n case 'text':\n result.push(node.value || '');\n break;\n case 'link':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'list':\n node.children.forEach((item) => {\n if (item.children) {\n item.children.forEach((child) => richTextToString(child, result));\n }\n result.push(' ');\n });\n break;\n default:\n throw new Error(`Unknown node encountered ${node.type}`);\n }\n\n return result.join('').trim();\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport type RichTextPropsForDocs<AsType extends React.ElementType = 'div'> =\n RichTextPropsBase<AsType>;\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RichText.mjs","sources":["../../src/RichText.tsx"],"sourcesContent":["import {createElement, Fragment, type ReactNode, useMemo} from 'react';\nimport type {RichTextASTNode} from './RichText.types.js';\nimport {\n type CustomComponents,\n RichTextComponents,\n} from './RichText.components.js';\n\nexport interface RichTextPropsBase<ComponentGeneric extends React.ElementType> {\n /** An HTML tag or React Component to be rendered as the base element wrapper. The default is `div`. */\n as?: ComponentGeneric;\n /** The JSON string that correspond to the Storefront API's [RichText format](https://shopify.dev/docs/apps/custom-data/metafields/types#rich-text-formatting). */\n data: string;\n /** Customize how rich text components are rendered */\n components?: CustomComponents;\n /** Remove rich text formatting and render plain text */\n plain?: boolean;\n}\n\nexport function RichText<ComponentGeneric extends React.ElementType = 'div'>({\n as,\n data,\n plain,\n components,\n ...passthroughProps\n}: RichTextProps<ComponentGeneric>): JSX.Element {\n try {\n const Wrapper = as ?? 'div';\n const parsedData = useMemo(\n () => JSON.parse(data) as RichTextASTNode,\n [data],\n );\n\n return (\n <Wrapper {...passthroughProps}>\n {plain\n ? richTextToString(parsedData)\n : serializeRichTextASTNode(components, parsedData)}\n </Wrapper>\n );\n } catch (e) {\n throw new Error(\n '[h2:error:RichText] Parsing error. Make sure to pass a JSON string of rich text metafield',\n {\n cause: e,\n },\n );\n }\n}\n\n// This article helps understand the typing here https://www.benmvp.com/blog/polymorphic-react-components-typescript/ Ben is the best :)\nexport type RichTextProps<ComponentGeneric extends React.ElementType> =\n RichTextPropsBase<ComponentGeneric> &\n Omit<\n React.ComponentPropsWithoutRef<ComponentGeneric>,\n keyof RichTextPropsBase<ComponentGeneric>\n >;\n\nfunction serializeRichTextASTNode(\n components: CustomComponents = {},\n node: RichTextASTNode,\n index = 0,\n): ReactNode {\n let children;\n if ('children' in node) {\n children = node.children.map((child, childIndex) =>\n serializeRichTextASTNode(components, child, childIndex),\n );\n }\n\n const Component =\n components[node.type === 'list-item' ? 'listItem' : node.type] ??\n RichTextComponents[node.type];\n\n switch (node.type) {\n case 'root':\n return createElement(\n Component as Exclude<CustomComponents['root'], undefined>,\n {\n key: index,\n node: {\n type: 'root',\n children,\n },\n },\n );\n case 'heading':\n return createElement(\n Component as Exclude<CustomComponents['heading'], undefined>,\n {\n key: index,\n node: {\n type: 'heading',\n level: node.level,\n children,\n },\n },\n );\n case 'paragraph':\n return createElement(\n Component as Exclude<CustomComponents['paragraph'], undefined>,\n {\n key: index,\n node: {\n type: 'paragraph',\n children,\n },\n },\n );\n case 'text': {\n const elements = (node.value ?? '')\n .split('\\n')\n .flatMap((value, subindex) => {\n const key = `${index}-${value}-${subindex}`;\n const textElement = createElement(\n Component as Exclude<CustomComponents['text'], undefined>,\n {\n key,\n node: {\n type: 'text',\n italic: node.italic,\n bold: node.bold,\n value,\n },\n },\n );\n\n // Add a `<br>` before each substring except the first one\n return subindex === 0\n ? textElement\n : [createElement('br', {key: `${key}-br`}), textElement];\n });\n\n return elements.length > 1\n ? createElement(Fragment, {key: index}, elements)\n : elements[0];\n }\n case 'link':\n return createElement(\n Component as Exclude<CustomComponents['link'], undefined>,\n {\n key: index,\n node: {\n type: 'link',\n url: node.url,\n title: node.title,\n target: node.target,\n children,\n },\n },\n );\n case 'list':\n return createElement(\n Component as Exclude<CustomComponents['list'], undefined>,\n {\n key: index,\n node: {\n type: 'list',\n listType: node.listType,\n children,\n },\n },\n );\n case 'list-item':\n return createElement(\n Component as Exclude<CustomComponents['listItem'], undefined>,\n {\n key: index,\n node: {\n type: 'list-item',\n children,\n },\n },\n );\n }\n}\n\nfunction richTextToString(\n node: RichTextASTNode,\n result: string[] = [],\n): string {\n switch (node.type) {\n case 'root':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'heading':\n case 'paragraph':\n node.children.forEach((child) => richTextToString(child, result));\n result.push(' ');\n break;\n case 'text':\n result.push(node.value || '');\n break;\n case 'link':\n node.children.forEach((child) => richTextToString(child, result));\n break;\n case 'list':\n node.children.forEach((item) => {\n if (item.children) {\n item.children.forEach((child) => richTextToString(child, result));\n }\n result.push(' ');\n });\n break;\n default:\n throw new Error(`Unknown node encountered ${node.type}`);\n }\n\n return result.join('').trim();\n}\n\n// This is only for documentation purposes, and it is not used in the code.\nexport type RichTextPropsForDocs<AsType extends React.ElementType = 'div'> =\n RichTextPropsBase<AsType>;\n"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ShopPayButton.js","sources":["../../src/ShopPayButton.tsx"],"sourcesContent":["import {defaultShopifyContext, useShop} from './ShopifyProvider.js';\nimport {useLoadScript} from './load-script.js';\nimport {parseGid} from './analytics-utils.js';\n\n// By using 'never' in the \"or\" cases below, it makes these props \"exclusive\" and means that you cannot pass both of them; you must pass either one OR the other.\ntype ShopPayButtonProps = ShopPayButtonStyleProps &\n ShopPayDomainProps &\n ShopPayChannelAttribution &\n (ShopPayVariantIds | ShopPayVariantAndQuantities);\n\ntype ShopPayButtonStyleProps = {\n /** A string of classes to apply to the `div` that wraps the Shop Pay button. */\n className?: string;\n /** A string that's applied to the [CSS custom property (variable)](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) `--shop-pay-button-width` for the [Buy with Shop Pay component](https://shopify.dev/custom-storefronts/tools/web-components#buy-with-shop-pay-component). */\n width?: string;\n};\n\ntype ShopPayDomainProps = {\n /** The domain of your Shopify storefront URL (eg: `your-store.myshopify.com`). */\n storeDomain?: string;\n};\n\ntype ShopPayVariantIds = {\n /** An array of IDs of the variants to purchase with Shop Pay. This will only ever have a quantity of 1 for each variant. If you want to use other quantities, then use `variantIdsAndQuantities`. */\n variantIds: string[];\n /** An array of variant IDs and quantities to purchase with Shop Pay. */\n variantIdsAndQuantities?: never;\n};\n\ntype ShopPayVariantAndQuantities = {\n /** An array of IDs of the variants to purchase with Shop Pay. This will only ever have a quantity of 1 for each variant. If you want to use other quantities, then use `variantIdsAndQuantities`. */\n variantIds?: never;\n /** An array of variant IDs and quantities to purchase with Shop Pay. */\n variantIdsAndQuantities: Array<{\n id: string;\n quantity: number;\n }>;\n};\n\ntype ShopPayChannelAttribution = {\n /** A string that adds channel attribution to the order. Can be either `headless` or `hydrogen` */\n channel?: 'headless' | 'hydrogen';\n};\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'shop-pay-button': {\n channel?: string;\n variants: string;\n 'store-url': string;\n };\n }\n }\n}\n\nconst SHOPJS_URL =\n 'https://cdn.shopify.com/shopifycloud/shop-js/v1.0/client.js';\n\nfunction isChannel(\n channel: string,\n): channel is Exclude<ShopPayChannelAttribution['channel'], undefined> {\n return channel === 'headless' || channel === 'hydrogen';\n}\n\n/**\n * The `ShopPayButton` component renders a button that redirects to the Shop Pay checkout.\n * It renders a [`<shop-pay-button>`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically.\n * It relies on the `<ShopProvider>` context provider.\n */\nexport function ShopPayButton({\n channel,\n variantIds,\n className,\n variantIdsAndQuantities,\n width,\n storeDomain: _storeDomain,\n}: ShopPayButtonProps): JSX.Element {\n const shop = useShop();\n const storeDomain = _storeDomain || shop?.storeDomain;\n const shopPayLoadedStatus = useLoadScript(SHOPJS_URL);\n\n let ids: string[] = [];\n let channelAttribution: string | undefined;\n\n if (!storeDomain || storeDomain === defaultShopifyContext.storeDomain) {\n throw new Error(MissingStoreDomainErrorMessage);\n }\n\n if (variantIds && variantIdsAndQuantities) {\n throw new Error(DoublePropsErrorMessage);\n }\n\n if (!variantIds && !variantIdsAndQuantities) {\n throw new Error(MissingPropsErrorMessage);\n }\n\n if (channel) {\n if (isChannel(channel)) {\n channelAttribution = channel;\n } else {\n throw new Error(InvalidChannelErrorMessage);\n }\n }\n\n if (variantIds) {\n ids = variantIds.reduce<string[]>((prev, curr) => {\n const bareId = parseGid(curr).id;\n if (bareId) {\n prev.push(bareId);\n }\n return prev;\n }, []);\n } else if (variantIdsAndQuantities) {\n ids = variantIdsAndQuantities.reduce<string[]>((prev, curr) => {\n const bareId = parseGid(curr?.id).id;\n if (bareId) {\n prev.push(`${bareId}:${curr?.quantity ?? 1}`);\n }\n return prev;\n }, []);\n } else {\n throw new Error(MissingPropsErrorMessage);\n }\n\n if (ids.length === 0) {\n throw new Error(InvalidPropsErrorMessage);\n }\n\n const style = width\n ? ({\n '--shop-pay-button-width': width,\n } as React.CSSProperties)\n : undefined;\n\n return (\n <div className={className} style={style}>\n {shopPayLoadedStatus === 'done' && (\n <shop-pay-button\n {...(channelAttribution ? {channel: channelAttribution} : {})}\n store-url={storeDomain}\n variants={ids.join(',')}\n />\n )}\n </div>\n );\n}\n\nexport const MissingStoreDomainErrorMessage =\n 'You must pass a \"storeDomain\" prop to the \"ShopPayButton\" component, or wrap it in a \"ShopifyProvider\" component.';\nexport const InvalidPropsErrorMessage = `You must pass in \"variantIds\" in the form of [\"gid://shopify/ProductVariant/1\"]`;\nexport const MissingPropsErrorMessage = `You must pass in either \"variantIds\" or \"variantIdsAndQuantities\" to ShopPayButton`;\nexport const DoublePropsErrorMessage = `You must provide either a variantIds or variantIdsAndQuantities prop, but not both in the ShopPayButton component`;\nexport const InvalidChannelErrorMessage = `Invalid channel attribution value. Must be either \"headless\" or \"hydrogen\"`;\n"],"names":["useShop","useLoadScript","defaultShopifyContext","parseGid","jsx"],"mappings":";;;;;;AAyDA,MAAM,aACJ;AAEF,SAAS,UACP,SACqE;AAC9D,SAAA,YAAY,cAAc,YAAY;AAC/C;AAOO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAAoC;AAClC,QAAM,OAAOA,gBAAAA;AACP,QAAA,cAAc,iBAAgB,6BAAM;AACpC,QAAA,sBAAsBC,yBAAc,UAAU;AAEpD,MAAI,MAAgB,CAAA;AAChB,MAAA;AAEJ,MAAI,CAAC,eAAe,gBAAgBC,gBAAAA,sBAAsB,aAAa;AAC/D,UAAA,IAAI,MAAM,8BAA8B;AAAA,EAChD;AAEA,MAAI,cAAc,yBAAyB;AACnC,UAAA,IAAI,MAAM,uBAAuB;AAAA,EACzC;AAEI,MAAA,CAAC,cAAc,CAAC,yBAAyB;AACrC,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,MAAI,SAAS;AACP,QAAA,UAAU,OAAO,GAAG;AACD,2BAAA;AAAA,IAAA,OAChB;AACC,YAAA,IAAI,MAAM,0BAA0B;AAAA,IAC5C;AAAA,EACF;AAEA,MAAI,YAAY;AACd,UAAM,WAAW,OAAiB,CAAC,MAAM,SAAS;AAC1C,YAAA,SAASC,eAAAA,SAAS,IAAI,EAAE;AAC9B,UAAI,QAAQ;AACV,aAAK,KAAK,MAAM;AAAA,MAClB;AACO,aAAA;AAAA,IACT,GAAG,CAAE,CAAA;AAAA,aACI,yBAAyB;AAClC,UAAM,wBAAwB,OAAiB,CAAC,MAAM,SAAS;AAC7D,YAAM,SAASA,eAAA,SAAS,6BAAM,EAAE,EAAE;AAClC,UAAI,QAAQ;AACV,aAAK,KAAK,GAAG,MAAM,KAAI,6BAAM,aAAY,CAAC,EAAE;AAAA,MAC9C;AACO,aAAA;AAAA,IACT,GAAG,CAAE,CAAA;AAAA,EAAA,OACA;AACC,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEI,MAAA,IAAI,WAAW,GAAG;AACd,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAC1C;AAEA,QAAM,QAAQ,QACT;AAAA,IACC,2BAA2B;AAAA,EAE7B,IAAA;AAEJ,SACGC,2BAAA,IAAA,OAAA,EAAI,WAAsB,OACxB,kCAAwB,UACvBA,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAI,qBAAqB,EAAC,SAAS,uBAAsB,CAAC;AAAA,MAC3D,aAAW;AAAA,MACX,UAAU,IAAI,KAAK,GAAG;AAAA,IAAA;AAAA,EAG5B,EAAA,CAAA;AAEJ;AAEO,MAAM,iCACX;AACK,MAAM,2BAA2B;AACjC,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,6BAA6B;;;;;;;"}
1
+ {"version":3,"file":"ShopPayButton.js","sources":["../../src/ShopPayButton.tsx"],"sourcesContent":["import {defaultShopifyContext, useShop} from './ShopifyProvider.js';\nimport {useLoadScript} from './load-script.js';\nimport {parseGid} from './analytics-utils.js';\n\n// By using 'never' in the \"or\" cases below, it makes these props \"exclusive\" and means that you cannot pass both of them; you must pass either one OR the other.\ntype ShopPayButtonProps = ShopPayButtonStyleProps &\n ShopPayDomainProps &\n ShopPayChannelAttribution &\n (ShopPayVariantIds | ShopPayVariantAndQuantities);\n\ntype ShopPayButtonStyleProps = {\n /** A string of classes to apply to the `div` that wraps the Shop Pay button. */\n className?: string;\n /** A string that's applied to the [CSS custom property (variable)](https://developer.mozilla.org/en-US/docs/Web/CSS/--*) `--shop-pay-button-width` for the [Buy with Shop Pay component](https://shopify.dev/custom-storefronts/tools/web-components#buy-with-shop-pay-component). */\n width?: string;\n};\n\ntype ShopPayDomainProps = {\n /** The domain of your Shopify storefront URL (eg: `your-store.myshopify.com`). */\n storeDomain?: string;\n};\n\ntype ShopPayVariantIds = {\n /** An array of IDs of the variants to purchase with Shop Pay. This will only ever have a quantity of 1 for each variant. If you want to use other quantities, then use `variantIdsAndQuantities`. */\n variantIds: string[];\n /** An array of variant IDs and quantities to purchase with Shop Pay. */\n variantIdsAndQuantities?: never;\n};\n\ntype ShopPayVariantAndQuantities = {\n /** An array of IDs of the variants to purchase with Shop Pay. This will only ever have a quantity of 1 for each variant. If you want to use other quantities, then use `variantIdsAndQuantities`. */\n variantIds?: never;\n /** An array of variant IDs and quantities to purchase with Shop Pay. */\n variantIdsAndQuantities: Array<{\n id: string;\n quantity: number;\n }>;\n};\n\ntype ShopPayChannelAttribution = {\n /** A string that adds channel attribution to the order. Can be either `headless` or `hydrogen` */\n channel?: 'headless' | 'hydrogen';\n};\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace JSX {\n interface IntrinsicElements {\n 'shop-pay-button': {\n channel?: string;\n variants: string;\n 'store-url': string;\n };\n }\n }\n}\n\nconst SHOPJS_URL =\n 'https://cdn.shopify.com/shopifycloud/shop-js/v1.0/client.js';\n\nfunction isChannel(\n channel: string,\n): channel is Exclude<ShopPayChannelAttribution['channel'], undefined> {\n return channel === 'headless' || channel === 'hydrogen';\n}\n\n/**\n * The `ShopPayButton` component renders a button that redirects to the Shop Pay checkout.\n * It renders a [`<shop-pay-button>`](https://shopify.dev/custom-storefronts/tools/web-components) custom element, for which it will lazy-load the source code automatically.\n * It relies on the `<ShopProvider>` context provider.\n */\nexport function ShopPayButton({\n channel,\n variantIds,\n className,\n variantIdsAndQuantities,\n width,\n storeDomain: _storeDomain,\n}: ShopPayButtonProps): JSX.Element {\n const shop = useShop();\n const storeDomain = _storeDomain || shop?.storeDomain;\n const shopPayLoadedStatus = useLoadScript(SHOPJS_URL);\n\n let ids: string[] = [];\n let channelAttribution: string | undefined;\n\n if (!storeDomain || storeDomain === defaultShopifyContext.storeDomain) {\n throw new Error(MissingStoreDomainErrorMessage);\n }\n\n if (variantIds && variantIdsAndQuantities) {\n throw new Error(DoublePropsErrorMessage);\n }\n\n if (!variantIds && !variantIdsAndQuantities) {\n throw new Error(MissingPropsErrorMessage);\n }\n\n if (channel) {\n if (isChannel(channel)) {\n channelAttribution = channel;\n } else {\n throw new Error(InvalidChannelErrorMessage);\n }\n }\n\n if (variantIds) {\n ids = variantIds.reduce<string[]>((prev, curr) => {\n const bareId = parseGid(curr).id;\n if (bareId) {\n prev.push(bareId);\n }\n return prev;\n }, []);\n } else if (variantIdsAndQuantities) {\n ids = variantIdsAndQuantities.reduce<string[]>((prev, curr) => {\n const bareId = parseGid(curr?.id).id;\n if (bareId) {\n prev.push(`${bareId}:${curr?.quantity ?? 1}`);\n }\n return prev;\n }, []);\n } else {\n throw new Error(MissingPropsErrorMessage);\n }\n\n if (ids.length === 0) {\n throw new Error(InvalidPropsErrorMessage);\n }\n\n const style = width\n ? ({\n '--shop-pay-button-width': width,\n } as React.CSSProperties)\n : undefined;\n\n return (\n <div className={className} style={style}>\n {shopPayLoadedStatus === 'done' && (\n <shop-pay-button\n {...(channelAttribution ? {channel: channelAttribution} : {})}\n store-url={storeDomain}\n variants={ids.join(',')}\n />\n )}\n </div>\n );\n}\n\nexport const MissingStoreDomainErrorMessage =\n 'You must pass a \"storeDomain\" prop to the \"ShopPayButton\" component, or wrap it in a \"ShopifyProvider\" component.';\nexport const InvalidPropsErrorMessage = `You must pass in \"variantIds\" in the form of [\"gid://shopify/ProductVariant/1\"]`;\nexport const MissingPropsErrorMessage = `You must pass in either \"variantIds\" or \"variantIdsAndQuantities\" to ShopPayButton`;\nexport const DoublePropsErrorMessage = `You must provide either a variantIds or variantIdsAndQuantities prop, but not both in the ShopPayButton component`;\nexport const InvalidChannelErrorMessage = `Invalid channel attribution value. Must be either \"headless\" or \"hydrogen\"`;\n"],"names":["useShop","useLoadScript","defaultShopifyContext","parseGid","jsx"],"mappings":";;;;;;AAyDA,MAAM,aACJ;AAEF,SAAS,UACP,SACqE;AAC9D,SAAA,YAAY,cAAc,YAAY;AAC/C;AAOO,SAAS,cAAc;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AACf,GAAoC;AAClC,QAAM,OAAOA,gBAAAA,QAAQ;AACf,QAAA,cAAc,iBAAgB,6BAAM;AACpC,QAAA,sBAAsBC,yBAAc,UAAU;AAEpD,MAAI,MAAgB,CAAC;AACjB,MAAA;AAEJ,MAAI,CAAC,eAAe,gBAAgBC,gBAAAA,sBAAsB,aAAa;AAC/D,UAAA,IAAI,MAAM,8BAA8B;AAAA,EAAA;AAGhD,MAAI,cAAc,yBAAyB;AACnC,UAAA,IAAI,MAAM,uBAAuB;AAAA,EAAA;AAGrC,MAAA,CAAC,cAAc,CAAC,yBAAyB;AACrC,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAAA;AAG1C,MAAI,SAAS;AACP,QAAA,UAAU,OAAO,GAAG;AACD,2BAAA;AAAA,IAAA,OAChB;AACC,YAAA,IAAI,MAAM,0BAA0B;AAAA,IAAA;AAAA,EAC5C;AAGF,MAAI,YAAY;AACd,UAAM,WAAW,OAAiB,CAAC,MAAM,SAAS;AAC1C,YAAA,SAASC,eAAAA,SAAS,IAAI,EAAE;AAC9B,UAAI,QAAQ;AACV,aAAK,KAAK,MAAM;AAAA,MAAA;AAEX,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,aACI,yBAAyB;AAClC,UAAM,wBAAwB,OAAiB,CAAC,MAAM,SAAS;AAC7D,YAAM,SAASA,eAAA,SAAS,6BAAM,EAAE,EAAE;AAClC,UAAI,QAAQ;AACV,aAAK,KAAK,GAAG,MAAM,KAAI,6BAAM,aAAY,CAAC,EAAE;AAAA,MAAA;AAEvC,aAAA;AAAA,IACT,GAAG,EAAE;AAAA,EAAA,OACA;AACC,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAAA;AAGtC,MAAA,IAAI,WAAW,GAAG;AACd,UAAA,IAAI,MAAM,wBAAwB;AAAA,EAAA;AAG1C,QAAM,QAAQ,QACT;AAAA,IACC,2BAA2B;AAAA,EAAA,IAE7B;AAEJ,SACGC,2BAAA,IAAA,OAAA,EAAI,WAAsB,OACxB,kCAAwB,UACvBA,2BAAA;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAI,qBAAqB,EAAC,SAAS,uBAAsB,CAAC;AAAA,MAC3D,aAAW;AAAA,MACX,UAAU,IAAI,KAAK,GAAG;AAAA,IAAA;AAAA,EAAA,GAG5B;AAEJ;AAEO,MAAM,iCACX;AACK,MAAM,2BAA2B;AACjC,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,6BAA6B;;;;;;;"}