@merchi/cart 1.4.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 (372) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +134 -0
  3. package/esm/Cart.js +62 -0
  4. package/esm/CartAlert.js +47 -0
  5. package/esm/CartHeader.js +29 -0
  6. package/esm/CartProvider.js +1101 -0
  7. package/esm/CartTotals.js +89 -0
  8. package/esm/assets/merchi-monster-loader.gif +0 -0
  9. package/esm/assets/merchi-swipe-outline.png +0 -0
  10. package/esm/assets/product-not-found.png +0 -0
  11. package/esm/assets/stripe-payment-secure.png +0 -0
  12. package/esm/buttons/Button.js +23 -0
  13. package/esm/buttons/ButtonBack.js +35 -0
  14. package/esm/buttons/ButtonClearCart.js +17 -0
  15. package/esm/buttons/ButtonInvoiceDownload.js +88 -0
  16. package/esm/buttons/ButtonNext.js +39 -0
  17. package/esm/buttons/ButtonNextDynamic.js +19 -0
  18. package/esm/buttons/ButtonOpenCartMerchiCartModal.js +47 -0
  19. package/esm/buttons/ButtonPay.js +26 -0
  20. package/esm/buttons/ButtonShipmentTabNext.js +28 -0
  21. package/esm/buttons/index.js +7 -0
  22. package/esm/components/Alert.js +26 -0
  23. package/esm/components/CartBody.js +14 -0
  24. package/esm/components/CartClient.js +62 -0
  25. package/esm/components/CartFooter.js +12 -0
  26. package/esm/components/CartItemRow.js +230 -0
  27. package/esm/components/CartPaymentSettingsInvalid.js +13 -0
  28. package/esm/components/CartShipment.js +52 -0
  29. package/esm/components/CartTabContent.js +12 -0
  30. package/esm/components/CartTabPanel.js +25 -0
  31. package/esm/components/CartTableContainer.js +12 -0
  32. package/esm/components/CartTitle.js +26 -0
  33. package/esm/components/CartTotalsListGroup.js +71 -0
  34. package/esm/components/Collapse.js +32 -0
  35. package/esm/components/CustomCartProductForm.js +128 -0
  36. package/esm/components/CustomerCheckoutMethodTabs.js +94 -0
  37. package/esm/components/DiscountInputGroup.js +293 -0
  38. package/esm/components/LoadingTemplate.js +23 -0
  39. package/esm/components/NoCartItems.js +20 -0
  40. package/esm/components/ShipmentGroupCard.js +207 -0
  41. package/esm/components/Table.js +13 -0
  42. package/esm/components/containers.js +31 -0
  43. package/esm/components/index.js +18 -0
  44. package/esm/components/product-form-sdk-externals.js +36 -0
  45. package/esm/forms/FormNewCustomer.js +243 -0
  46. package/esm/forms/FormReturningCustomer.js +165 -0
  47. package/esm/forms/FormShipmentAddressAndNotes.js +195 -0
  48. package/esm/forms/FormSquarePayment.js +19 -0
  49. package/esm/forms/FormStripePayment.js +19 -0
  50. package/esm/forms/FormWhatsappCustomer.js +253 -0
  51. package/esm/forms/InputAcceptUserTermsAndConditions.js +35 -0
  52. package/esm/forms/InputError.js +16 -0
  53. package/esm/forms/InputSelect.js +48 -0
  54. package/esm/forms/InputText.js +56 -0
  55. package/esm/forms/InputsAddress.js +476 -0
  56. package/esm/forms/index.js +8 -0
  57. package/esm/index.js +13 -0
  58. package/esm/modals.js +51 -0
  59. package/esm/panels/PanelCartItems.js +49 -0
  60. package/esm/panels/PanelCartShipment.js +66 -0
  61. package/esm/panels/PanelClearCart.js +56 -0
  62. package/esm/panels/PanelClientCheckout.js +81 -0
  63. package/esm/panels/PanelEditCartItem.js +199 -0
  64. package/esm/panels/PanelPaymentSuccess.js +63 -0
  65. package/esm/panels/index.js +7 -0
  66. package/esm/square/SquareCard.js +106 -0
  67. package/esm/stripe/LegacyStripeCardForm.js +157 -0
  68. package/esm/stripe/StripeCardFields.js +126 -0
  69. package/esm/stripe/StripeCardForm.js +33 -0
  70. package/esm/stripe/StripePaymentButtons.js +98 -0
  71. package/esm/stripe/actions.js +192 -0
  72. package/esm/stripe/utils.js +20 -0
  73. package/esm/styles/globals.css +463 -0
  74. package/esm/styles/globals.css.map +1 -0
  75. package/esm/tabs/CartNav.js +28 -0
  76. package/esm/tabs/NavTab.js +31 -0
  77. package/esm/types/assets.d.js +0 -0
  78. package/esm/types/index.js +1 -0
  79. package/esm/types/merchi_product_form/index.d.js +0 -0
  80. package/esm/types/merchi_sdk_ts/index.d.js +0 -0
  81. package/esm/types/react-redux/index.d.js +0 -0
  82. package/esm/utilities/address.js +457 -0
  83. package/esm/utilities/area.js +297 -0
  84. package/esm/utilities/cart.js +95 -0
  85. package/esm/utilities/company.js +6 -0
  86. package/esm/utilities/cookie.js +156 -0
  87. package/esm/utilities/currency.js +219 -0
  88. package/esm/utilities/customForm.js +57 -0
  89. package/esm/utilities/helpers.js +229 -0
  90. package/esm/utilities/index.js +14 -0
  91. package/esm/utilities/invoice.js +112 -0
  92. package/esm/utilities/job.js +9 -0
  93. package/esm/utilities/local_storage.js +85 -0
  94. package/esm/utilities/product.js +13 -0
  95. package/esm/utilities/shipment.js +11 -0
  96. package/esm/utilities/tabs.js +51 -0
  97. package/esm/utilities/user.js +65 -0
  98. package/esm/utilities/variations.js +58 -0
  99. package/lib/Cart.d.ts +9 -0
  100. package/lib/Cart.js +69 -0
  101. package/lib/CartAlert.d.ts +3 -0
  102. package/lib/CartAlert.js +53 -0
  103. package/lib/CartHeader.d.ts +3 -0
  104. package/lib/CartHeader.js +37 -0
  105. package/lib/CartProvider.d.ts +236 -0
  106. package/lib/CartProvider.js +1107 -0
  107. package/lib/CartTotals.d.ts +7 -0
  108. package/lib/CartTotals.js +96 -0
  109. package/lib/assets/merchi-monster-loader.gif +0 -0
  110. package/lib/assets/merchi-swipe-outline.png +0 -0
  111. package/lib/assets/product-not-found.png +0 -0
  112. package/lib/assets/stripe-payment-secure.png +0 -0
  113. package/lib/buttons/Button.d.ts +11 -0
  114. package/lib/buttons/Button.js +31 -0
  115. package/lib/buttons/ButtonBack.d.ts +6 -0
  116. package/lib/buttons/ButtonBack.js +44 -0
  117. package/lib/buttons/ButtonClearCart.d.ts +3 -0
  118. package/lib/buttons/ButtonClearCart.js +24 -0
  119. package/lib/buttons/ButtonInvoiceDownload.d.ts +3 -0
  120. package/lib/buttons/ButtonInvoiceDownload.js +96 -0
  121. package/lib/buttons/ButtonNext.d.ts +3 -0
  122. package/lib/buttons/ButtonNext.js +48 -0
  123. package/lib/buttons/ButtonNextDynamic.d.ts +3 -0
  124. package/lib/buttons/ButtonNextDynamic.js +28 -0
  125. package/lib/buttons/ButtonOpenCartMerchiCartModal.d.ts +9 -0
  126. package/lib/buttons/ButtonOpenCartMerchiCartModal.js +57 -0
  127. package/lib/buttons/ButtonPay.d.ts +7 -0
  128. package/lib/buttons/ButtonPay.js +35 -0
  129. package/lib/buttons/ButtonShipmentTabNext.d.ts +2 -0
  130. package/lib/buttons/ButtonShipmentTabNext.js +37 -0
  131. package/lib/buttons/index.d.ts +7 -0
  132. package/lib/buttons/index.js +48 -0
  133. package/lib/components/Alert.d.ts +7 -0
  134. package/lib/components/Alert.js +34 -0
  135. package/lib/components/CartBody.d.ts +7 -0
  136. package/lib/components/CartBody.js +22 -0
  137. package/lib/components/CartClient.d.ts +4 -0
  138. package/lib/components/CartClient.js +71 -0
  139. package/lib/components/CartFooter.d.ts +6 -0
  140. package/lib/components/CartFooter.js +20 -0
  141. package/lib/components/CartItemRow.d.ts +7 -0
  142. package/lib/components/CartItemRow.js +236 -0
  143. package/lib/components/CartPaymentSettingsInvalid.d.ts +2 -0
  144. package/lib/components/CartPaymentSettingsInvalid.js +21 -0
  145. package/lib/components/CartShipment.d.ts +6 -0
  146. package/lib/components/CartShipment.js +59 -0
  147. package/lib/components/CartTabContent.d.ts +6 -0
  148. package/lib/components/CartTabContent.js +20 -0
  149. package/lib/components/CartTabPanel.d.ts +7 -0
  150. package/lib/components/CartTabPanel.js +31 -0
  151. package/lib/components/CartTableContainer.d.ts +6 -0
  152. package/lib/components/CartTableContainer.js +20 -0
  153. package/lib/components/CartTitle.d.ts +2 -0
  154. package/lib/components/CartTitle.js +34 -0
  155. package/lib/components/CartTotalsListGroup.d.ts +3 -0
  156. package/lib/components/CartTotalsListGroup.js +78 -0
  157. package/lib/components/Collapse.d.ts +3 -0
  158. package/lib/components/Collapse.js +40 -0
  159. package/lib/components/CustomCartProductForm.d.ts +18 -0
  160. package/lib/components/CustomCartProductForm.js +136 -0
  161. package/lib/components/CustomerCheckoutMethodTabs.d.ts +6 -0
  162. package/lib/components/CustomerCheckoutMethodTabs.js +103 -0
  163. package/lib/components/DiscountInputGroup.d.ts +2 -0
  164. package/lib/components/DiscountInputGroup.js +301 -0
  165. package/lib/components/LoadingTemplate.d.ts +7 -0
  166. package/lib/components/LoadingTemplate.js +30 -0
  167. package/lib/components/NoCartItems.d.ts +3 -0
  168. package/lib/components/NoCartItems.js +27 -0
  169. package/lib/components/ShipmentGroupCard.d.ts +7 -0
  170. package/lib/components/ShipmentGroupCard.js +215 -0
  171. package/lib/components/Table.d.ts +7 -0
  172. package/lib/components/Table.js +19 -0
  173. package/lib/components/containers.d.ts +14 -0
  174. package/lib/components/containers.js +40 -0
  175. package/lib/components/index.d.ts +18 -0
  176. package/lib/components/index.js +133 -0
  177. package/lib/components/product-form-sdk-externals.d.ts +46 -0
  178. package/lib/components/product-form-sdk-externals.js +43 -0
  179. package/lib/forms/FormNewCustomer.d.ts +4 -0
  180. package/lib/forms/FormNewCustomer.js +252 -0
  181. package/lib/forms/FormReturningCustomer.d.ts +3 -0
  182. package/lib/forms/FormReturningCustomer.js +173 -0
  183. package/lib/forms/FormShipmentAddressAndNotes.d.ts +15 -0
  184. package/lib/forms/FormShipmentAddressAndNotes.js +203 -0
  185. package/lib/forms/FormSquarePayment.d.ts +3 -0
  186. package/lib/forms/FormSquarePayment.js +27 -0
  187. package/lib/forms/FormStripePayment.d.ts +3 -0
  188. package/lib/forms/FormStripePayment.js +27 -0
  189. package/lib/forms/FormWhatsappCustomer.d.ts +3 -0
  190. package/lib/forms/FormWhatsappCustomer.js +261 -0
  191. package/lib/forms/InputAcceptUserTermsAndConditions.d.ts +7 -0
  192. package/lib/forms/InputAcceptUserTermsAndConditions.js +41 -0
  193. package/lib/forms/InputError.d.ts +8 -0
  194. package/lib/forms/InputError.js +24 -0
  195. package/lib/forms/InputSelect.d.ts +14 -0
  196. package/lib/forms/InputSelect.js +55 -0
  197. package/lib/forms/InputText.d.ts +12 -0
  198. package/lib/forms/InputText.js +63 -0
  199. package/lib/forms/InputsAddress.d.ts +11 -0
  200. package/lib/forms/InputsAddress.js +485 -0
  201. package/lib/forms/index.d.ts +8 -0
  202. package/lib/forms/index.js +63 -0
  203. package/lib/index.d.ts +13 -0
  204. package/lib/index.js +93 -0
  205. package/lib/modals.d.ts +17 -0
  206. package/lib/modals.js +59 -0
  207. package/lib/panels/PanelCartItems.d.ts +3 -0
  208. package/lib/panels/PanelCartItems.js +56 -0
  209. package/lib/panels/PanelCartShipment.d.ts +3 -0
  210. package/lib/panels/PanelCartShipment.js +72 -0
  211. package/lib/panels/PanelClearCart.d.ts +3 -0
  212. package/lib/panels/PanelClearCart.js +62 -0
  213. package/lib/panels/PanelClientCheckout.d.ts +3 -0
  214. package/lib/panels/PanelClientCheckout.js +88 -0
  215. package/lib/panels/PanelEditCartItem.d.ts +6 -0
  216. package/lib/panels/PanelEditCartItem.js +206 -0
  217. package/lib/panels/PanelPaymentSuccess.d.ts +3 -0
  218. package/lib/panels/PanelPaymentSuccess.js +69 -0
  219. package/lib/panels/index.d.ts +7 -0
  220. package/lib/panels/index.js +48 -0
  221. package/lib/square/SquareCard.d.ts +3 -0
  222. package/lib/square/SquareCard.js +114 -0
  223. package/lib/stripe/LegacyStripeCardForm.d.ts +3 -0
  224. package/lib/stripe/LegacyStripeCardForm.js +166 -0
  225. package/lib/stripe/StripeCardFields.d.ts +15 -0
  226. package/lib/stripe/StripeCardFields.js +135 -0
  227. package/lib/stripe/StripeCardForm.d.ts +2 -0
  228. package/lib/stripe/StripeCardForm.js +42 -0
  229. package/lib/stripe/StripePaymentButtons.d.ts +8 -0
  230. package/lib/stripe/StripePaymentButtons.js +106 -0
  231. package/lib/stripe/actions.d.ts +4 -0
  232. package/lib/stripe/actions.js +200 -0
  233. package/lib/stripe/utils.d.ts +18 -0
  234. package/lib/stripe/utils.js +27 -0
  235. package/lib/styles/globals.css +463 -0
  236. package/lib/styles/globals.css.map +1 -0
  237. package/lib/tabs/CartNav.d.ts +3 -0
  238. package/lib/tabs/CartNav.js +36 -0
  239. package/lib/tabs/NavTab.d.ts +9 -0
  240. package/lib/tabs/NavTab.js +39 -0
  241. package/lib/types/index.d.ts +13 -0
  242. package/lib/types/index.js +1 -0
  243. package/lib/utilities/address.d.ts +30 -0
  244. package/lib/utilities/address.js +468 -0
  245. package/lib/utilities/area.d.ts +77 -0
  246. package/lib/utilities/area.js +328 -0
  247. package/lib/utilities/cart.d.ts +9 -0
  248. package/lib/utilities/cart.js +108 -0
  249. package/lib/utilities/company.d.ts +2 -0
  250. package/lib/utilities/company.js +13 -0
  251. package/lib/utilities/cookie.d.ts +4 -0
  252. package/lib/utilities/cookie.js +165 -0
  253. package/lib/utilities/currency.d.ts +5 -0
  254. package/lib/utilities/currency.js +229 -0
  255. package/lib/utilities/customForm.d.ts +17 -0
  256. package/lib/utilities/customForm.js +64 -0
  257. package/lib/utilities/helpers.d.ts +24 -0
  258. package/lib/utilities/helpers.js +240 -0
  259. package/lib/utilities/index.d.ts +14 -0
  260. package/lib/utilities/index.js +159 -0
  261. package/lib/utilities/invoice.d.ts +3 -0
  262. package/lib/utilities/invoice.js +118 -0
  263. package/lib/utilities/job.d.ts +1 -0
  264. package/lib/utilities/job.js +15 -0
  265. package/lib/utilities/local_storage.d.ts +31 -0
  266. package/lib/utilities/local_storage.js +96 -0
  267. package/lib/utilities/product.d.ts +2 -0
  268. package/lib/utilities/product.js +21 -0
  269. package/lib/utilities/shipment.d.ts +2 -0
  270. package/lib/utilities/shipment.js +18 -0
  271. package/lib/utilities/tabs.d.ts +14 -0
  272. package/lib/utilities/tabs.js +60 -0
  273. package/lib/utilities/user.d.ts +5 -0
  274. package/lib/utilities/user.js +75 -0
  275. package/lib/utilities/variations.d.ts +18 -0
  276. package/lib/utilities/variations.js +66 -0
  277. package/package.json +109 -0
  278. package/src/Cart.tsx +96 -0
  279. package/src/CartAlert.tsx +35 -0
  280. package/src/CartHeader.tsx +28 -0
  281. package/src/CartProvider.tsx +992 -0
  282. package/src/CartTotals.tsx +102 -0
  283. package/src/assets/merchi-monster-loader.gif +0 -0
  284. package/src/assets/merchi-swipe-outline.png +0 -0
  285. package/src/assets/product-not-found.png +0 -0
  286. package/src/assets/stripe-payment-secure.png +0 -0
  287. package/src/buttons/Button.tsx +35 -0
  288. package/src/buttons/ButtonBack.tsx +42 -0
  289. package/src/buttons/ButtonClearCart.tsx +17 -0
  290. package/src/buttons/ButtonInvoiceDownload.tsx +60 -0
  291. package/src/buttons/ButtonNext.tsx +35 -0
  292. package/src/buttons/ButtonNextDynamic.tsx +31 -0
  293. package/src/buttons/ButtonOpenCartMerchiCartModal.tsx +61 -0
  294. package/src/buttons/ButtonPay.tsx +32 -0
  295. package/src/buttons/ButtonShipmentTabNext.tsx +24 -0
  296. package/src/buttons/index.ts +15 -0
  297. package/src/components/Alert.tsx +31 -0
  298. package/src/components/CartBody.tsx +16 -0
  299. package/src/components/CartClient.tsx +52 -0
  300. package/src/components/CartFooter.tsx +15 -0
  301. package/src/components/CartItemRow.tsx +212 -0
  302. package/src/components/CartPaymentSettingsInvalid.tsx +12 -0
  303. package/src/components/CartShipment.tsx +56 -0
  304. package/src/components/CartTabContent.tsx +15 -0
  305. package/src/components/CartTabPanel.tsx +25 -0
  306. package/src/components/CartTableContainer.tsx +15 -0
  307. package/src/components/CartTitle.tsx +22 -0
  308. package/src/components/CartTotalsListGroup.tsx +58 -0
  309. package/src/components/Collapse.tsx +23 -0
  310. package/src/components/CustomCartProductForm.tsx +123 -0
  311. package/src/components/CustomerCheckoutMethodTabs.tsx +106 -0
  312. package/src/components/DiscountInputGroup.tsx +160 -0
  313. package/src/components/LoadingTemplate.tsx +22 -0
  314. package/src/components/NoCartItems.tsx +20 -0
  315. package/src/components/ShipmentGroupCard.tsx +170 -0
  316. package/src/components/Table.tsx +15 -0
  317. package/src/components/containers.tsx +40 -0
  318. package/src/components/index.ts +38 -0
  319. package/src/components/product-form-sdk-externals.ts +67 -0
  320. package/src/forms/FormNewCustomer.tsx +170 -0
  321. package/src/forms/FormReturningCustomer.tsx +117 -0
  322. package/src/forms/FormShipmentAddressAndNotes.tsx +165 -0
  323. package/src/forms/FormSquarePayment.tsx +19 -0
  324. package/src/forms/FormStripePayment.tsx +19 -0
  325. package/src/forms/FormWhatsappCustomer.tsx +179 -0
  326. package/src/forms/InputAcceptUserTermsAndConditions.tsx +43 -0
  327. package/src/forms/InputError.tsx +23 -0
  328. package/src/forms/InputSelect.tsx +58 -0
  329. package/src/forms/InputText.tsx +56 -0
  330. package/src/forms/InputsAddress.tsx +438 -0
  331. package/src/forms/index.ts +18 -0
  332. package/src/index.ts +31 -0
  333. package/src/modals.tsx +65 -0
  334. package/src/panels/PanelCartItems.tsx +56 -0
  335. package/src/panels/PanelCartShipment.tsx +86 -0
  336. package/src/panels/PanelClearCart.tsx +50 -0
  337. package/src/panels/PanelClientCheckout.tsx +102 -0
  338. package/src/panels/PanelEditCartItem.tsx +172 -0
  339. package/src/panels/PanelPaymentSuccess.tsx +66 -0
  340. package/src/panels/index.ts +15 -0
  341. package/src/square/SquareCard.tsx +84 -0
  342. package/src/stripe/LegacyStripeCardForm.tsx +91 -0
  343. package/src/stripe/StripeCardFields.tsx +173 -0
  344. package/src/stripe/StripeCardForm.tsx +18 -0
  345. package/src/stripe/StripePaymentButtons.tsx +117 -0
  346. package/src/stripe/actions.ts +94 -0
  347. package/src/stripe/utils.ts +22 -0
  348. package/src/styles/globals.scss +475 -0
  349. package/src/tabs/CartNav.tsx +18 -0
  350. package/src/tabs/NavTab.tsx +36 -0
  351. package/src/types/assets.d.ts +4 -0
  352. package/src/types/index.ts +15 -0
  353. package/src/types/merchi_product_form/index.d.ts +1 -0
  354. package/src/types/merchi_sdk_ts/index.d.ts +1 -0
  355. package/src/types/react-redux/index.d.ts +0 -0
  356. package/src/utilities/address.ts +463 -0
  357. package/src/utilities/area.ts +359 -0
  358. package/src/utilities/cart.ts +80 -0
  359. package/src/utilities/company.ts +8 -0
  360. package/src/utilities/cookie.ts +58 -0
  361. package/src/utilities/currency.ts +216 -0
  362. package/src/utilities/customForm.ts +71 -0
  363. package/src/utilities/helpers.ts +213 -0
  364. package/src/utilities/index.ts +14 -0
  365. package/src/utilities/invoice.ts +60 -0
  366. package/src/utilities/job.ts +13 -0
  367. package/src/utilities/local_storage.ts +130 -0
  368. package/src/utilities/product.ts +14 -0
  369. package/src/utilities/shipment.ts +11 -0
  370. package/src/utilities/tabs.ts +38 -0
  371. package/src/utilities/user.ts +33 -0
  372. package/src/utilities/variations.ts +80 -0
