@envive-ai/react-hooks 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (385) hide show
  1. package/LICENSE +2 -0
  2. package/README.md +2 -0
  3. package/dist/GridInsertionService-CEYo9pGj.js +22 -0
  4. package/dist/GridInsertionService-CS_bnPh0.cjs +28 -0
  5. package/dist/bandolier-Ble8jEa8.js +1221 -0
  6. package/dist/bandolier-Bm2xAt_j.cjs +1221 -0
  7. package/dist/carpe-Da7b-LCW.cjs +599 -0
  8. package/dist/carpe-W13mhRRP.js +597 -0
  9. package/dist/cdnService-CZ-aXcY6.cjs +23 -0
  10. package/dist/cdnService-zQfKk3Eb.js +18 -0
  11. package/dist/chatElementDisplayLocation-CX8fuNao.d.cts +239 -0
  12. package/dist/chatElementDisplayLocation-CwptS9tx.d.ts +239 -0
  13. package/dist/chunk-CUT6urMc.cjs +30 -0
  14. package/dist/contexts/index.cjs +13 -0
  15. package/dist/contexts/index.d.cts +65 -0
  16. package/dist/contexts/index.d.ts +66 -0
  17. package/dist/contexts/index.js +7 -0
  18. package/dist/contexts-BRjfVq_k.js +5064 -0
  19. package/dist/contexts-BYArqZtK.cjs +5164 -0
  20. package/dist/coterie-3y0D9ko4.cjs +229 -0
  21. package/dist/coterie-DOWcJAYv.js +229 -0
  22. package/dist/custservice-types-CFIFwZ-r.js +10 -0
  23. package/dist/custservice-types-CkfxZiHY.cjs +16 -0
  24. package/dist/default-C2fEZKXk.js +175 -0
  25. package/dist/default-CBUq6Q6G.cjs +4 -0
  26. package/dist/default-CGIFZK6m.js +4 -0
  27. package/dist/default-D_KPZdPJ.cjs +198 -0
  28. package/dist/divIds-Bss-btao.js +49 -0
  29. package/dist/divIds-DnZNd7rA.cjs +223 -0
  30. package/dist/dreamlandBaby-DCIsuU9R.cjs +338 -0
  31. package/dist/dreamlandBaby-DvSaZGrz.js +338 -0
  32. package/dist/entrypoints-D_JUvkgy.cjs +18 -0
  33. package/dist/entrypoints-YLQsbBRD.js +6 -0
  34. package/dist/enviveConfigContext-CUGLpPGU.js +34 -0
  35. package/dist/enviveConfigContext-Dfr2VH6u.cjs +48 -0
  36. package/dist/fiveCbd-B1SESMCO.js +605 -0
  37. package/dist/fiveCbd-CkOlVby_.cjs +605 -0
  38. package/dist/forLoveAndLemons-CfYPMnKS.cjs +660 -0
  39. package/dist/forLoveAndLemons-DmwYZIk0.js +658 -0
  40. package/dist/greenpan-Bsl3ir59.cjs +389 -0
  41. package/dist/greenpan-BtOi45lf.js +389 -0
  42. package/dist/grooveLife-6_dtYsRk.js +334 -0
  43. package/dist/grooveLife-Cmm1PSCL.cjs +334 -0
  44. package/dist/homegrownCannabis-C-kw-74X.js +400 -0
  45. package/dist/homegrownCannabis-CO0uY_mp.cjs +400 -0
  46. package/dist/hooks/index.cjs +16 -0
  47. package/dist/hooks/index.d.cts +357 -0
  48. package/dist/hooks/index.d.ts +357 -0
  49. package/dist/hooks/index.js +7 -0
  50. package/dist/jackArcher-CLVmwwpI.js +719 -0
  51. package/dist/jackArcher-DdYTIzAV.cjs +719 -0
  52. package/dist/jordanCraig-Am-Oor-O.js +1778 -0
  53. package/dist/jordanCraig-_u3-w4Hp.cjs +1778 -0
  54. package/dist/kindredBravely-CWovIDSc.cjs +482 -0
  55. package/dist/kindredBravely-eWp-ud_E.js +482 -0
  56. package/dist/kutFromTheKloth-BMV4BuGQ.js +361 -0
  57. package/dist/kutFromTheKloth-Q589bAOC.cjs +361 -0
  58. package/dist/larryAndSerges-BMUlTgI-.js +252 -0
  59. package/dist/larryAndSerges-CEau764j.cjs +252 -0
  60. package/dist/leapsAndRebounds-DGMzPO7T.js +352 -0
  61. package/dist/leapsAndRebounds-DHAtRTJD.cjs +352 -0
  62. package/dist/logger-Dln20ans.cjs +25 -0
  63. package/dist/logger-pdEEY8T2.js +19 -0
  64. package/dist/longevityrx-CZW8Hxzi.cjs +312 -0
  65. package/dist/longevityrx-jH2JLhNH.js +312 -0
  66. package/dist/lookOptic-BGXP5P_V.js +274 -0
  67. package/dist/lookOptic-CA6RwLbG.cjs +274 -0
  68. package/dist/mantraBrand-Cm9_PBCT.js +742 -0
  69. package/dist/mantraBrand-DByNqpnL.cjs +742 -0
  70. package/dist/medterra-B0wxj_PV.js +575 -0
  71. package/dist/medterra-DnPN2ksU.cjs +575 -0
  72. package/dist/modells-Bmz8Ag5M.js +476 -0
  73. package/dist/modells-CoYgkLSp.cjs +476 -0
  74. package/dist/models-DHdb7QWn.js +51 -0
  75. package/dist/models-ixxUsGL_.cjs +69 -0
  76. package/dist/pressedFloral-DSKs_oVG.js +653 -0
  77. package/dist/pressedFloral-DjBiSoUl.cjs +653 -0
  78. package/dist/skinPerfection-B_3xzVNS.cjs +326 -0
  79. package/dist/skinPerfection-IDrBuAPt.js +326 -0
  80. package/dist/snapSupplements-BJk5T5ba.js +277 -0
  81. package/dist/snapSupplements-BStTsdOZ.cjs +277 -0
  82. package/dist/socialProofClasses-Bhv2Vulz.js +9 -0
  83. package/dist/socialProofClasses-CrQBWdSA.cjs +39 -0
  84. package/dist/spanx-BYg0LE7R.js +653 -0
  85. package/dist/spanx-LwU1zSzq.cjs +655 -0
  86. package/dist/spanxStaging-CfSmuKYB.js +837 -0
  87. package/dist/spanxStaging-OZLV9qix.cjs +840 -0
  88. package/dist/suggestionBarV2-types-BllzwsBD.js +34 -0
  89. package/dist/suggestionBarV2-types-CaovchMP.cjs +46 -0
  90. package/dist/supergoop-BqPXDnKk.cjs +327 -0
  91. package/dist/supergoop-CIlrHND_.js +325 -0
  92. package/dist/types-C4T5UOIW.cjs +230 -0
  93. package/dist/types-CYNvLeSA.js +176 -0
  94. package/dist/uniqueVintage-B30mOqbH.cjs +1205 -0
  95. package/dist/uniqueVintage-CFueJOhO.js +1203 -0
  96. package/dist/venaCbd-DHGZy49P.cjs +357 -0
  97. package/dist/venaCbd-T0CqVD4k.js +357 -0
  98. package/dist/westonJonBoucher-BdMzs_Yg.cjs +414 -0
  99. package/dist/westonJonBoucher-b4TCQ4ev.js +414 -0
  100. package/dist/wineEnthusiast-BLGlOjgr.cjs +932 -0
  101. package/dist/wineEnthusiast-BqR0i_54.js +932 -0
  102. package/dist/wolfMattress-CyyO-LoC.js +362 -0
  103. package/dist/wolfMattress-DNGZOivg.cjs +362 -0
  104. package/dist/wolfTactical-3Mm2fvVF.js +341 -0
  105. package/dist/wolfTactical-BmXYlFjr.cjs +341 -0
  106. package/package.json +66 -0
  107. package/src/adapters/amplitude/amplitudeAdapter.ts +454 -0
  108. package/src/adapters/amplitude/index.ts +2 -0
  109. package/src/adapters/amplitude/stubAmplitudeAdapter.ts +34 -0
  110. package/src/adapters/spiffy/commerce/api.ts +596 -0
  111. package/src/adapters/spiffy/commerce/exceptions/sessionExceptions.ts +6 -0
  112. package/src/adapters/spiffy/commerce/exceptions/unsupportedProductExceptions.ts +6 -0
  113. package/src/adapters/spiffy/commerce/graphql.ts +184 -0
  114. package/src/application/config/generalStaticConfig.ts +37 -0
  115. package/src/application/logging/logger.ts +29 -0
  116. package/src/application/models/api/context.ts +4 -0
  117. package/src/application/models/api/generationParams.ts +4 -0
  118. package/src/application/models/api/nextMessageRequest.ts +11 -0
  119. package/src/application/models/api/orgAnalyticsConfig.ts +19 -0
  120. package/src/application/models/api/orgConfigResults.ts +40 -0
  121. package/src/application/models/api/organizationConfig.ts +12 -0
  122. package/src/application/models/api/response.ts +132 -0
  123. package/src/application/models/api/responseGenerics.ts +67 -0
  124. package/src/application/models/api/search.ts +26 -0
  125. package/src/application/models/api/suggestion.ts +4 -0
  126. package/src/application/models/api/supportedEventRequest.ts +8 -0
  127. package/src/application/models/api/userEvent.ts +101 -0
  128. package/src/application/models/cachedValue.ts +8 -0
  129. package/src/application/models/chatElementDisplayLocation.ts +22 -0
  130. package/src/application/models/clientDetails.ts +18 -0
  131. package/src/application/models/colorsConfig.ts +28 -0
  132. package/src/application/models/conversationalSearchIds.ts +5 -0
  133. package/src/application/models/dataLayer.ts +45 -0
  134. package/src/application/models/domMutationContinuation.ts +7 -0
  135. package/src/application/models/domObservationStrategy.ts +9 -0
  136. package/src/application/models/events.ts +5 -0
  137. package/src/application/models/featureGates.ts +23 -0
  138. package/src/application/models/frontendConfig.ts +14 -0
  139. package/src/application/models/googleAnalyticsEvents.ts +8 -0
  140. package/src/application/models/graphql/index.ts +2 -0
  141. package/src/application/models/graphql/queries/getMerchantColorsQuery.ts +37 -0
  142. package/src/application/models/graphql/queries/getMerchantFrontendConfigQuery.ts +103 -0
  143. package/src/application/models/graphql/queries/getMerchantOrgIdQuery.ts +11 -0
  144. package/src/application/models/guards/api/index.ts +12 -0
  145. package/src/application/models/guards/api/isApiFormResponse.ts +90 -0
  146. package/src/application/models/guards/api/isApiFormSubmittedResponseAttributes.ts +37 -0
  147. package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +155 -0
  148. package/src/application/models/guards/api/isApiOrgConfigResults.ts +277 -0
  149. package/src/application/models/guards/api/isApiOrganizationConfig.ts +207 -0
  150. package/src/application/models/guards/api/isApiPDPEventAttributes.ts +21 -0
  151. package/src/application/models/guards/api/isApiPLPEventAttributes.ts +41 -0
  152. package/src/application/models/guards/api/isApiPageResponseAttributes.ts +21 -0
  153. package/src/application/models/guards/api/isApiProductResponseAttributes.ts +85 -0
  154. package/src/application/models/guards/api/isApiProductSearchAttributes.ts +23 -0
  155. package/src/application/models/guards/api/isApiProductSearchFilterAttributes.ts +15 -0
  156. package/src/application/models/guards/api/isApiQueryTypedEventAttributes.ts +4 -0
  157. package/src/application/models/guards/api/isApiResponse.ts +39 -0
  158. package/src/application/models/guards/api/isApiReviewResponseAttributes.ts +30 -0
  159. package/src/application/models/guards/api/isApiReviewRichInformation.ts +37 -0
  160. package/src/application/models/guards/api/isApiSearchEventAttributes.ts +28 -0
  161. package/src/application/models/guards/api/isApiSuggestion.ts +36 -0
  162. package/src/application/models/guards/api/isApiSuggestionClickedEventAttributes.ts +9 -0
  163. package/src/application/models/guards/api/isApiTextResponseAttributes.ts +9 -0
  164. package/src/application/models/guards/api/isApiUserEvent.ts +25 -0
  165. package/src/application/models/guards/graphQL/isGraphQLColorsConfig.ts +50 -0
  166. package/src/application/models/guards/isBaseEcommerceEvent.ts +17 -0
  167. package/src/application/models/guards/isGA4EcommerceEvent.ts +17 -0
  168. package/src/application/models/guards/isLegacyUAEcommerceEvent.ts +17 -0
  169. package/src/application/models/guards/isMobilePLPChatPlacementParameter.ts +11 -0
  170. package/src/application/models/guards/isSpanxTakeAQuizCtaParameter.ts +4 -0
  171. package/src/application/models/guards/isVariantInfo.ts +37 -0
  172. package/src/application/models/guards/utils.ts +43 -0
  173. package/src/application/models/index.ts +20 -0
  174. package/src/application/models/localStorageEventListener.ts +4 -0
  175. package/src/application/models/message.ts +146 -0
  176. package/src/application/models/mobilePLPChatPlacementParameter.ts +3 -0
  177. package/src/application/models/orgsEnum.ts +36 -0
  178. package/src/application/models/productExperiment.ts +5 -0
  179. package/src/application/models/spanxTakeAQuizCtaParameter.ts +4 -0
  180. package/src/application/models/spiffyWidgets.ts +16 -0
  181. package/src/application/models/supportedOrgs.ts +137 -0
  182. package/src/application/models/utilityTypes/camelCase.ts +87 -0
  183. package/src/application/models/utilityTypes/camelCasedPropertiesDeep.ts +80 -0
  184. package/src/application/models/utilityTypes/delimiterCase.ts +121 -0
  185. package/src/application/models/utilityTypes/delimiterCasedPropertiesDeep.ts +98 -0
  186. package/src/application/models/utilityTypes/index.ts +1 -0
  187. package/src/application/models/utilityTypes/internal.ts +93 -0
  188. package/src/application/models/utilityTypes/primitive.ts +8 -0
  189. package/src/application/models/utilityTypes/snakeCasedPropertiesDeep.ts +49 -0
  190. package/src/application/models/utilityTypes/splitWords.ts +76 -0
  191. package/src/application/models/utilityTypes/trim.ts +28 -0
  192. package/src/application/models/utilityTypes/unknownArray.ts +25 -0
  193. package/src/application/models/utils/snakeToCamelTransformer.ts +90 -0
  194. package/src/application/models/utils/stringToFulfillmentDisplayStatusEnumValue.ts +68 -0
  195. package/src/application/models/validators/validateGraphQLColorsConfig.ts +29 -0
  196. package/src/application/models/validators/validateGraphQLFrontendConfig.ts +594 -0
  197. package/src/application/models/validators/validateGraphQLOrgId.ts +7 -0
  198. package/src/application/models/validators/validateMobilePLPChatPlacementParameter.ts +14 -0
  199. package/src/application/models/validators/validateOrgConfigResults.ts +47 -0
  200. package/src/application/models/validators/validateOrganizationConfig.ts +37 -0
  201. package/src/application/models/validators/validateResponse.ts +187 -0
  202. package/src/application/models/validators/validateSuggestion.ts +16 -0
  203. package/src/application/models/validators/validateUserEvent.ts +110 -0
  204. package/src/application/models/variantInfo/index.ts +1 -0
  205. package/src/application/models/variantInfo/pageVisitInfo.ts +6 -0
  206. package/src/application/models/variantInfo/plpInfo.ts +3 -0
  207. package/src/application/models/variantInfo/productInfo.ts +5 -0
  208. package/src/application/models/variantInfo/variantInfo.ts +23 -0
  209. package/src/application/service/cachingService.ts +84 -0
  210. package/src/application/service/cdnService.ts +18 -0
  211. package/src/application/service/customerService/index.ts +8 -0
  212. package/src/application/service/customerService/providers/UnsupportedCustomerService.ts +15 -0
  213. package/src/application/service/customerService/types.ts +31 -0
  214. package/src/application/service/domMutationObserver.ts +320 -0
  215. package/src/application/service/domMutations/GridInsertionService.ts +123 -0
  216. package/src/application/service/domMutations/dataLayer/dataLayerEventsListener.ts +99 -0
  217. package/src/application/service/domMutations/domInsertionService.ts +90 -0
  218. package/src/application/service/domMutations/domMutationListener.ts +15 -0
  219. package/src/application/service/domMutations/domMutationListenerState.ts +52 -0
  220. package/src/application/service/domMutations/floatingChat/embeddedChatsPlacementsListener.ts +41 -0
  221. package/src/application/service/domMutations/gladly/gladlyListener.ts +61 -0
  222. package/src/application/service/domMutations/spiffy/orgs/common/kustomerVisibilityListener.ts +41 -0
  223. package/src/application/service/domMutations/spiffy/orgs/common/orgsCommonDataLayerListener.ts +119 -0
  224. package/src/application/service/environmentService.ts +51 -0
  225. package/src/application/service/featureFlagService.ts +130 -0
  226. package/src/application/service/kustomerIntegrationService.ts +111 -0
  227. package/src/application/service/localStorageService.ts +77 -0
  228. package/src/application/service/pageVariantService.ts +779 -0
  229. package/src/application/service/searchService.ts +140 -0
  230. package/src/application/service/sessionStorageService.ts +27 -0
  231. package/src/application/service/shopifyUrlService.ts +63 -0
  232. package/src/application/service/userIdentityService.ts +114 -0
  233. package/src/application/service/windowChatToggleService.ts +71 -0
  234. package/src/application/service/windowDataLayerService.ts +181 -0
  235. package/src/application/service/windowFrontendConfigService.ts +104 -0
  236. package/src/application/utils/__tests__/divideArrays.test.ts +14 -0
  237. package/src/application/utils/analyticsUtils.ts +110 -0
  238. package/src/application/utils/coreContextToApiContext.ts +11 -0
  239. package/src/application/utils/coreUserEventToApiUserEvent.ts +106 -0
  240. package/src/application/utils/divideArray.ts +7 -0
  241. package/src/application/utils/domObserver.ts +96 -0
  242. package/src/application/utils/elementObserver.ts +246 -0
  243. package/src/application/utils/imageFilter.ts +12 -0
  244. package/src/application/utils/index.ts +3 -0
  245. package/src/application/utils/merchantUtils.ts +16 -0
  246. package/src/application/utils/messageFromFormSubmittedEvent.ts +31 -0
  247. package/src/application/utils/messageFromQueryEvent.ts +38 -0
  248. package/src/application/utils/messageFromResponse.ts +133 -0
  249. package/src/application/utils/messageFromSuggestionEvent.ts +32 -0
  250. package/src/application/utils/mouseEventTypes.ts +1 -0
  251. package/src/application/utils/mutationHelper.ts +51 -0
  252. package/src/application/utils/nextMessageRequestToApiRequest.ts +31 -0
  253. package/src/application/utils/nodeSelector.ts +133 -0
  254. package/src/application/utils/overrides.ts +196 -0
  255. package/src/application/utils/stringUtils.ts +55 -0
  256. package/src/application/utils/supportedEventRequestToApiRequest.ts +12 -0
  257. package/src/application/utils/urlsParser.ts +53 -0
  258. package/src/application/utils/validation.ts +5 -0
  259. package/src/atoms/app/index.ts +57 -0
  260. package/src/atoms/app/variant.ts +261 -0
  261. package/src/atoms/atomStore.ts +34 -0
  262. package/src/atoms/chat/chatState.ts +44 -0
  263. package/src/atoms/chat/form.ts +19 -0
  264. package/src/atoms/chat/index.ts +38 -0
  265. package/src/atoms/chat/lastMessage.ts +11 -0
  266. package/src/atoms/chat/messageQueue.ts +65 -0
  267. package/src/atoms/chat/performanceMetrics.ts +84 -0
  268. package/src/atoms/chat/renderedWidgetRefs.ts +28 -0
  269. package/src/atoms/chat/replies.ts +51 -0
  270. package/src/atoms/chat/suggestions.ts +36 -0
  271. package/src/atoms/globalSearch.ts +12 -0
  272. package/src/atoms/index.ts +5 -0
  273. package/src/atoms/org/customerService.ts +13 -0
  274. package/src/atoms/org/graphqlConfig.ts +27 -0
  275. package/src/atoms/org/index.ts +7 -0
  276. package/src/atoms/org/merchantCss.ts +44 -0
  277. package/src/atoms/org/org.ts +256 -0
  278. package/src/atoms/org/orgAnalyticsConfig.ts +28 -0
  279. package/src/atoms/org/orgPageConfig.ts +38 -0
  280. package/src/atoms/org/orgUIConfig.ts +122 -0
  281. package/src/atoms/search/chatSearch.ts +293 -0
  282. package/src/atoms/search/index.ts +2 -0
  283. package/src/atoms/search/productFilters.ts +207 -0
  284. package/src/atoms/search/productSorter.ts +23 -0
  285. package/src/atoms/search/searchAPI.ts +194 -0
  286. package/src/atoms/search/types.ts +55 -0
  287. package/src/atoms/search/utils.ts +18 -0
  288. package/src/config/divIds.ts +27 -0
  289. package/src/config/locators/components/chat/entrypoints.ts +13 -0
  290. package/src/config/locators/components/chat/index.ts +23 -0
  291. package/src/config/locators/components/chat/preview.ts +13 -0
  292. package/src/config/locators/components/chat/variants/index.ts +16 -0
  293. package/src/config/locators/components/common/buttons.ts +6 -0
  294. package/src/config/locators/components/common/cards.ts +18 -0
  295. package/src/config/locators/components/common/links.ts +1 -0
  296. package/src/config/locators/components/common/tables.ts +2 -0
  297. package/src/config/locators/components/floating-button.ts +2 -0
  298. package/src/config/locators/components/index.ts +3 -0
  299. package/src/config/locators/components/report-issue.ts +21 -0
  300. package/src/config/locators/components/search/index.ts +5 -0
  301. package/src/config/locators/components/shadow-dom.ts +1 -0
  302. package/src/config/locators/embedded.ts +21 -0
  303. package/src/config/locators/index.ts +3 -0
  304. package/src/config/socialProofClasses.ts +17 -0
  305. package/src/contexts/chatContext.tsx +451 -0
  306. package/src/contexts/enviveConfigContext.tsx +70 -0
  307. package/src/contexts/index.ts +4 -0
  308. package/src/contexts/systemSettingsContext.tsx +61 -0
  309. package/src/contexts/types.ts +1059 -0
  310. package/src/enabled-features.ts +83 -0
  311. package/src/events/event-types.ts +11 -0
  312. package/src/events/index.ts +52 -0
  313. package/src/events/registerAnalyticsListeners.ts +49 -0
  314. package/src/extension.ts +63 -0
  315. package/src/hooks/index.ts +22 -0
  316. package/src/hooks/useBlockBackButton.ts +29 -0
  317. package/src/hooks/useChatToggle.ts +66 -0
  318. package/src/hooks/useCustomerSupportHandoff.ts +39 -0
  319. package/src/hooks/useDebounce.ts +17 -0
  320. package/src/hooks/useDynamicVariants.ts +210 -0
  321. package/src/hooks/useElementObserver.ts +245 -0
  322. package/src/hooks/useFileUpload.ts +61 -0
  323. package/src/hooks/useGrabAndScroll.ts +133 -0
  324. package/src/hooks/useHideElements.ts +82 -0
  325. package/src/hooks/useHorizontalScrollAnimation.ts +115 -0
  326. package/src/hooks/useImageResolver.ts +51 -0
  327. package/src/hooks/useIntersection.ts +28 -0
  328. package/src/hooks/useIsSmallScreen.ts +23 -0
  329. package/src/hooks/useMessageFilter.ts +49 -0
  330. package/src/hooks/useMessageScrollObserver.ts +47 -0
  331. package/src/hooks/useReducedMotionWithOverride.ts +15 -0
  332. package/src/hooks/useSearch.tsx +433 -0
  333. package/src/hooks/useSnapCalculator.ts +38 -0
  334. package/src/hooks/useSnapControl.ts +155 -0
  335. package/src/hooks/useSystemSettingsContext.ts +12 -0
  336. package/src/hooks/useTrackComponentVisibleEvent.ts +52 -0
  337. package/src/hooks/useUpdateAnalyticsProps.ts +56 -0
  338. package/src/hooks/utils.ts +153 -0
  339. package/src/index.ts +31 -0
  340. package/src/initialize.ts +163 -0
  341. package/src/interceptors/types.ts +6 -0
  342. package/src/interceptors/useFormEscalation.ts +40 -0
  343. package/src/interceptors/useMessageInterceptor.ts +32 -0
  344. package/src/main.ts +85 -0
  345. package/src/main.tsx +123 -0
  346. package/src/merchants/bandolier/bandolier.ts +1389 -0
  347. package/src/merchants/carpe/carpe.ts +656 -0
  348. package/src/merchants/coterie/coterie.ts +280 -0
  349. package/src/merchants/default.ts +193 -0
  350. package/src/merchants/dreamlandBaby/dreamlandBaby.ts +375 -0
  351. package/src/merchants/fiveCbd/fiveCbd.ts +697 -0
  352. package/src/merchants/forLoveAndLemons/forLoveAndLemons.ts +721 -0
  353. package/src/merchants/greenpan/greenpan.ts +440 -0
  354. package/src/merchants/grooveLife/grooveLife.ts +386 -0
  355. package/src/merchants/homegrownCannabis/homegrownCannabis.ts +468 -0
  356. package/src/merchants/init-merchant.sh +53 -0
  357. package/src/merchants/jackArcher/jackArcher.ts +974 -0
  358. package/src/merchants/jordanCraig/jordanCraig.ts +1927 -0
  359. package/src/merchants/kindredBravely/kindredBravely.ts +529 -0
  360. package/src/merchants/kutFromTheKloth/kutFromTheKloth.ts +418 -0
  361. package/src/merchants/larryAndSerges/larryAndSerges.ts +314 -0
  362. package/src/merchants/leapsAndRebounds/leapsAndRebounds.ts +424 -0
  363. package/src/merchants/longevityrx/longevityrx.ts +368 -0
  364. package/src/merchants/lookOptic/lookOptic.ts +323 -0
  365. package/src/merchants/mantraBrand/mantraBrand.ts +838 -0
  366. package/src/merchants/medterra/medterra.ts +670 -0
  367. package/src/merchants/modells/modells.ts +546 -0
  368. package/src/merchants/pressedFloral/pressedFloral.ts +734 -0
  369. package/src/merchants/skinPerfection/skinPerfection.ts +379 -0
  370. package/src/merchants/snapSupplements/snapSupplements.ts +325 -0
  371. package/src/merchants/spanx/spanx.ts +810 -0
  372. package/src/merchants/spanx/spanxStaging.ts +942 -0
  373. package/src/merchants/supergoop/supergoop.ts +376 -0
  374. package/src/merchants/uniqueVintage/uniqueVintage.ts +1314 -0
  375. package/src/merchants/uniqueVintage/views/useUniqueVintageChatSearch.ts +147 -0
  376. package/src/merchants/venaCbd/venaCbd.ts +410 -0
  377. package/src/merchants/westonJonBoucher/westonJonBoucher.ts +473 -0
  378. package/src/merchants/wineEnthusiast/wineEnthusiast.ts +990 -0
  379. package/src/merchants/wolfMattress/wolfMattress.ts +411 -0
  380. package/src/merchants/wolfTactical/wolfTactical.ts +389 -0
  381. package/src/types/custservice-types.ts +28 -0
  382. package/src/types/search-filter-types.ts +111 -0
  383. package/src/types/suggestionBarV2-types.ts +4 -0
  384. package/src/types/test-types.ts +3 -0
  385. package/src/types.ts +66 -0
