@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,104 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { EnvironmentService } from 'src/application/service/environmentService';
3
+ import { applyOverrides, Override } from 'src/application/utils/overrides';
4
+ import { getAtomStore } from 'src/atoms/atomStore';
5
+ import { colorsConfigAtom, frontendConfigAtom } from 'src/atoms/org/graphqlConfig';
6
+ import { orgPageConfigAtom } from 'src/atoms/org/orgPageConfig';
7
+ import { orgUIConfigAtom } from 'src/atoms/org/orgUIConfig';
8
+ import { ColorMapping, OrgUIConfig, WidgetConfig, WidgetMountingConfig } from 'src/contexts/types';
9
+
10
+ type CustomizableFrontendConfig = {
11
+ orgUIConfig: OrgUIConfig;
12
+ colorsConfig: ColorMapping;
13
+ mountingConfigs: Record<string, WidgetMountingConfig>;
14
+ widgetConfigs: Record<string, WidgetConfig>;
15
+ };
16
+ type FrontendConfigOverrides = Override<CustomizableFrontendConfig>;
17
+
18
+ class WindowFrontendConfigService {
19
+ private static instance: WindowFrontendConfigService | undefined;
20
+
21
+ private isInitialized = false;
22
+
23
+ static getSingletonInstance(): WindowFrontendConfigService {
24
+ if (!this.instance) {
25
+ this.instance = new WindowFrontendConfigService();
26
+ }
27
+ return this.instance;
28
+ }
29
+
30
+ init() {
31
+ if (this.isInitialized) return;
32
+ this.isInitialized = true;
33
+
34
+ try {
35
+ const identifyingPrefix = EnvironmentService.getIdentifyingPrefix();
36
+ if (!window[identifyingPrefix]) {
37
+ window[identifyingPrefix] = {} as Window[typeof identifyingPrefix];
38
+ }
39
+
40
+ // Attach an explicit apply function to be called by the host page
41
+ const apply = (overrides: FrontendConfigOverrides[]) => {
42
+ Logger.logDebug('[spiffy-ai] applyFrontendConfigOverrides invoked');
43
+ WindowFrontendConfigService.applyOverridesToFrontendConfig(overrides);
44
+ };
45
+
46
+ window[identifyingPrefix].applyFrontendConfigOverrides = apply;
47
+
48
+ Logger.logDebug('[spiffy-ai] FrontendConfig overrides apply function attached');
49
+ } catch (e) {
50
+ Logger.logError('[spiffy-ai] Failed to attach FrontendConfig overrides apply function', e);
51
+ }
52
+ }
53
+
54
+ private static async applyOverridesToFrontendConfig(overrides: FrontendConfigOverrides[]) {
55
+ const atomStore = getAtomStore();
56
+ try {
57
+ // Get the current frontend config(s) from the atoms once
58
+ const frontendConfig = await atomStore.get(frontendConfigAtom);
59
+ // Get the orgPageConfig for widgetConfigs and mountingConfigs, but don't write to it
60
+ // If you use frontendConfigAtom to get the mountingConfigs and widgetConfigs,
61
+ // you will get {}, which are not the full version of the mountingConfigs and widgetConfigs
62
+ const orgPageConfig = await atomStore.get(orgPageConfigAtom);
63
+
64
+ let currentConfig: CustomizableFrontendConfig = {
65
+ orgUIConfig: atomStore.get(orgUIConfigAtom),
66
+ colorsConfig: (await atomStore.get(colorsConfigAtom)) as ColorMapping,
67
+ mountingConfigs: orgPageConfig.mountingConfigs,
68
+ widgetConfigs: orgPageConfig.widgetConfigs,
69
+ };
70
+
71
+ Logger.logInfo('[spiffy-ai] base config', {
72
+ baseConfig: currentConfig,
73
+ });
74
+
75
+ for (let i = 0; i < overrides.length; i += 1) {
76
+ const currentOverride = overrides[i];
77
+ currentConfig = applyOverrides<CustomizableFrontendConfig>(currentConfig, currentOverride, {
78
+ arrayStrategy: 'mergeByIndex',
79
+ });
80
+ }
81
+
82
+ // Update the atoms with the new frontend config(s)
83
+ atomStore.set(orgUIConfigAtom, currentConfig.orgUIConfig);
84
+ atomStore.set(colorsConfigAtom, currentConfig.colorsConfig);
85
+ // Write to the frontendConfigAtom to update the mountingConfigs and widgetConfigs
86
+ // This is the only way to update the mountingConfigs and widgetConfigs
87
+ // because the orgPageConfigAtom write only updates the pageVariants
88
+ atomStore.set(frontendConfigAtom, {
89
+ ...frontendConfig,
90
+ mountingConfigs: currentConfig.mountingConfigs,
91
+ widgetConfigs: currentConfig.widgetConfigs,
92
+ });
93
+
94
+ Logger.logInfo('[spiffy-ai] atoms updated via overrides', {
95
+ currentConfig,
96
+ });
97
+ } catch (e) {
98
+ Logger.logError('[spiffy-ai] Failed to apply FrontendConfig overrides', e);
99
+ }
100
+ }
101
+ }
102
+
103
+ export const initWindowFrontendConfigOverridesApplyFunction = () =>
104
+ WindowFrontendConfigService.getSingletonInstance().init();
@@ -0,0 +1,14 @@
1
+ // import { expect } from 'vitest';
2
+ // import { divideArray } from '../divideArray';
3
+
4
+ // describe('divideArray', () => {
5
+ // it('should divide an array into 3 rows', () => {
6
+ // const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
7
+ // const rows = divideArray(array, 3);
8
+ // expect(rows).toEqual([
9
+ // [1, 4, 7, 10],
10
+ // [2, 5, 8],
11
+ // [3, 6, 9],
12
+ // ]);
13
+ // });
14
+ // });
@@ -0,0 +1,110 @@
1
+ import Logger from 'src/application/logging/logger';
2
+ import { AmplitudeAdapter, SpiffyMetricsEventName } from 'src/adapters/amplitude';
3
+ import {
4
+ isLegacyUAEcommerceEvent,
5
+ isGA4EcommerceEvent,
6
+ isBaseEcommerceEvent,
7
+ BaseEcommerceEvent,
8
+ } from 'src/application/models';
9
+
10
+ const NORMALIZED_ADD_TO_CART_EVENT_NAMES = ['addtocart', 'addedtocart'];
11
+ const CHECK_DATA_LAYER_INTERVAL_MS = 500;
12
+ const CHECK_DATA_LAYER_MAX_ATTEMPTS = 10;
13
+
14
+ /**
15
+ * Checks if a Google Analytics event is an add_to_cart event.
16
+ *
17
+ * @param event The event name to check.
18
+ *
19
+ * @returns True if the event is an add_to_cart event, false otherwise.
20
+ */
21
+ const isAddToCartEvent = (event: string): boolean => {
22
+ const normalizedEvent = event.replace(/[-_]/g, '').toLowerCase();
23
+ return NORMALIZED_ADD_TO_CART_EVENT_NAMES.some((name) => normalizedEvent.includes(name));
24
+ };
25
+
26
+ /**
27
+ * Tracks an add_to_cart event in Amplitude.
28
+ *
29
+ * @param event The event to track.
30
+ */
31
+ const handleAddToCartEvent = (event: BaseEcommerceEvent) => {
32
+ let eventProps: Record<string, unknown>;
33
+
34
+ if (isLegacyUAEcommerceEvent(event)) {
35
+ eventProps = {
36
+ items: event.ecommerce.add.products.map((product) => ({
37
+ item_name: product.name,
38
+ item_category: product.category,
39
+ price: product.price,
40
+ quantity: product.quantity,
41
+ })),
42
+ currency: event.ecommerce.add.currencyCode,
43
+ event_format_version: 'legacy_universal_analytics',
44
+ };
45
+ } else if (isGA4EcommerceEvent(event)) {
46
+ eventProps = {
47
+ items: event.ecommerce.items.map((item) => ({
48
+ item_name: item.item_name,
49
+ item_category: item.item_category,
50
+ price: item.price,
51
+ quantity: item.quantity,
52
+ })),
53
+ currency: event.ecommerce.currency,
54
+ event_format_version: 'google_analytics_4',
55
+ };
56
+ } else {
57
+ eventProps = {
58
+ event_properties: { ...event },
59
+ event_format_version: 'unknown',
60
+ };
61
+ }
62
+
63
+ AmplitudeAdapter.trackEvent({
64
+ eventName: SpiffyMetricsEventName.AddToCartClicked,
65
+ eventProps,
66
+ });
67
+ };
68
+
69
+ /**
70
+ * Wraps the window.dataLayer.push method to intercept add_to_cart events and send them to Amplitude.
71
+ * This function runs on an interval until the dataLayer is available.
72
+ */
73
+ export const initDataLayerWrapper = () => {
74
+ let attempts = 0;
75
+
76
+ // currently, our bundle is always loaded after GTM has initialized GA/dataLayer
77
+ // we'll need this check here if/when we load spiffy before/outside of GTM
78
+ const checkAndInitialize = () => {
79
+ if (!window.dataLayer || !window.dataLayer.push) {
80
+ attempts += 1;
81
+
82
+ if (attempts >= CHECK_DATA_LAYER_MAX_ATTEMPTS) {
83
+ Logger.logDebug(
84
+ `[spiffy-ai] dataLayer not available after ${CHECK_DATA_LAYER_MAX_ATTEMPTS} attempts`,
85
+ );
86
+ return;
87
+ }
88
+
89
+ setTimeout(checkAndInitialize, CHECK_DATA_LAYER_INTERVAL_MS);
90
+ return;
91
+ }
92
+
93
+ Logger.logDebug('[spiffy-ai] dataLayer is available, wrapping push function...');
94
+ const originalPush = window.dataLayer.push;
95
+ window.dataLayer.push = (args: unknown) => {
96
+ if (isBaseEcommerceEvent(args) && isAddToCartEvent(args.event)) {
97
+ handleAddToCartEvent(args);
98
+ }
99
+
100
+ // IMPORTANT: call the original push method so that the event is still logged to GA
101
+ originalPush.apply(window.dataLayer, [args]);
102
+ };
103
+ };
104
+
105
+ checkAndInitialize();
106
+ };
107
+
108
+ export const initAmplitude = () => {
109
+ AmplitudeAdapter.getSingletonInstanceOf();
110
+ };
@@ -0,0 +1,11 @@
1
+ import { Context as ApiContext } from '@spiffy-ai/commerce-api-client';
2
+ import { Context as CoreContext } from '../models';
3
+
4
+ export const coreContextToApiContext = (context: CoreContext): ApiContext => ({
5
+ chat_id: context.chatId,
6
+ org_id: context.orgId,
7
+ user_id: context.userId,
8
+ org_short_name: context.orgShortName,
9
+ source: context.source,
10
+ env: context.env,
11
+ });
@@ -0,0 +1,106 @@
1
+ import {
2
+ UserEvent as ApiUserEvent,
3
+ PLPAttributeCategory,
4
+ PLPIdAttributes,
5
+ UserEventCategory,
6
+ } from '@spiffy-ai/commerce-api-client';
7
+ import { UserEvent as CoreUserEvent } from '../models';
8
+
9
+ export const coreUserEventToApiUserEvent = (data: CoreUserEvent): ApiUserEvent => {
10
+ if (
11
+ data.category === UserEventCategory.PdpVisit ||
12
+ data.category === UserEventCategory.AddToCart
13
+ ) {
14
+ return {
15
+ event_id: data.eventId,
16
+ created_at: data.createdAt,
17
+ category: data.category,
18
+ attributes: {
19
+ product_id: data.attributes.productId,
20
+ parent_product_id: data.attributes.parentProductId,
21
+ url: data.attributes.url,
22
+ },
23
+ };
24
+ }
25
+
26
+ if (data.category === UserEventCategory.PlpVisit) {
27
+ return {
28
+ event_id: data.eventId,
29
+ created_at: data.createdAt,
30
+ category: data.category,
31
+ attributes: {
32
+ category: PLPAttributeCategory.Id,
33
+ attributes: {
34
+ // we're only handling id attributes for now
35
+ id: (data.attributes.attributes as PLPIdAttributes).id,
36
+ },
37
+ },
38
+ };
39
+ }
40
+
41
+ if (data.category === UserEventCategory.QueryTyped) {
42
+ return {
43
+ event_id: data.eventId,
44
+ created_at: data.createdAt,
45
+ category: data.category,
46
+ attributes: {
47
+ query: data.attributes.query,
48
+ },
49
+ };
50
+ }
51
+
52
+ if (data.category === UserEventCategory.Search) {
53
+ return {
54
+ event_id: data.eventId,
55
+ created_at: data.createdAt,
56
+ category: data.category,
57
+ attributes: {
58
+ search_term: data.attributes.searchTerm,
59
+ selected_filters: data.attributes.selectedFilters,
60
+ },
61
+ };
62
+ }
63
+
64
+ if (data.category === UserEventCategory.SuggestionClicked) {
65
+ return {
66
+ event_id: data.eventId,
67
+ created_at: data.createdAt,
68
+ category: data.category,
69
+ attributes: {
70
+ suggestion_id: data.attributes.suggestionId,
71
+ },
72
+ };
73
+ }
74
+
75
+ if (data.category === UserEventCategory.PageVisit) {
76
+ return {
77
+ event_id: data.eventId,
78
+ created_at: data.createdAt,
79
+ category: data.category,
80
+ attributes: {
81
+ url: data.attributes.url,
82
+ page_visit_category: data.attributes.pageVisitCategory,
83
+ },
84
+ };
85
+ }
86
+
87
+ if (data.category === UserEventCategory.FormSubmitted) {
88
+ return {
89
+ event_id: data.eventId,
90
+ created_at: data.createdAt,
91
+ category: data.category,
92
+ attributes: {
93
+ filled_schema: { ...data.attributes.filledSchema },
94
+ form_response_id: data.attributes.formResponseId,
95
+ form_type: data.attributes.formType,
96
+ },
97
+ };
98
+ }
99
+
100
+ // This is the default app_loaded event, when we start to use app_unloaded we need to handle it here
101
+ return {
102
+ event_id: data.eventId,
103
+ created_at: data.createdAt,
104
+ category: data.category,
105
+ };
106
+ };
@@ -0,0 +1,7 @@
1
+ export const divideArray = <T>(array: T[], size: number) => {
2
+ const rows = [];
3
+ for (let i = 0; i < size; i += 1) {
4
+ rows.push(array.filter((_: T, index: number) => index % size === i));
5
+ }
6
+ return rows;
7
+ };
@@ -0,0 +1,96 @@
1
+ import { ElementObserver } from './elementObserver';
2
+ import { NodeSelector } from './nodeSelector';
3
+
4
+ /**
5
+ * This class manages a `MutationObserver` for the `document.body`. It monitors nodes that are
6
+ * added or removed from the DOM and notifies the observers for each observable element
7
+ * when they are ready to be watched.
8
+ *
9
+ * How that works:
10
+ *
11
+ * When the `observe` function is called, it attaches a `MutationObserver` to the `document.body` element.
12
+ * All changes to `document.body` are tracked by this observer. When changes are detected, the `MutationObserver`
13
+ * iterates through all added and removed nodes and checks if each node exists in the main map of observable elements.
14
+ *
15
+ * If a match is found, it notifies the corresponding `ElementObserver` instance to start monitoring changes for that element.
16
+ *
17
+ * The primary `MutationObserver` remains active until the `destroy` function is called. Calling `destroy` will disconnect all
18
+ * individual element observers and release any associated resources.
19
+ *
20
+ * There is no need to explicitly call the `observe` function. It is automatically invoked when a `useElementObserver` hook is instantiated.
21
+ *
22
+ */
23
+ export class DOMObserver {
24
+ private static documentObserver: MutationObserver | null;
25
+ private static listeners: Map<string, ElementObserver> = new Map();
26
+
27
+ private static notifyAdition(records: MutationRecord[], nodeMapping: Map<Node, ElementObserver>) {
28
+ records.forEach((record) =>
29
+ record.addedNodes.forEach((node) => {
30
+ nodeMapping.get(node)?.watch(node);
31
+ nodeMapping.delete(node);
32
+ }),
33
+ );
34
+ }
35
+
36
+ private static notifyRemoval(records: MutationRecord[], nodeMapping: Map<Node, ElementObserver>) {
37
+ records.forEach((record) =>
38
+ record.removedNodes.forEach((node) => {
39
+ nodeMapping.get(node)?.destroy();
40
+ nodeMapping.delete(node);
41
+ }),
42
+ );
43
+ }
44
+
45
+ private static notifyChildrenChanges(nodeMapping: Map<Node, ElementObserver>) {
46
+ Array.from(nodeMapping.values()).forEach((observer) => observer.synchronize());
47
+ }
48
+
49
+ private static mapNodeInstancesToListeners(): Map<Node, ElementObserver> {
50
+ const nodeMapping = new Map<Node, ElementObserver>();
51
+ this.listeners.forEach((observer) => {
52
+ const node = observer.getNode() || observer.getSelector()?.parse();
53
+ if (node) {
54
+ nodeMapping.set(node, observer);
55
+ }
56
+ });
57
+ return nodeMapping;
58
+ }
59
+
60
+ static add(selector: NodeSelector): ElementObserver {
61
+ const node = selector.getPattern();
62
+ let elementObserver = this.listeners.get(node);
63
+ if (elementObserver) {
64
+ return elementObserver;
65
+ }
66
+
67
+ elementObserver = new ElementObserver(selector);
68
+ this.listeners.set(node, elementObserver);
69
+ return elementObserver;
70
+ }
71
+
72
+ static remove(selector: NodeSelector) {
73
+ const node = selector.getPattern();
74
+ this.listeners.get(node)?.destroy();
75
+ this.listeners.delete(node);
76
+ }
77
+
78
+ static observe() {
79
+ if (!this.documentObserver) {
80
+ this.documentObserver = new MutationObserver((mutations) => {
81
+ const nodeMapping = this.mapNodeInstancesToListeners();
82
+ this.notifyAdition(mutations, nodeMapping);
83
+ this.notifyRemoval(mutations, nodeMapping);
84
+ this.notifyChildrenChanges(nodeMapping);
85
+ });
86
+ this.documentObserver.observe(document.body, { childList: true, subtree: true });
87
+ }
88
+ }
89
+
90
+ static destroy() {
91
+ this.documentObserver?.disconnect();
92
+ this.listeners.forEach((listener) => listener.destroy());
93
+ this.listeners.clear();
94
+ this.documentObserver = null;
95
+ }
96
+ }
@@ -0,0 +1,246 @@
1
+ import { ReactElement } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { MouseEventTypes } from './mouseEventTypes';
4
+ import { MutationHelper } from './mutationHelper';
5
+ import { NodeSelector } from './nodeSelector';
6
+
7
+ /**
8
+ * The `ElementObserver` class monitors all changes related to the specific element it is responsible for.
9
+ * It is automatically instantiated by the `useElementObserver` hook, which manages all instances of this class.
10
+ *
11
+ * How it works:
12
+ *
13
+ * Each instance of `ElementObserver` holds several pieces of information related to the node it is monitoring,
14
+ * such as the node's selector and its `MutationObserver`. This class is responsible for translating raw node
15
+ * changes into a more user-friendly format. For example, when the node is added to the DOM, the `ElementObserver`
16
+ * is notified by the `DOMObserver`, which triggers the `onAdd` callback function on the respective `useElementObserver`
17
+ * instance.
18
+ *
19
+ * Instances of `ElementObserver` are fully managed by the `useElementObserver` hook, requiring no additional configuration.
20
+ * When the hook is destroyed, its corresponding `ElementObserver` instance is automatically removed.
21
+ *
22
+ */
23
+ export class ElementObserver {
24
+ private mutation: MutationObserver | null;
25
+
26
+ private node: HTMLElement | null;
27
+
28
+ private selector: NodeSelector;
29
+
30
+ private eventListeners: Map<keyof HTMLElementEventMap, EventListenerOrEventListenerObject>;
31
+
32
+ private classAdded: Map<string, () => void>;
33
+
34
+ private classRemoved: Map<string, () => void>;
35
+
36
+ private nodeDisplay: string;
37
+
38
+ private addFn: (el: HTMLElement | null) => void;
39
+
40
+ private removeFn: (el: HTMLElement | null) => void;
41
+
42
+ private changeFn: (el: HTMLElement | null) => void;
43
+
44
+ private classChangeFn: (classes?: DOMTokenList) => void;
45
+
46
+ private addChildFn?: () => void;
47
+
48
+ private removeChildFn?: () => void;
49
+
50
+ private resetFn: () => void;
51
+
52
+ constructor(selector: NodeSelector) {
53
+ this.selector = selector;
54
+ this.mutation = null;
55
+ this.node = null;
56
+ this.eventListeners = new Map();
57
+ this.classAdded = new Map();
58
+ this.classRemoved = new Map();
59
+ this.nodeDisplay = 'block';
60
+ this.addFn = () => {};
61
+ this.removeFn = () => {};
62
+ this.changeFn = () => {};
63
+ this.classChangeFn = () => {};
64
+ this.addChildFn = undefined;
65
+ this.removeChildFn = undefined;
66
+ this.resetFn = () => {};
67
+ }
68
+
69
+ private handleAttributes(mutationRecord: MutationRecord[]) {
70
+ mutationRecord.filter(MutationHelper.isClass).forEach((mutation) => {
71
+ MutationHelper.fireClassChange(mutation, this.node, this.classChangeFn);
72
+ MutationHelper.fireClassAdded(mutation, this.node, this.classAdded);
73
+ MutationHelper.fireClassRemoved(mutation, this.node, this.classRemoved);
74
+ });
75
+ }
76
+
77
+ private handleChildList(mutationRecord: MutationRecord[]) {
78
+ if (!this.addChildFn && !this.removeChildFn) {
79
+ return;
80
+ }
81
+
82
+ mutationRecord.filter(MutationHelper.isChildList).forEach((mutation) => {
83
+ MutationHelper.fireSubtreeChange(mutation.addedNodes, this.addChildFn);
84
+ MutationHelper.fireSubtreeChange(mutation.removedNodes, this.removeChildFn);
85
+ });
86
+ }
87
+
88
+ private addInitialListeners() {
89
+ this.eventListeners.forEach((v, k) => this.node?.addEventListener(k, v));
90
+ }
91
+
92
+ private clearEventListeners() {
93
+ this.eventListeners.forEach((v, k) => this.node?.removeEventListener(k, v));
94
+ this.eventListeners.clear();
95
+ }
96
+
97
+ private renew(newNode: Node | null) {
98
+ if (newNode) {
99
+ this.destroy();
100
+ this.watch(newNode);
101
+ }
102
+ }
103
+
104
+ getSelector() {
105
+ return this.selector;
106
+ }
107
+
108
+ getNode() {
109
+ return this.node;
110
+ }
111
+
112
+ synchronize() {
113
+ const parsedNode = this.getSelector().parse();
114
+
115
+ if (this.getNode() !== parsedNode) {
116
+ this.renew(parsedNode);
117
+ return;
118
+ }
119
+
120
+ if (!this.getNode() && parsedNode) {
121
+ this.watch(parsedNode);
122
+ return;
123
+ }
124
+
125
+ if (this.getNode() && !parsedNode) {
126
+ this.destroy();
127
+ }
128
+ }
129
+
130
+ init() {
131
+ const node = this.getSelector().parse();
132
+ if (node) {
133
+ this.watch(node);
134
+ }
135
+ }
136
+
137
+ watch(node: Node) {
138
+ if (!this.mutation) {
139
+ this.mutation = new MutationObserver((mutationRecord) => {
140
+ this.handleAttributes(mutationRecord);
141
+ this.handleChildList(mutationRecord);
142
+ this.changeFn(this.node);
143
+ });
144
+ this.mutation.observe(node, {
145
+ childList: true,
146
+ subtree: true,
147
+ attributes: true,
148
+ attributeOldValue: true,
149
+ });
150
+ this.node = node as HTMLElement;
151
+ this.addFn(this.node);
152
+ this.addInitialListeners();
153
+ }
154
+ }
155
+
156
+ destroy() {
157
+ this.resetFn();
158
+ this.removeFn(this.node);
159
+ this.mutation?.disconnect();
160
+ this.clearEventListeners();
161
+ this.classAdded.clear();
162
+ this.classRemoved.clear();
163
+ this.mutation = null;
164
+ this.node = null;
165
+ }
166
+
167
+ render(fn: () => ReactElement) {
168
+ if (this.node) {
169
+ return createPortal(fn(), this.node);
170
+ }
171
+ }
172
+
173
+ fire(event: MouseEventTypes) {
174
+ if (this.node) {
175
+ this.node.dispatchEvent(
176
+ new MouseEvent(event, {
177
+ bubbles: true,
178
+ cancelable: true,
179
+ view: window,
180
+ }),
181
+ );
182
+ }
183
+ }
184
+
185
+ show() {
186
+ const display = this.node?.style.display;
187
+ if (this.node && display === 'none') {
188
+ this.node.style.display = this.nodeDisplay || 'block';
189
+ }
190
+ }
191
+
192
+ hide() {
193
+ const display = this.node?.style.display;
194
+ if (this.node && display !== 'none') {
195
+ this.nodeDisplay = display || 'block';
196
+ this.node.style.display = 'none';
197
+ }
198
+ }
199
+
200
+ registerEvent(event: keyof HTMLElementEventMap, fn: EventListenerOrEventListenerObject) {
201
+ if (this.node) {
202
+ const previousFn = this.eventListeners.get(event);
203
+ if (previousFn) {
204
+ this.node.removeEventListener(event, previousFn);
205
+ }
206
+ this.node.addEventListener(event, fn);
207
+ }
208
+ this.eventListeners.set(event, fn);
209
+ }
210
+
211
+ registerOnReset(fn: () => void) {
212
+ this.resetFn = fn;
213
+ }
214
+
215
+ registerOnChange(fn: (el: HTMLElement | null) => void) {
216
+ this.changeFn = fn;
217
+ }
218
+
219
+ registerOnAdd(fn: (el: HTMLElement | null) => void) {
220
+ this.addFn = fn;
221
+ }
222
+
223
+ registerOnRemove(fn: (el: HTMLElement | null) => void) {
224
+ this.removeFn = fn;
225
+ }
226
+
227
+ registerOnclassChange(fn: (classes?: DOMTokenList) => void) {
228
+ this.classChangeFn = fn;
229
+ }
230
+
231
+ registerOnAddChild(fn: (classes?: DOMTokenList) => void) {
232
+ this.addChildFn = fn;
233
+ }
234
+
235
+ registerOnRemoveChild(fn: (classes?: DOMTokenList) => void) {
236
+ this.removeChildFn = fn;
237
+ }
238
+
239
+ registerOnClassAdded(className: string, fn: () => void) {
240
+ this.classAdded.set(className, fn);
241
+ }
242
+
243
+ registerOnClassRemoved(className: string, fn: () => void) {
244
+ this.classRemoved.set(className, fn);
245
+ }
246
+ }