@@ -0,0 +1,359 @@
1
+ /** Shared helpers for Area (height × width) variation fields. Stored value is always millimetres. */
2
+
3
+ export type AreaUnit = 'mm' | 'cm' | 'm' | 'in' | 'ft';
4
+ export type AreaInputType = 'input' | 'slider';
5
+ export type DisplayModality = 'metric' | 'imperial';
6
+
7
+ const MM_PER_INCH = 25.4;
8
+ const MM_PER_FOOT = 304.8;
9
+ const MM_PER_CM = 10;
10
+ const MM_PER_M = 1000;
11
+
12
+ export function parseAreaValue(
13
+ value: string | null | undefined
14
+ ): { heightMm: number; widthMm: number } | null {
15
+ if (value == null) return null;
16
+ const text = String(value).trim();
17
+ if (!text) return null;
18
+ const parts = text.split(',').map((p) => p.trim());
19
+ if (parts.length !== 2) return null;
20
+ const heightMm = Number(parts[0]);
21
+ const widthMm = Number(parts[1]);
22
+ if (!Number.isFinite(heightMm) || !Number.isFinite(widthMm)) return null;
23
+ if (heightMm <= 0 || widthMm <= 0) return null;
24
+ return { heightMm, widthMm };
25
+ }
26
+
27
+ export function formatAreaValue(heightMm: number, widthMm: number): string {
28
+ const h = Number(heightMm);
29
+ const w = Number(widthMm);
30
+ if (!Number.isFinite(h) || !Number.isFinite(w) || h <= 0 || w <= 0) return '';
31
+ return `${trimNum(h)},${trimNum(w)}`;
32
+ }
33
+
34
+ function trimNum(n: number): string {
35
+ return String(Number(n.toFixed(6)));
36
+ }
37
+
38
+ export function isImperialUnit(unit: AreaUnit | string | null | undefined): boolean {
39
+ const u = String(unit || 'mm').toLowerCase();
40
+ return u === 'in' || u === 'ft';
41
+ }
42
+
43
+ export function normaliseAreaUnit(unit?: string | null): AreaUnit {
44
+ const u = String(unit || 'mm').toLowerCase();
45
+ if (u === 'cm' || u === 'm' || u === 'in' || u === 'ft' || u === 'mm') {
46
+ return u;
47
+ }
48
+ return 'mm';
49
+ }
50
+
51
+ /** Imperial for en-US / en-LR / en-MM; otherwise metric (incl. en-GB). */
52
+ export function localePrefersImperial(locale?: string): boolean {
53
+ const lang =
54
+ locale ||
55
+ (typeof navigator !== 'undefined' ? navigator.language : undefined) ||
56
+ '';
57
+ const normalized = lang.toLowerCase().replace('_', '-');
58
+ return (
59
+ normalized === 'en-us' ||
60
+ normalized === 'en-lr' ||
61
+ normalized === 'en-mm' ||
62
+ normalized.endsWith('-us') ||
63
+ normalized.endsWith('-lr') ||
64
+ normalized.endsWith('-mm')
65
+ );
66
+ }
67
+
68
+ /** Millimetres per one unit of the given area unit. */
69
+ export function unitFactorMm(unit: AreaUnit | string = 'mm'): number {
70
+ switch (normaliseAreaUnit(unit)) {
71
+ case 'cm':
72
+ return MM_PER_CM;
73
+ case 'm':
74
+ return MM_PER_M;
75
+ case 'in':
76
+ return MM_PER_INCH;
77
+ case 'ft':
78
+ return MM_PER_FOOT;
79
+ default:
80
+ return 1;
81
+ }
82
+ }
83
+
84
+ /** @deprecated use unitFactorMm */
85
+ export function metricUnitFactor(unit: AreaUnit = 'mm'): number {
86
+ return unitFactorMm(unit);
87
+ }
88
+
89
+ export function mmToUnit(mm: number, unit: AreaUnit | string = 'mm'): number {
90
+ return mm / unitFactorMm(unit);
91
+ }
92
+
93
+ export function unitToMm(value: number, unit: AreaUnit | string = 'mm'): number {
94
+ return value * unitFactorMm(unit);
95
+ }
96
+
97
+ /** @deprecated use mmToUnit */
98
+ export function mmToMetricDisplay(mm: number, unit: AreaUnit = 'mm'): number {
99
+ return mmToUnit(mm, unit);
100
+ }
101
+
102
+ /** @deprecated use unitToMm */
103
+ export function metricDisplayToMm(value: number, unit: AreaUnit = 'mm'): number {
104
+ return unitToMm(value, unit);
105
+ }
106
+
107
+ export function mmToInches(mm: number): number {
108
+ return mm / MM_PER_INCH;
109
+ }
110
+
111
+ export function inchesToMm(inches: number): number {
112
+ return inches * MM_PER_INCH;
113
+ }
114
+
115
+ /**
116
+ * Unit shown for the current buyer modality.
117
+ * Metric → field unit if metric, else mm.
118
+ * Imperial → field unit if imperial, else inches.
119
+ */
120
+ export function activeDisplayUnit(
121
+ modality: DisplayModality,
122
+ areaUnit: AreaUnit | string = 'mm'
123
+ ): AreaUnit {
124
+ const unit = normaliseAreaUnit(areaUnit);
125
+ if (modality === 'imperial') {
126
+ return isImperialUnit(unit) ? unit : 'in';
127
+ }
128
+ return isImperialUnit(unit) ? 'mm' : unit;
129
+ }
130
+
131
+ export function defaultModalityForAreaUnit(
132
+ areaUnit: AreaUnit | string = 'mm'
133
+ ): DisplayModality {
134
+ if (isImperialUnit(areaUnit)) return 'imperial';
135
+ return localePrefersImperial() ? 'imperial' : 'metric';
136
+ }
137
+
138
+ export function mmToDisplay(
139
+ mm: number,
140
+ modality: DisplayModality,
141
+ areaUnit: AreaUnit | string = 'mm'
142
+ ): number {
143
+ return mmToUnit(mm, activeDisplayUnit(modality, areaUnit));
144
+ }
145
+
146
+ export function displayToMm(
147
+ value: number,
148
+ modality: DisplayModality,
149
+ areaUnit: AreaUnit | string = 'mm'
150
+ ): number {
151
+ return unitToMm(value, activeDisplayUnit(modality, areaUnit));
152
+ }
153
+
154
+ export function unitLabel(
155
+ modality: DisplayModality,
156
+ areaUnit: AreaUnit | string = 'mm'
157
+ ): string {
158
+ return activeDisplayUnit(modality, areaUnit);
159
+ }
160
+
161
+ export function defaultAreaStep(unit: AreaUnit | string = 'mm'): number {
162
+ switch (normaliseAreaUnit(unit)) {
163
+ case 'm':
164
+ return 0.001;
165
+ case 'cm':
166
+ return 0.1;
167
+ case 'in':
168
+ return 0.125;
169
+ case 'ft':
170
+ return 0.01;
171
+ default:
172
+ return 1;
173
+ }
174
+ }
175
+
176
+ /**
177
+ * Step for inputs/sliders in the current display unit.
178
+ * `stepMm` is the admin-configured step stored in millimetres.
179
+ */
180
+ export function stepInDisplayUnit(
181
+ stepMm: number | null | undefined,
182
+ modality: DisplayModality,
183
+ areaUnit: AreaUnit | string = 'mm'
184
+ ): number {
185
+ const unit = activeDisplayUnit(modality, areaUnit);
186
+ const raw = Number(stepMm);
187
+ if (Number.isFinite(raw) && raw > 0) {
188
+ return raw / unitFactorMm(unit);
189
+ }
190
+ return defaultAreaStep(unit);
191
+ }
192
+
193
+ function decimalsForUnit(unit: AreaUnit): number {
194
+ if (unit === 'm') return 4;
195
+ if (unit === 'ft') return 3;
196
+ if (unit === 'in' || unit === 'cm') return 2;
197
+ return 2;
198
+ }
199
+
200
+ /** Format a length in mm for buyer-facing summary. */
201
+ export function formatLengthFromMm(
202
+ mm: number,
203
+ modality: DisplayModality,
204
+ areaUnit: AreaUnit | string = 'mm'
205
+ ): string {
206
+ const unit = activeDisplayUnit(modality, areaUnit);
207
+ if (unit === 'ft') {
208
+ const feet = mmToUnit(mm, 'ft');
209
+ return `${trimNum(Number(feet.toFixed(3)))} ft`;
210
+ }
211
+ if (unit === 'in') {
212
+ const inches = mmToInches(mm);
213
+ if (inches >= 12) {
214
+ const feet = Math.floor(inches / 12);
215
+ const rem = inches - feet * 12;
216
+ if (rem < 0.05) return `${feet} ft`;
217
+ return `${feet} ft ${trimNum(Number(rem.toFixed(2)))} in`;
218
+ }
219
+ return `${trimNum(Number(inches.toFixed(2)))} in`;
220
+ }
221
+ const display = mmToUnit(mm, unit);
222
+ return `${trimNum(Number(display.toFixed(decimalsForUnit(unit))))} ${unit}`;
223
+ }
224
+
225
+ export function formatAreaParts(
226
+ value: string | null | undefined,
227
+ modality: DisplayModality = 'metric',
228
+ areaUnit: AreaUnit | string = 'mm'
229
+ ): { width: string; height: string; areaLabel: string } | null {
230
+ const parsed = parseAreaValue(value);
231
+ if (!parsed) return null;
232
+ const { heightMm, widthMm } = parsed;
233
+ const height = formatLengthFromMm(heightMm, modality, areaUnit);
234
+ const width = formatLengthFromMm(widthMm, modality, areaUnit);
235
+ const areaMm2 = heightMm * widthMm;
236
+ const unit = activeDisplayUnit(modality, areaUnit);
237
+ let areaLabel: string;
238
+ if (unit === 'ft') {
239
+ const sqFt = areaMm2 / (MM_PER_FOOT * MM_PER_FOOT);
240
+ areaLabel = `${trimNum(Number(sqFt.toFixed(3)))} sq ft`;
241
+ } else if (unit === 'in') {
242
+ const sqIn = areaMm2 / (MM_PER_INCH * MM_PER_INCH);
243
+ if (sqIn >= 144) {
244
+ areaLabel = `${trimNum(Number((sqIn / 144).toFixed(3)))} sq ft`;
245
+ } else {
246
+ areaLabel = `${trimNum(Number(sqIn.toFixed(2)))} sq in`;
247
+ }
248
+ } else if (unit === 'm') {
249
+ areaLabel = `${trimNum(Number((areaMm2 / 1_000_000).toFixed(4)))} m²`;
250
+ } else if (unit === 'cm') {
251
+ areaLabel = `${trimNum(Number((areaMm2 / 100).toFixed(2)))} cm²`;
252
+ } else {
253
+ areaLabel = `${trimNum(Number(areaMm2.toFixed(2)))} mm²`;
254
+ }
255
+ return { width, height, areaLabel };
256
+ }
257
+
258
+ export function formatAreaSummary(
259
+ value: string | null | undefined,
260
+ modality: DisplayModality = 'metric',
261
+ areaUnit: AreaUnit | string = 'mm'
262
+ ): string | null {
263
+ const parts = formatAreaParts(value, modality, areaUnit);
264
+ if (!parts) return null;
265
+ // Width × height (landscape-first) to match the buyer control order.
266
+ return `Width ${parts.width} × Height ${parts.height} (${parts.areaLabel})`;
267
+ }
268
+
269
+ export function clamp(n: number, min?: number | null, max?: number | null): number {
270
+ let v = n;
271
+ if (min != null && Number.isFinite(min)) v = Math.max(v, min);
272
+ if (max != null && Number.isFinite(max)) v = Math.min(v, max);
273
+ return v;
274
+ }
275
+
276
+ /**
277
+ * Client-side Area cost estimate (pre-discount), matching API formula:
278
+ * onceOff = heightCost × widthCost × height_u × width_u
279
+ * unitCost = heightUnit × widthUnit × height_u × width_u
280
+ * (dimensions expressed in the field's areaUnit)
281
+ */
282
+ export function estimateAreaCosts(
283
+ variationField: any,
284
+ value: string | null | undefined
285
+ ): { onceOffCost: number; unitCost: number } | null {
286
+ const parsed = parseAreaValue(value);
287
+ if (!parsed) return null;
288
+ const areaUnit = normaliseAreaUnit(
289
+ variationField?.areaUnit ?? variationField?.area_unit ?? 'mm'
290
+ );
291
+ const heightU = mmToUnit(parsed.heightMm, areaUnit);
292
+ const widthU = mmToUnit(parsed.widthMm, areaUnit);
293
+ const area = heightU * widthU;
294
+ const heightCost = Number(
295
+ variationField?.heightVariationCost ??
296
+ variationField?.height_variation_cost
297
+ ) || 0;
298
+ const widthCost = Number(
299
+ variationField?.widthVariationCost ?? variationField?.width_variation_cost
300
+ ) || 0;
301
+ const heightUnit = Number(
302
+ variationField?.heightVariationUnitCost ??
303
+ variationField?.height_variation_unit_cost
304
+ ) || 0;
305
+ const widthUnit = Number(
306
+ variationField?.widthVariationUnitCost ??
307
+ variationField?.width_variation_unit_cost
308
+ ) || 0;
309
+ const onceOffCost = heightCost * widthCost * area;
310
+ const unitCost = heightUnit * widthUnit * area;
311
+ if (!(onceOffCost > 0) && !(unitCost > 0)) return null;
312
+ return {
313
+ onceOffCost: Number(onceOffCost.toFixed(3)),
314
+ unitCost: Number(unitCost.toFixed(3)),
315
+ };
316
+ }
317
+
318
+ /**
319
+ * Keep width/height linked by aspectRatio (width/height).
320
+ * `changed` indicates which dimension the user edited.
321
+ */
322
+ export function clampWithAspectRatio(args: {
323
+ heightMm: number;
324
+ widthMm: number;
325
+ changed: 'height' | 'width';
326
+ aspectRatio: number;
327
+ heightMin?: number | null;
328
+ heightMax?: number | null;
329
+ widthMin?: number | null;
330
+ widthMax?: number | null;
331
+ }): { heightMm: number; widthMm: number } {
332
+ const ratio = args.aspectRatio;
333
+ if (!Number.isFinite(ratio) || ratio <= 0) {
334
+ return {
335
+ heightMm: clamp(args.heightMm, args.heightMin, args.heightMax),
336
+ widthMm: clamp(args.widthMm, args.widthMin, args.widthMax),
337
+ };
338
+ }
339
+
340
+ let heightMm = args.heightMm;
341
+ let widthMm = args.widthMm;
342
+
343
+ if (args.changed === 'height') {
344
+ heightMm = clamp(heightMm, args.heightMin, args.heightMax);
345
+ widthMm = heightMm * ratio;
346
+ widthMm = clamp(widthMm, args.widthMin, args.widthMax);
347
+ heightMm = widthMm / ratio;
348
+ heightMm = clamp(heightMm, args.heightMin, args.heightMax);
349
+ widthMm = heightMm * ratio;
350
+ } else {
351
+ widthMm = clamp(widthMm, args.widthMin, args.widthMax);
352
+ heightMm = widthMm / ratio;
353
+ heightMm = clamp(heightMm, args.heightMin, args.heightMax);
354
+ widthMm = heightMm * ratio;
355
+ widthMm = clamp(widthMm, args.widthMin, args.widthMax);
356
+ }
357
+
358
+ return { heightMm, widthMm };
359
+ }
@@ -0,0 +1,80 @@
1
+ import { Merchi } from '@merchi/sdk';
2
+ import { companyStripePubKeyOrTestPubKey } from './company';
3
+
4
+ const merchi = new Merchi();
5
+
6
+ export const makeCart = (json: any, makeDirty?: boolean, cartToken?: string) => {
7
+ if (cartToken) merchi.cartToken = cartToken;
8
+ const cart = new merchi.Cart();
9
+ return cart.fromJson(json, { makeDirty });
10
+ };
11
+
12
+ export const makeCartItem = (cartItemJson: any, makeDirty?: boolean, cartToken?: string) => {
13
+ if (cartToken) merchi.cartToken = cartToken;
14
+ const cartItem = new merchi.CartItem();
15
+ return cartItem.fromJson(cartItemJson, { makeDirty });
16
+ };
17
+
18
+ export function makeDiscountItem(itemJson: any, makeDirty?: boolean) {
19
+ const item = new merchi.Item();
20
+ if (makeDirty) {
21
+ return item.fromJson(itemJson, {makeDirty: true});
22
+ } else {
23
+ return item.fromJson(itemJson);
24
+ }
25
+ }
26
+
27
+ export function makeCartShipmentQuote(json: any, makeDirty?: boolean) {
28
+ const cartShipmentQuote = new merchi.CartShipmentQuote();
29
+ return cartShipmentQuote.fromJson(json, { makeDirty });
30
+ }
31
+
32
+ export function cartHasSelectedQuotes(cart: any) {
33
+ const { shipmentGroups = [] } = cart;
34
+ if (shipmentGroups.length) {
35
+ const selected = shipmentGroups.filter((g: any) => !!g.selectedQuote);
36
+ return selected.length === shipmentGroups.length;
37
+ }
38
+ return false;
39
+ }
40
+
41
+ export async function getCartShipmentGroupsAndQuotes(cart: any) {
42
+ const query: Array<any> = [];
43
+ query.push(['cart_token', cart.token]);
44
+ return merchi.authenticatedFetch(
45
+ `/generate-cart-shipment-quotes/${cart.id}/`,
46
+ {method: 'GET', query});
47
+ }
48
+
49
+ function cartCompany(cart: any) {
50
+ return cart && cart.domain && cart.domain.company ?
51
+ cart.domain.company : null;
52
+ }
53
+
54
+ export function hasStripeLegacyAccount(cart: any) {
55
+ const company = cartCompany(cart);
56
+ return Boolean(company && companyStripePubKeyOrTestPubKey(company));
57
+ }
58
+
59
+ function hasStripeConnectAccount(cart: any) {
60
+ const company = cartCompany(cart);
61
+ return Boolean(company && company.isStripeAccountEnabled);
62
+ }
63
+
64
+ export function stripeIsValidAndActive(cart: any) {
65
+ const company = cartCompany(cart);
66
+ const hasStripe = hasStripeLegacyAccount(cart) ||
67
+ hasStripeConnectAccount(cart);
68
+ return Boolean(hasStripe && company && company.acceptStripe);
69
+ }
70
+
71
+ export function cartRequiresShipment(cart: any) {
72
+ const { cartItems = [] } = cart;
73
+ for (let i = 0; i < cartItems.length; i++) {
74
+ const item = cartItems[i];
75
+ if (item && item.product && item.product.needsShipping) {
76
+ return true;
77
+ }
78
+ }
79
+ return false;
80
+ }
@@ -0,0 +1,8 @@
1
+ export function companyPrimaryAddress(company: any) {
2
+ return company && company.addresses ? company.addresses[0] : undefined;
3
+ }
4
+
5
+ export function companyStripePubKeyOrTestPubKey(company: any) {
6
+ return company.isTesting ? company.stripePublishableTestKey :
7
+ company.stripePublishableKey;
8
+ }
@@ -0,0 +1,58 @@
1
+ import { isBrowser } from "browser-or-node";
2
+
3
+ export async function getCookie(name: string, defaultValue: any) {
4
+ if (!isBrowser) {
5
+ return '';
6
+ }
7
+ const searchPrefix = name + '=';
8
+ const cookies = document.cookie.split(';');
9
+ let i;
10
+ let cookie;
11
+ for (i = 0; i < cookies.length; ++i) {
12
+ cookie = cookies[i];
13
+ cookie = cookie.replace(/^\s*/, '');
14
+ if (cookie.indexOf(searchPrefix) === 0) {
15
+ return cookie.substring(searchPrefix.length, cookie.length);
16
+ }
17
+ }
18
+ if (defaultValue === undefined) {
19
+ throw 'no such cookie present';
20
+ } else {
21
+ return defaultValue;
22
+ }
23
+ }
24
+
25
+ export async function getCartCookie(domainId: number | string) {
26
+ const idAndToken: string = await getCookie(`cart-${domainId}`, null);
27
+ return idAndToken ? idAndToken.split(',') : null;
28
+ }
29
+
30
+ export async function getCartCookieToken(domainId: number | string) {
31
+ const cartIdAndToken = domainId ? await getCartCookie(Number(domainId)) : undefined;
32
+ return cartIdAndToken && cartIdAndToken[1] || undefined;
33
+ }
34
+
35
+ async function setSessionCookie(name: string, value: any, domain?: any) {
36
+ if (!isBrowser) {
37
+ return;
38
+ }
39
+ let cookie = name + '=' + value;
40
+ if (!!domain) {
41
+ // remove port, if it exists
42
+ let n = domain.indexOf(':');
43
+ domain = domain.substring(0, n != -1 ? n : domain.length);
44
+ // remove trailing slash and path, if they exists
45
+ n = domain.indexOf('/');
46
+ domain = domain.substring(0, n != -1 ? n : domain.length);
47
+ cookie += '; Domain=' + domain;
48
+ } else {
49
+ cookie += '; Domain=' + '.' + (location as any).hostname;
50
+ }
51
+ cookie += '; path=/';
52
+ document.cookie = cookie;
53
+ }
54
+
55
+ export const setCartCookie = async (storeId: number, cart: any, domain?: any) => {
56
+ const cookieValue = cart ? cart.id + ',' + cart.token : '';
57
+ await setSessionCookie('cart-' + String(storeId), cookieValue, domain);
58
+ };