@@ -0,0 +1,245 @@
1
+ import { ReactElement, useEffect, useRef, useState } from 'react';
2
+ import { DOMObserver } from 'src/application/utils/domObserver';
3
+ import { ElementObserver } from 'src/application/utils/elementObserver';
4
+ import { MouseEventTypes } from 'src/application/utils/mouseEventTypes';
5
+ import { NodeSelector } from 'src/application/utils/nodeSelector';
6
+
7
+ export interface ElementObserverUtility {
8
+ onChange: (fn: (el: HTMLElement | null) => void) => void;
9
+ onAdd: (fn: (el: HTMLElement | null) => void) => void;
10
+ onRemove: (fn: (el: HTMLElement | null) => void) => void;
11
+ onClassChange: (fn: (classes?: DOMTokenList) => void) => void;
12
+ onClassAdded: (className: string, fn: () => void) => void;
13
+ onClassRemoved: (className: string, fn: () => void) => void;
14
+ onAddChild: (fn: (nodes?: DOMTokenList) => void) => void;
15
+ onRemoveChild: (fn: (nodes?: DOMTokenList) => void) => void;
16
+ onEvent: <K extends keyof HTMLElementEventMap>(
17
+ event: K,
18
+ fn: EventListenerOrEventListenerObject,
19
+ ) => void;
20
+ blockRendering: () => void;
21
+ unblockRendering: () => void;
22
+ exists: () => boolean;
23
+ isRendered: () => boolean;
24
+ render: (fn: () => ReactElement) => ReactElement | undefined;
25
+ fire: (event: MouseEventTypes) => void;
26
+ show: () => void;
27
+ hide: () => void;
28
+ applyStyle: (styles: React.CSSProperties) => void;
29
+ targetNode: HTMLElement | null;
30
+ }
31
+
32
+ /*
33
+ * This hook enables the connection between a `MutationObserver` and a React.js component.
34
+ * It abstracts all the necessary validations and implementations to modify HTML elements,
35
+ * monitor their changes, and interact with them. The only required input is the element's selector.
36
+ *
37
+ * How to use it:
38
+ *
39
+ * Let's assume we want to interact with the following element:
40
+ * `<div id="awesome-content"><content /></div>`
41
+ *
42
+ * To hook into that element, simply do the following:
43
+ * `const element = useElementObserver(SelectorFactory.id("awesome-content"))`
44
+ *
45
+ * With the `ElementObserver` instance in hand, we can interact with the HTML element without having
46
+ * to manage its presence in the DOM. The `onAdd` and `onRemove` functions can be used to determine
47
+ * whether the element is present in the DOM or not, and to ensure interaction occurs only when necessary.
48
+ *
49
+ * @param selector
50
+ * @returns
51
+ */
52
+ export const useElementObserver = (selector: NodeSelector): ElementObserverUtility => {
53
+ const INITIAL_RENDER_STATE = true;
54
+ const eoRef = useRef<ElementObserver>(DOMObserver.add(selector));
55
+ const [renderBlocked, setRenderBlocked] = useState(INITIAL_RENDER_STATE);
56
+
57
+ /**
58
+ * Fired every time the HTML element changes.
59
+ *
60
+ * @param fn
61
+ */
62
+ const onChange = (fn: (el: HTMLElement | null) => void) => {
63
+ eoRef.current?.registerOnChange(fn);
64
+ };
65
+
66
+ /**
67
+ * Fired when the HTML element is added to the DOM.
68
+ *
69
+ * @param fn
70
+ */
71
+ const onAdd = (fn: (el: HTMLElement | null) => void) => {
72
+ eoRef.current?.registerOnAdd(fn);
73
+ };
74
+
75
+ /**
76
+ * Fired when the HTML element is removed from the DOM.
77
+ *
78
+ * @param fn
79
+ */
80
+ const onRemove = (fn: (el: HTMLElement | null) => void) => {
81
+ eoRef.current?.registerOnRemove(fn);
82
+ };
83
+
84
+ /**
85
+ * Fired when the class of the HTML element changes.
86
+ *
87
+ * @param fn
88
+ */
89
+ const onClassChange = (fn: (classes?: DOMTokenList) => void) => {
90
+ eoRef.current?.registerOnclassChange(fn);
91
+ };
92
+
93
+ /**
94
+ * Fired when a class is added to the HTML element.
95
+ *
96
+ * @param className
97
+ * @param fn
98
+ */
99
+ const onClassAdded = (className: string, fn: () => void) => {
100
+ eoRef.current?.registerOnClassAdded(className, fn);
101
+ };
102
+
103
+ /**
104
+ * Fired when a class is removed from the HTML element.
105
+ *
106
+ * @param className
107
+ * @param fn
108
+ */
109
+ const onClassRemoved = (className: string, fn: () => void) => {
110
+ eoRef.current?.registerOnClassRemoved(className, fn);
111
+ };
112
+
113
+ /**
114
+ * Fired when a child element is added to the HTML element.
115
+ *
116
+ * @param fn
117
+ */
118
+ const onAddChild = (fn: (nodes?: DOMTokenList) => void) => {
119
+ eoRef.current?.registerOnAddChild(fn);
120
+ };
121
+
122
+ /**
123
+ * Fired when a child element is removed from the HTML element.
124
+ *
125
+ * @param fn
126
+ */
127
+ const onRemoveChild = (fn: (nodes?: DOMTokenList) => void) => {
128
+ eoRef.current?.registerOnRemoveChild(fn);
129
+ };
130
+
131
+ /**
132
+ * Allows hooking event listeners to the HTML element, such as `focus`, `blur`, etc.
133
+ *
134
+ * @param event
135
+ * @param fn
136
+ */
137
+ const onEvent = <K extends keyof HTMLElementEventMap>(
138
+ event: K,
139
+ fn: EventListenerOrEventListenerObject,
140
+ ) => {
141
+ eoRef.current.registerEvent(event, fn);
142
+ };
143
+
144
+ /**
145
+ * Useful when rendering a React.js component inside the HTML element.
146
+ *
147
+ * @param fn
148
+ * @returns
149
+ */
150
+ const render = (fn: () => ReactElement) => {
151
+ if (!renderBlocked) {
152
+ return eoRef.current.render(fn);
153
+ }
154
+ };
155
+
156
+ /**
157
+ * Checks if the element exists in the DOM.
158
+ *
159
+ * @returns
160
+ */
161
+ const exists = () => !!eoRef.current.getNode();
162
+
163
+ /**
164
+ * Checks if rendering is unblocked.
165
+ *
166
+ * @returns
167
+ */
168
+ const isRendered = () => !renderBlocked;
169
+
170
+ /**
171
+ * Triggers an event for the HTML element.
172
+ *
173
+ * @param event
174
+ */
175
+ const fire = (event: MouseEventTypes) => {
176
+ eoRef.current.fire(event);
177
+ };
178
+
179
+ /**
180
+ * Shows the HTML element.
181
+ *
182
+ * @returns
183
+ */
184
+ const show = () => eoRef.current.show();
185
+
186
+ /**
187
+ * Hides the HTML element.
188
+ *
189
+ * @returns
190
+ */
191
+ const hide = () => eoRef.current.hide();
192
+
193
+ /**
194
+ * Blocks the rendering of elements.
195
+ *
196
+ * @returns
197
+ */
198
+ const blockRendering = () => setRenderBlocked(true);
199
+
200
+ /**
201
+ * Unblocks the rendering of elements.
202
+ *
203
+ * @returns
204
+ */
205
+ const unblockRendering = () => setRenderBlocked(false);
206
+
207
+ /**
208
+ * Applies CSS styles to the HTML element.
209
+ *
210
+ * @param styles
211
+ */
212
+ const applyStyle = (styles: React.CSSProperties) => {
213
+ const node = eoRef?.current?.getNode();
214
+ node && Object.assign(node.style, styles);
215
+ };
216
+
217
+ useEffect(() => {
218
+ eoRef.current.init();
219
+ eoRef.current.registerOnReset(() => setRenderBlocked(INITIAL_RENDER_STATE));
220
+ DOMObserver.observe();
221
+ return () => DOMObserver.remove(selector);
222
+ }, [selector.getPattern()]);
223
+
224
+ return {
225
+ targetNode: eoRef.current.getNode(),
226
+ onChange,
227
+ onAdd,
228
+ onRemove,
229
+ onClassChange,
230
+ onClassAdded,
231
+ onClassRemoved,
232
+ onAddChild,
233
+ onRemoveChild,
234
+ onEvent,
235
+ blockRendering,
236
+ unblockRendering,
237
+ exists,
238
+ isRendered,
239
+ render,
240
+ fire,
241
+ show,
242
+ hide,
243
+ applyStyle,
244
+ } satisfies ElementObserverUtility;
245
+ };
@@ -0,0 +1,61 @@
1
+ import { AttachmentRequest } from '@spiffy-ai/commerce-api-client';
2
+ import { useAtom, useAtomValue } from 'jotai';
3
+ import { useRef, useState } from 'react';
4
+ import { customerServiceAttachment } from 'src/atoms';
5
+ import { orgCustomerServiceService } from 'src/atoms/org/orgUIConfig';
6
+
7
+ export const useFileUpload = () => {
8
+ const fileInputRef = useRef(null);
9
+ const [attachment, setAttachment] = useAtom(customerServiceAttachment);
10
+ const customerServiceImpl = useAtomValue(orgCustomerServiceService);
11
+ const [error, setError] = useState('');
12
+
13
+ const isValidFile = (file?: File) => {
14
+ const maxSize = customerServiceImpl.maxfileUploadSize;
15
+ if (maxSize && (file?.size || 0) > maxSize) {
16
+ const strFileSize = `${(maxSize / 1000000).toFixed(1)}MB`;
17
+ setError(`Exceeded file size of ${strFileSize}`);
18
+ return false;
19
+ }
20
+ return true;
21
+ };
22
+
23
+ const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
24
+ const file = event?.target?.files?.[0];
25
+
26
+ if (file && isValidFile(file)) {
27
+ const reader = new FileReader();
28
+ reader.readAsDataURL(file);
29
+ reader.onloadend = () => {
30
+ const base64 = reader?.result as string;
31
+ setAttachment({
32
+ base64_content: base64,
33
+ content_type: file.type,
34
+ file_name: file.name,
35
+ });
36
+ };
37
+ }
38
+ };
39
+
40
+ const prepareAttachment = (): AttachmentRequest | undefined => {
41
+ if (attachment) {
42
+ return {
43
+ ...attachment,
44
+ base64_content: attachment?.base64_content.split(',')[1],
45
+ } as AttachmentRequest;
46
+ }
47
+ };
48
+
49
+ const handleFileRemove = () => {
50
+ setAttachment(undefined);
51
+ };
52
+
53
+ return {
54
+ error,
55
+ fileInputRef,
56
+ attachment,
57
+ handleFileSelect,
58
+ handleFileRemove,
59
+ prepareAttachment,
60
+ };
61
+ };
@@ -0,0 +1,133 @@
1
+ import { useRef, useState } from "react";
2
+
3
+ type ArrowPosition = 'lt' | 'ct' | 'rt'
4
+
5
+ interface AnimationProps {
6
+ element: HTMLElement
7
+ targetScroll: number
8
+ duration: number
9
+ direction?: 'lt' | 'rt'
10
+ multiply?: number
11
+ offset?: number
12
+ callback?: (position: ArrowPosition) => void
13
+ }
14
+
15
+ const animateHorizontalScroll = ({ element, duration, targetScroll, multiply = 1, direction, callback, offset = 0 }: AnimationProps) => {
16
+ const start = element.scrollLeft
17
+ const distance = (targetScroll - start) * multiply;
18
+ const startTime = performance.now();
19
+
20
+ function easeOutSine(x: number): number {
21
+ return Math.sin((x * Math.PI) / 2);
22
+ }
23
+
24
+ function scrollStep(currentTime: number): void {
25
+ const timeElapsed = currentTime - startTime;
26
+ const progress = Math.min(timeElapsed / duration, 1);
27
+ const easing = easeOutSine(progress);
28
+ const step = start + (distance * easing)
29
+ const canScroll = (direction === 'rt' ? (element.scrollLeft < step) : (element.scrollLeft > step)) || !direction
30
+
31
+ if (step > 0 && canScroll) {
32
+ element.scrollTo(step, 0)
33
+ }
34
+
35
+ if (timeElapsed < duration) {
36
+ requestAnimationFrame(scrollStep);
37
+ }
38
+
39
+ // End of scrolling container
40
+ else if ((element.scrollLeft + offset) === element.scrollWidth) {
41
+ callback?.('rt')
42
+ }
43
+ // Begin of scrolling container
44
+ else if (element.scrollLeft <= 1) {
45
+ callback?.('lt')
46
+ }
47
+ // Somewhere in the middle
48
+ else {
49
+ callback?.('ct')
50
+ }
51
+
52
+ }
53
+
54
+ requestAnimationFrame(scrollStep);
55
+ }
56
+
57
+
58
+ export const useGrabAndScroll = (enabled: boolean, chunkWidth: number, speed: number = 400, offset: number = 0) => {
59
+ const containerRef = useRef<HTMLDivElement>(null)
60
+ const [ leftArrow, setLeftArrow ] = useState(false)
61
+ const [ rightArrow, setRightArrow ] = useState(true)
62
+
63
+ const handleArrows = (position: ArrowPosition) => {
64
+ switch (position) {
65
+ case 'lt':
66
+ setLeftArrow(false)
67
+ setRightArrow(true)
68
+ break
69
+
70
+ case 'rt':
71
+ setLeftArrow(true)
72
+ setRightArrow(false)
73
+ break
74
+
75
+ default:
76
+ setLeftArrow(true)
77
+ setRightArrow(true)
78
+ }
79
+ }
80
+
81
+ const animationTrigger = () => {
82
+ if (enabled && containerRef.current) {
83
+ const dist = (containerRef?.current?.scrollLeft || 0) / chunkWidth
84
+ const targetScroll = chunkWidth * (Math.floor(dist) + (dist % 1 > 0.5 ? 1 : 0))
85
+ animateHorizontalScroll({
86
+ element: containerRef.current,
87
+ targetScroll,
88
+ duration: speed,
89
+ offset,
90
+ callback: handleArrows
91
+ })
92
+ }
93
+ }
94
+
95
+ const onNext = (cardsToSlide: number) => {
96
+ if (containerRef.current) {
97
+ const targetScroll = containerRef.current.scrollLeft + chunkWidth
98
+ animateHorizontalScroll({
99
+ element: containerRef.current,
100
+ targetScroll,
101
+ duration: speed,
102
+ direction: 'rt',
103
+ multiply: cardsToSlide,
104
+ offset,
105
+ callback: handleArrows
106
+ })
107
+ }
108
+ }
109
+
110
+ const onPrevious = (cardsToSlide: number) => {
111
+ if (containerRef.current) {
112
+ const targetScroll = containerRef.current.scrollLeft - chunkWidth
113
+ animateHorizontalScroll({
114
+ element: containerRef.current,
115
+ targetScroll,
116
+ duration: speed,
117
+ direction: 'lt',
118
+ multiply: cardsToSlide,
119
+ offset,
120
+ callback: handleArrows
121
+ })
122
+ }
123
+ }
124
+
125
+ return {
126
+ containerRef,
127
+ leftArrow,
128
+ rightArrow,
129
+ animationTrigger,
130
+ onNext,
131
+ onPrevious
132
+ }
133
+ }
@@ -0,0 +1,82 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { SelectorFactory } from 'src/application/utils/nodeSelector';
3
+ import { useEffect, useRef, useState } from 'react';
4
+ import { useAtomValue } from 'jotai';
5
+ import { SpiffyEventName } from 'src/application/models/events';
6
+ import { ElementObserver } from 'src/application/utils/elementObserver';
7
+ import { DOMObserver } from 'src/application/utils/domObserver';
8
+ import { orgUIConfigAtom } from 'src/atoms/org/orgUIConfig';
9
+
10
+ /**
11
+ * This hook hides/shows elements that could overlap with the chat when the chat is opened/closed.
12
+ */
13
+ export const useHideElements = () => {
14
+ const orgUIConfig = useAtomValue(orgUIConfigAtom);
15
+ const [hasRegisteredEventListeners, setHasRegisteredEventListeners] = useState(false);
16
+ const elementObservers = useRef<ElementObserver[]>([]);
17
+
18
+ const hideElements = (elements: ElementObserver[]) => {
19
+ elements.forEach((element) => {
20
+ element.hide();
21
+ });
22
+ };
23
+
24
+ const showElements = (elements: ElementObserver[]) => {
25
+ elements.forEach((element) => {
26
+ element.show();
27
+ });
28
+ };
29
+
30
+ useEffect(() => {
31
+ elementObservers.current = [];
32
+ orgUIConfig.hiddenElementSelectors?.forEach((selector) => {
33
+ elementObservers.current.push(DOMObserver.add(SelectorFactory.query(selector)));
34
+ });
35
+ // since we're not using useElementObserver, we need to manually start the observer
36
+ elementObservers.current.forEach((element) => {
37
+ element.init();
38
+ });
39
+ DOMObserver.observe();
40
+
41
+ return () => {
42
+ elementObservers.current.forEach((element) => {
43
+ element.destroy();
44
+ });
45
+ elementObservers.current = [];
46
+ };
47
+ }, [orgUIConfig.hiddenElementSelectors]);
48
+
49
+ useEffect(() => {
50
+ if (elementObservers.current.length > 0 && !hasRegisteredEventListeners) {
51
+ Logger.logDebug(
52
+ `Registering ${SpiffyEventName.WidgetOpen} event for ${elementObservers.current.length} elements`,
53
+ );
54
+ window.addEventListener(SpiffyEventName.WidgetOpen, () =>
55
+ hideElements(elementObservers.current),
56
+ );
57
+
58
+ Logger.logDebug(
59
+ `Registering ${SpiffyEventName.WidgetClose} event for ${elementObservers.current.length} elements`,
60
+ );
61
+ window.addEventListener(SpiffyEventName.WidgetClose, () =>
62
+ showElements(elementObservers.current),
63
+ );
64
+
65
+ setHasRegisteredEventListeners(true);
66
+ }
67
+
68
+ return () => {
69
+ if (elementObservers.current.length > 0 && hasRegisteredEventListeners) {
70
+ window.removeEventListener(SpiffyEventName.WidgetOpen, () =>
71
+ hideElements(elementObservers.current),
72
+ );
73
+
74
+ window.removeEventListener(SpiffyEventName.WidgetClose, () =>
75
+ showElements(elementObservers.current),
76
+ );
77
+
78
+ setHasRegisteredEventListeners(false);
79
+ }
80
+ };
81
+ }, [hasRegisteredEventListeners]);
82
+ };
@@ -0,0 +1,115 @@
1
+ import { useEffect, RefObject, useRef } from 'react';
2
+ import { useReducedMotionWithOverride } from 'src/hooks/useReducedMotionWithOverride';
3
+
4
+ // IMPORTANT: All refs passed to this hook must be mutable (not Readonly)
5
+ interface UseHorizontalScrollAnimationOptions {
6
+ scrollContainerRef: RefObject<HTMLDivElement>;
7
+ animationSpeed?: 'standard' | 'slow' | 'none';
8
+ }
9
+
10
+ // When using this hook, ensure that scrouuContainerRef is not undefined. It is allowed to
11
+ // prevent issued elsewhere in the codebase, but it should be dealt with whenever you use this hook.
12
+ export function useHorizontalScrollAnimation({
13
+ scrollContainerRef,
14
+ animationSpeed = 'standard',
15
+ }: UseHorizontalScrollAnimationOptions): void {
16
+ const reducedMotion = useReducedMotionWithOverride();
17
+ const resumeTimeoutRef = useRef<NodeJS.Timeout | null>(null);
18
+ const scrollAnimationRef = useRef<number | null>(null);
19
+
20
+ const pauseOnHover = true; // This is unlikely to need to be configurable but I'm throwing it up here just in case.
21
+
22
+ // Animation constants: time-based
23
+ let PIXELS_PER_SECOND = 40;
24
+ switch (animationSpeed) {
25
+ case 'standard':
26
+ PIXELS_PER_SECOND = 40;
27
+ break;
28
+ case 'slow':
29
+ PIXELS_PER_SECOND = 25;
30
+ break;
31
+ case 'none':
32
+ PIXELS_PER_SECOND = 0;
33
+ break;
34
+ default:
35
+ PIXELS_PER_SECOND = 40;
36
+ }
37
+ const RESUME_DELAY_MS = 2000;
38
+ const isAnimated = animationSpeed !== 'none';
39
+
40
+ useEffect(() => {
41
+ if (!isAnimated || reducedMotion || !scrollContainerRef) return;
42
+
43
+ const container = scrollContainerRef.current;
44
+ if (!container) return;
45
+ if (container.scrollWidth <= container.clientWidth) return;
46
+
47
+ let isPaused = false;
48
+ let lastTimestamp: number | null = null;
49
+ let accumulatedScroll = 0; // Accumulate fractional pixels
50
+
51
+ const step = (timestamp: number) => {
52
+ if (lastTimestamp === null) lastTimestamp = timestamp;
53
+ if (!isPaused) {
54
+ const delta = timestamp - lastTimestamp;
55
+ lastTimestamp = timestamp;
56
+
57
+ // Accumulate the scroll amount (including fractional pixels)
58
+ accumulatedScroll += PIXELS_PER_SECOND * (delta / 1000);
59
+
60
+ // Only apply whole pixel movements
61
+ const pixelsToScroll = Math.floor(accumulatedScroll);
62
+ if (pixelsToScroll > 0) {
63
+ container.scrollLeft += pixelsToScroll;
64
+ accumulatedScroll -= pixelsToScroll; // Keep the fractional remainder
65
+
66
+ if (container.scrollLeft >= container.scrollWidth - container.clientWidth) {
67
+ container.scrollLeft = 0; // Reset scroll to create a looping effect
68
+ accumulatedScroll = 0; // Reset accumulated scroll when looping
69
+ }
70
+ }
71
+ }
72
+ scrollAnimationRef.current = requestAnimationFrame(step);
73
+ };
74
+
75
+ // Start animation
76
+ scrollAnimationRef.current = requestAnimationFrame(step);
77
+
78
+ // Pause/resume logic
79
+ const pauseAnimation = () => {
80
+ isPaused = true;
81
+ if (resumeTimeoutRef.current) {
82
+ clearTimeout(resumeTimeoutRef.current);
83
+ resumeTimeoutRef.current = null;
84
+ }
85
+ };
86
+ const scheduleResumeAnimation = () => {
87
+ resumeTimeoutRef.current = setTimeout(() => {
88
+ isPaused = false;
89
+ lastTimestamp = null; // Reset timestamp so delta is correct after pause
90
+ }, RESUME_DELAY_MS);
91
+ };
92
+
93
+ if (pauseOnHover) {
94
+ container.addEventListener('mouseenter', pauseAnimation);
95
+ container.addEventListener('mouseleave', scheduleResumeAnimation);
96
+ container.addEventListener('touchstart', pauseAnimation);
97
+ container.addEventListener('touchend', scheduleResumeAnimation);
98
+ }
99
+
100
+ return () => {
101
+ if (scrollAnimationRef.current) {
102
+ cancelAnimationFrame(scrollAnimationRef.current);
103
+ }
104
+ if (pauseOnHover) {
105
+ container.removeEventListener('mouseenter', pauseAnimation);
106
+ container.removeEventListener('mouseleave', scheduleResumeAnimation);
107
+ container.removeEventListener('touchstart', pauseAnimation);
108
+ container.removeEventListener('touchend', scheduleResumeAnimation);
109
+ }
110
+ if (resumeTimeoutRef.current) {
111
+ clearTimeout(resumeTimeoutRef.current);
112
+ }
113
+ };
114
+ }, [isAnimated, reducedMotion, PIXELS_PER_SECOND, pauseOnHover, scrollContainerRef]);
115
+ }
@@ -0,0 +1,51 @@
1
+ import { useAtomValue } from 'jotai';
2
+ import { OrgShortName } from 'src/application/models';
3
+ import { orgUIConfigAtom } from 'src/atoms/org/orgUIConfig';
4
+
5
+ abstract class ImageResolver {
6
+ abstract resolve(url: string, size: number): string;
7
+ }
8
+
9
+ class MerchantImageResolver {
10
+ private static imageResolverMap = new Map<string, ImageResolver>();
11
+
12
+ private static loadMapping() {
13
+ if (this.imageResolverMap.size === 0) {
14
+ this.imageResolverMap.set(OrgShortName.Spanx, new ShopifyImageResolver());
15
+ this.imageResolverMap.set(OrgShortName.SpanxStaging, new ShopifyImageResolver());
16
+ this.imageResolverMap.set(OrgShortName.UniqueVintage, new ShopifyImageResolver());
17
+ }
18
+ return this.imageResolverMap;
19
+ }
20
+
21
+ static get(name: string) {
22
+ return this.loadMapping().get(name);
23
+ }
24
+ }
25
+
26
+ class ShopifyImageResolver extends ImageResolver {
27
+ resolve(url: string, size: number): string {
28
+ const pattern = /_\d+x\.jpg/;
29
+ const urlHasPrefix = pattern.test(url);
30
+ const newSizePrefix = `_${size}x.jpg`;
31
+ if (urlHasPrefix) {
32
+ return url.replace(pattern, newSizePrefix);
33
+ }
34
+ return url.replace('.jpg', newSizePrefix);
35
+ }
36
+ }
37
+
38
+ export const useImageResolver = () => {
39
+ const orgUIConfig = useAtomValue(orgUIConfigAtom);
40
+ const resolve = (image?: string, size?: number) => {
41
+ if (image && size) {
42
+ const newImagePath = MerchantImageResolver.get(orgUIConfig.shortName)?.resolve(image, size);
43
+ return newImagePath || image;
44
+ }
45
+ return image;
46
+ };
47
+
48
+ return {
49
+ resolve,
50
+ };
51
+ };