@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.
- package/LICENSE +2 -0
- package/README.md +2 -0
- package/dist/GridInsertionService-CEYo9pGj.js +22 -0
- package/dist/GridInsertionService-CS_bnPh0.cjs +28 -0
- package/dist/bandolier-Ble8jEa8.js +1221 -0
- package/dist/bandolier-Bm2xAt_j.cjs +1221 -0
- package/dist/carpe-Da7b-LCW.cjs +599 -0
- package/dist/carpe-W13mhRRP.js +597 -0
- package/dist/cdnService-CZ-aXcY6.cjs +23 -0
- package/dist/cdnService-zQfKk3Eb.js +18 -0
- package/dist/chatElementDisplayLocation-CX8fuNao.d.cts +239 -0
- package/dist/chatElementDisplayLocation-CwptS9tx.d.ts +239 -0
- package/dist/chunk-CUT6urMc.cjs +30 -0
- package/dist/contexts/index.cjs +13 -0
- package/dist/contexts/index.d.cts +65 -0
- package/dist/contexts/index.d.ts +66 -0
- package/dist/contexts/index.js +7 -0
- package/dist/contexts-BRjfVq_k.js +5064 -0
- package/dist/contexts-BYArqZtK.cjs +5164 -0
- package/dist/coterie-3y0D9ko4.cjs +229 -0
- package/dist/coterie-DOWcJAYv.js +229 -0
- package/dist/custservice-types-CFIFwZ-r.js +10 -0
- package/dist/custservice-types-CkfxZiHY.cjs +16 -0
- package/dist/default-C2fEZKXk.js +175 -0
- package/dist/default-CBUq6Q6G.cjs +4 -0
- package/dist/default-CGIFZK6m.js +4 -0
- package/dist/default-D_KPZdPJ.cjs +198 -0
- package/dist/divIds-Bss-btao.js +49 -0
- package/dist/divIds-DnZNd7rA.cjs +223 -0
- package/dist/dreamlandBaby-DCIsuU9R.cjs +338 -0
- package/dist/dreamlandBaby-DvSaZGrz.js +338 -0
- package/dist/entrypoints-D_JUvkgy.cjs +18 -0
- package/dist/entrypoints-YLQsbBRD.js +6 -0
- package/dist/enviveConfigContext-CUGLpPGU.js +34 -0
- package/dist/enviveConfigContext-Dfr2VH6u.cjs +48 -0
- package/dist/fiveCbd-B1SESMCO.js +605 -0
- package/dist/fiveCbd-CkOlVby_.cjs +605 -0
- package/dist/forLoveAndLemons-CfYPMnKS.cjs +660 -0
- package/dist/forLoveAndLemons-DmwYZIk0.js +658 -0
- package/dist/greenpan-Bsl3ir59.cjs +389 -0
- package/dist/greenpan-BtOi45lf.js +389 -0
- package/dist/grooveLife-6_dtYsRk.js +334 -0
- package/dist/grooveLife-Cmm1PSCL.cjs +334 -0
- package/dist/homegrownCannabis-C-kw-74X.js +400 -0
- package/dist/homegrownCannabis-CO0uY_mp.cjs +400 -0
- package/dist/hooks/index.cjs +16 -0
- package/dist/hooks/index.d.cts +357 -0
- package/dist/hooks/index.d.ts +357 -0
- package/dist/hooks/index.js +7 -0
- package/dist/jackArcher-CLVmwwpI.js +719 -0
- package/dist/jackArcher-DdYTIzAV.cjs +719 -0
- package/dist/jordanCraig-Am-Oor-O.js +1778 -0
- package/dist/jordanCraig-_u3-w4Hp.cjs +1778 -0
- package/dist/kindredBravely-CWovIDSc.cjs +482 -0
- package/dist/kindredBravely-eWp-ud_E.js +482 -0
- package/dist/kutFromTheKloth-BMV4BuGQ.js +361 -0
- package/dist/kutFromTheKloth-Q589bAOC.cjs +361 -0
- package/dist/larryAndSerges-BMUlTgI-.js +252 -0
- package/dist/larryAndSerges-CEau764j.cjs +252 -0
- package/dist/leapsAndRebounds-DGMzPO7T.js +352 -0
- package/dist/leapsAndRebounds-DHAtRTJD.cjs +352 -0
- package/dist/logger-Dln20ans.cjs +25 -0
- package/dist/logger-pdEEY8T2.js +19 -0
- package/dist/longevityrx-CZW8Hxzi.cjs +312 -0
- package/dist/longevityrx-jH2JLhNH.js +312 -0
- package/dist/lookOptic-BGXP5P_V.js +274 -0
- package/dist/lookOptic-CA6RwLbG.cjs +274 -0
- package/dist/mantraBrand-Cm9_PBCT.js +742 -0
- package/dist/mantraBrand-DByNqpnL.cjs +742 -0
- package/dist/medterra-B0wxj_PV.js +575 -0
- package/dist/medterra-DnPN2ksU.cjs +575 -0
- package/dist/modells-Bmz8Ag5M.js +476 -0
- package/dist/modells-CoYgkLSp.cjs +476 -0
- package/dist/models-DHdb7QWn.js +51 -0
- package/dist/models-ixxUsGL_.cjs +69 -0
- package/dist/pressedFloral-DSKs_oVG.js +653 -0
- package/dist/pressedFloral-DjBiSoUl.cjs +653 -0
- package/dist/skinPerfection-B_3xzVNS.cjs +326 -0
- package/dist/skinPerfection-IDrBuAPt.js +326 -0
- package/dist/snapSupplements-BJk5T5ba.js +277 -0
- package/dist/snapSupplements-BStTsdOZ.cjs +277 -0
- package/dist/socialProofClasses-Bhv2Vulz.js +9 -0
- package/dist/socialProofClasses-CrQBWdSA.cjs +39 -0
- package/dist/spanx-BYg0LE7R.js +653 -0
- package/dist/spanx-LwU1zSzq.cjs +655 -0
- package/dist/spanxStaging-CfSmuKYB.js +837 -0
- package/dist/spanxStaging-OZLV9qix.cjs +840 -0
- package/dist/suggestionBarV2-types-BllzwsBD.js +34 -0
- package/dist/suggestionBarV2-types-CaovchMP.cjs +46 -0
- package/dist/supergoop-BqPXDnKk.cjs +327 -0
- package/dist/supergoop-CIlrHND_.js +325 -0
- package/dist/types-C4T5UOIW.cjs +230 -0
- package/dist/types-CYNvLeSA.js +176 -0
- package/dist/uniqueVintage-B30mOqbH.cjs +1205 -0
- package/dist/uniqueVintage-CFueJOhO.js +1203 -0
- package/dist/venaCbd-DHGZy49P.cjs +357 -0
- package/dist/venaCbd-T0CqVD4k.js +357 -0
- package/dist/westonJonBoucher-BdMzs_Yg.cjs +414 -0
- package/dist/westonJonBoucher-b4TCQ4ev.js +414 -0
- package/dist/wineEnthusiast-BLGlOjgr.cjs +932 -0
- package/dist/wineEnthusiast-BqR0i_54.js +932 -0
- package/dist/wolfMattress-CyyO-LoC.js +362 -0
- package/dist/wolfMattress-DNGZOivg.cjs +362 -0
- package/dist/wolfTactical-3Mm2fvVF.js +341 -0
- package/dist/wolfTactical-BmXYlFjr.cjs +341 -0
- package/package.json +66 -0
- package/src/adapters/amplitude/amplitudeAdapter.ts +454 -0
- package/src/adapters/amplitude/index.ts +2 -0
- package/src/adapters/amplitude/stubAmplitudeAdapter.ts +34 -0
- package/src/adapters/spiffy/commerce/api.ts +596 -0
- package/src/adapters/spiffy/commerce/exceptions/sessionExceptions.ts +6 -0
- package/src/adapters/spiffy/commerce/exceptions/unsupportedProductExceptions.ts +6 -0
- package/src/adapters/spiffy/commerce/graphql.ts +184 -0
- package/src/application/config/generalStaticConfig.ts +37 -0
- package/src/application/logging/logger.ts +29 -0
- package/src/application/models/api/context.ts +4 -0
- package/src/application/models/api/generationParams.ts +4 -0
- package/src/application/models/api/nextMessageRequest.ts +11 -0
- package/src/application/models/api/orgAnalyticsConfig.ts +19 -0
- package/src/application/models/api/orgConfigResults.ts +40 -0
- package/src/application/models/api/organizationConfig.ts +12 -0
- package/src/application/models/api/response.ts +132 -0
- package/src/application/models/api/responseGenerics.ts +67 -0
- package/src/application/models/api/search.ts +26 -0
- package/src/application/models/api/suggestion.ts +4 -0
- package/src/application/models/api/supportedEventRequest.ts +8 -0
- package/src/application/models/api/userEvent.ts +101 -0
- package/src/application/models/cachedValue.ts +8 -0
- package/src/application/models/chatElementDisplayLocation.ts +22 -0
- package/src/application/models/clientDetails.ts +18 -0
- package/src/application/models/colorsConfig.ts +28 -0
- package/src/application/models/conversationalSearchIds.ts +5 -0
- package/src/application/models/dataLayer.ts +45 -0
- package/src/application/models/domMutationContinuation.ts +7 -0
- package/src/application/models/domObservationStrategy.ts +9 -0
- package/src/application/models/events.ts +5 -0
- package/src/application/models/featureGates.ts +23 -0
- package/src/application/models/frontendConfig.ts +14 -0
- package/src/application/models/googleAnalyticsEvents.ts +8 -0
- package/src/application/models/graphql/index.ts +2 -0
- package/src/application/models/graphql/queries/getMerchantColorsQuery.ts +37 -0
- package/src/application/models/graphql/queries/getMerchantFrontendConfigQuery.ts +103 -0
- package/src/application/models/graphql/queries/getMerchantOrgIdQuery.ts +11 -0
- package/src/application/models/guards/api/index.ts +12 -0
- package/src/application/models/guards/api/isApiFormResponse.ts +90 -0
- package/src/application/models/guards/api/isApiFormSubmittedResponseAttributes.ts +37 -0
- package/src/application/models/guards/api/isApiOrderResponseAttributes.ts +155 -0
- package/src/application/models/guards/api/isApiOrgConfigResults.ts +277 -0
- package/src/application/models/guards/api/isApiOrganizationConfig.ts +207 -0
- package/src/application/models/guards/api/isApiPDPEventAttributes.ts +21 -0
- package/src/application/models/guards/api/isApiPLPEventAttributes.ts +41 -0
- package/src/application/models/guards/api/isApiPageResponseAttributes.ts +21 -0
- package/src/application/models/guards/api/isApiProductResponseAttributes.ts +85 -0
- package/src/application/models/guards/api/isApiProductSearchAttributes.ts +23 -0
- package/src/application/models/guards/api/isApiProductSearchFilterAttributes.ts +15 -0
- package/src/application/models/guards/api/isApiQueryTypedEventAttributes.ts +4 -0
- package/src/application/models/guards/api/isApiResponse.ts +39 -0
- package/src/application/models/guards/api/isApiReviewResponseAttributes.ts +30 -0
- package/src/application/models/guards/api/isApiReviewRichInformation.ts +37 -0
- package/src/application/models/guards/api/isApiSearchEventAttributes.ts +28 -0
- package/src/application/models/guards/api/isApiSuggestion.ts +36 -0
- package/src/application/models/guards/api/isApiSuggestionClickedEventAttributes.ts +9 -0
- package/src/application/models/guards/api/isApiTextResponseAttributes.ts +9 -0
- package/src/application/models/guards/api/isApiUserEvent.ts +25 -0
- package/src/application/models/guards/graphQL/isGraphQLColorsConfig.ts +50 -0
- package/src/application/models/guards/isBaseEcommerceEvent.ts +17 -0
- package/src/application/models/guards/isGA4EcommerceEvent.ts +17 -0
- package/src/application/models/guards/isLegacyUAEcommerceEvent.ts +17 -0
- package/src/application/models/guards/isMobilePLPChatPlacementParameter.ts +11 -0
- package/src/application/models/guards/isSpanxTakeAQuizCtaParameter.ts +4 -0
- package/src/application/models/guards/isVariantInfo.ts +37 -0
- package/src/application/models/guards/utils.ts +43 -0
- package/src/application/models/index.ts +20 -0
- package/src/application/models/localStorageEventListener.ts +4 -0
- package/src/application/models/message.ts +146 -0
- package/src/application/models/mobilePLPChatPlacementParameter.ts +3 -0
- package/src/application/models/orgsEnum.ts +36 -0
- package/src/application/models/productExperiment.ts +5 -0
- package/src/application/models/spanxTakeAQuizCtaParameter.ts +4 -0
- package/src/application/models/spiffyWidgets.ts +16 -0
- package/src/application/models/supportedOrgs.ts +137 -0
- package/src/application/models/utilityTypes/camelCase.ts +87 -0
- package/src/application/models/utilityTypes/camelCasedPropertiesDeep.ts +80 -0
- package/src/application/models/utilityTypes/delimiterCase.ts +121 -0
- package/src/application/models/utilityTypes/delimiterCasedPropertiesDeep.ts +98 -0
- package/src/application/models/utilityTypes/index.ts +1 -0
- package/src/application/models/utilityTypes/internal.ts +93 -0
- package/src/application/models/utilityTypes/primitive.ts +8 -0
- package/src/application/models/utilityTypes/snakeCasedPropertiesDeep.ts +49 -0
- package/src/application/models/utilityTypes/splitWords.ts +76 -0
- package/src/application/models/utilityTypes/trim.ts +28 -0
- package/src/application/models/utilityTypes/unknownArray.ts +25 -0
- package/src/application/models/utils/snakeToCamelTransformer.ts +90 -0
- package/src/application/models/utils/stringToFulfillmentDisplayStatusEnumValue.ts +68 -0
- package/src/application/models/validators/validateGraphQLColorsConfig.ts +29 -0
- package/src/application/models/validators/validateGraphQLFrontendConfig.ts +594 -0
- package/src/application/models/validators/validateGraphQLOrgId.ts +7 -0
- package/src/application/models/validators/validateMobilePLPChatPlacementParameter.ts +14 -0
- package/src/application/models/validators/validateOrgConfigResults.ts +47 -0
- package/src/application/models/validators/validateOrganizationConfig.ts +37 -0
- package/src/application/models/validators/validateResponse.ts +187 -0
- package/src/application/models/validators/validateSuggestion.ts +16 -0
- package/src/application/models/validators/validateUserEvent.ts +110 -0
- package/src/application/models/variantInfo/index.ts +1 -0
- package/src/application/models/variantInfo/pageVisitInfo.ts +6 -0
- package/src/application/models/variantInfo/plpInfo.ts +3 -0
- package/src/application/models/variantInfo/productInfo.ts +5 -0
- package/src/application/models/variantInfo/variantInfo.ts +23 -0
- package/src/application/service/cachingService.ts +84 -0
- package/src/application/service/cdnService.ts +18 -0
- package/src/application/service/customerService/index.ts +8 -0
- package/src/application/service/customerService/providers/UnsupportedCustomerService.ts +15 -0
- package/src/application/service/customerService/types.ts +31 -0
- package/src/application/service/domMutationObserver.ts +320 -0
- package/src/application/service/domMutations/GridInsertionService.ts +123 -0
- package/src/application/service/domMutations/dataLayer/dataLayerEventsListener.ts +99 -0
- package/src/application/service/domMutations/domInsertionService.ts +90 -0
- package/src/application/service/domMutations/domMutationListener.ts +15 -0
- package/src/application/service/domMutations/domMutationListenerState.ts +52 -0
- package/src/application/service/domMutations/floatingChat/embeddedChatsPlacementsListener.ts +41 -0
- package/src/application/service/domMutations/gladly/gladlyListener.ts +61 -0
- package/src/application/service/domMutations/spiffy/orgs/common/kustomerVisibilityListener.ts +41 -0
- package/src/application/service/domMutations/spiffy/orgs/common/orgsCommonDataLayerListener.ts +119 -0
- package/src/application/service/environmentService.ts +51 -0
- package/src/application/service/featureFlagService.ts +130 -0
- package/src/application/service/kustomerIntegrationService.ts +111 -0
- package/src/application/service/localStorageService.ts +77 -0
- package/src/application/service/pageVariantService.ts +779 -0
- package/src/application/service/searchService.ts +140 -0
- package/src/application/service/sessionStorageService.ts +27 -0
- package/src/application/service/shopifyUrlService.ts +63 -0
- package/src/application/service/userIdentityService.ts +114 -0
- package/src/application/service/windowChatToggleService.ts +71 -0
- package/src/application/service/windowDataLayerService.ts +181 -0
- package/src/application/service/windowFrontendConfigService.ts +104 -0
- package/src/application/utils/__tests__/divideArrays.test.ts +14 -0
- package/src/application/utils/analyticsUtils.ts +110 -0
- package/src/application/utils/coreContextToApiContext.ts +11 -0
- package/src/application/utils/coreUserEventToApiUserEvent.ts +106 -0
- package/src/application/utils/divideArray.ts +7 -0
- package/src/application/utils/domObserver.ts +96 -0
- package/src/application/utils/elementObserver.ts +246 -0
- package/src/application/utils/imageFilter.ts +12 -0
- package/src/application/utils/index.ts +3 -0
- package/src/application/utils/merchantUtils.ts +16 -0
- package/src/application/utils/messageFromFormSubmittedEvent.ts +31 -0
- package/src/application/utils/messageFromQueryEvent.ts +38 -0
- package/src/application/utils/messageFromResponse.ts +133 -0
- package/src/application/utils/messageFromSuggestionEvent.ts +32 -0
- package/src/application/utils/mouseEventTypes.ts +1 -0
- package/src/application/utils/mutationHelper.ts +51 -0
- package/src/application/utils/nextMessageRequestToApiRequest.ts +31 -0
- package/src/application/utils/nodeSelector.ts +133 -0
- package/src/application/utils/overrides.ts +196 -0
- package/src/application/utils/stringUtils.ts +55 -0
- package/src/application/utils/supportedEventRequestToApiRequest.ts +12 -0
- package/src/application/utils/urlsParser.ts +53 -0
- package/src/application/utils/validation.ts +5 -0
- package/src/atoms/app/index.ts +57 -0
- package/src/atoms/app/variant.ts +261 -0
- package/src/atoms/atomStore.ts +34 -0
- package/src/atoms/chat/chatState.ts +44 -0
- package/src/atoms/chat/form.ts +19 -0
- package/src/atoms/chat/index.ts +38 -0
- package/src/atoms/chat/lastMessage.ts +11 -0
- package/src/atoms/chat/messageQueue.ts +65 -0
- package/src/atoms/chat/performanceMetrics.ts +84 -0
- package/src/atoms/chat/renderedWidgetRefs.ts +28 -0
- package/src/atoms/chat/replies.ts +51 -0
- package/src/atoms/chat/suggestions.ts +36 -0
- package/src/atoms/globalSearch.ts +12 -0
- package/src/atoms/index.ts +5 -0
- package/src/atoms/org/customerService.ts +13 -0
- package/src/atoms/org/graphqlConfig.ts +27 -0
- package/src/atoms/org/index.ts +7 -0
- package/src/atoms/org/merchantCss.ts +44 -0
- package/src/atoms/org/org.ts +256 -0
- package/src/atoms/org/orgAnalyticsConfig.ts +28 -0
- package/src/atoms/org/orgPageConfig.ts +38 -0
- package/src/atoms/org/orgUIConfig.ts +122 -0
- package/src/atoms/search/chatSearch.ts +293 -0
- package/src/atoms/search/index.ts +2 -0
- package/src/atoms/search/productFilters.ts +207 -0
- package/src/atoms/search/productSorter.ts +23 -0
- package/src/atoms/search/searchAPI.ts +194 -0
- package/src/atoms/search/types.ts +55 -0
- package/src/atoms/search/utils.ts +18 -0
- package/src/config/divIds.ts +27 -0
- package/src/config/locators/components/chat/entrypoints.ts +13 -0
- package/src/config/locators/components/chat/index.ts +23 -0
- package/src/config/locators/components/chat/preview.ts +13 -0
- package/src/config/locators/components/chat/variants/index.ts +16 -0
- package/src/config/locators/components/common/buttons.ts +6 -0
- package/src/config/locators/components/common/cards.ts +18 -0
- package/src/config/locators/components/common/links.ts +1 -0
- package/src/config/locators/components/common/tables.ts +2 -0
- package/src/config/locators/components/floating-button.ts +2 -0
- package/src/config/locators/components/index.ts +3 -0
- package/src/config/locators/components/report-issue.ts +21 -0
- package/src/config/locators/components/search/index.ts +5 -0
- package/src/config/locators/components/shadow-dom.ts +1 -0
- package/src/config/locators/embedded.ts +21 -0
- package/src/config/locators/index.ts +3 -0
- package/src/config/socialProofClasses.ts +17 -0
- package/src/contexts/chatContext.tsx +451 -0
- package/src/contexts/enviveConfigContext.tsx +70 -0
- package/src/contexts/index.ts +4 -0
- package/src/contexts/systemSettingsContext.tsx +61 -0
- package/src/contexts/types.ts +1059 -0
- package/src/enabled-features.ts +83 -0
- package/src/events/event-types.ts +11 -0
- package/src/events/index.ts +52 -0
- package/src/events/registerAnalyticsListeners.ts +49 -0
- package/src/extension.ts +63 -0
- package/src/hooks/index.ts +22 -0
- package/src/hooks/useBlockBackButton.ts +29 -0
- package/src/hooks/useChatToggle.ts +66 -0
- package/src/hooks/useCustomerSupportHandoff.ts +39 -0
- package/src/hooks/useDebounce.ts +17 -0
- package/src/hooks/useDynamicVariants.ts +210 -0
- package/src/hooks/useElementObserver.ts +245 -0
- package/src/hooks/useFileUpload.ts +61 -0
- package/src/hooks/useGrabAndScroll.ts +133 -0
- package/src/hooks/useHideElements.ts +82 -0
- package/src/hooks/useHorizontalScrollAnimation.ts +115 -0
- package/src/hooks/useImageResolver.ts +51 -0
- package/src/hooks/useIntersection.ts +28 -0
- package/src/hooks/useIsSmallScreen.ts +23 -0
- package/src/hooks/useMessageFilter.ts +49 -0
- package/src/hooks/useMessageScrollObserver.ts +47 -0
- package/src/hooks/useReducedMotionWithOverride.ts +15 -0
- package/src/hooks/useSearch.tsx +433 -0
- package/src/hooks/useSnapCalculator.ts +38 -0
- package/src/hooks/useSnapControl.ts +155 -0
- package/src/hooks/useSystemSettingsContext.ts +12 -0
- package/src/hooks/useTrackComponentVisibleEvent.ts +52 -0
- package/src/hooks/useUpdateAnalyticsProps.ts +56 -0
- package/src/hooks/utils.ts +153 -0
- package/src/index.ts +31 -0
- package/src/initialize.ts +163 -0
- package/src/interceptors/types.ts +6 -0
- package/src/interceptors/useFormEscalation.ts +40 -0
- package/src/interceptors/useMessageInterceptor.ts +32 -0
- package/src/main.ts +85 -0
- package/src/main.tsx +123 -0
- package/src/merchants/bandolier/bandolier.ts +1389 -0
- package/src/merchants/carpe/carpe.ts +656 -0
- package/src/merchants/coterie/coterie.ts +280 -0
- package/src/merchants/default.ts +193 -0
- package/src/merchants/dreamlandBaby/dreamlandBaby.ts +375 -0
- package/src/merchants/fiveCbd/fiveCbd.ts +697 -0
- package/src/merchants/forLoveAndLemons/forLoveAndLemons.ts +721 -0
- package/src/merchants/greenpan/greenpan.ts +440 -0
- package/src/merchants/grooveLife/grooveLife.ts +386 -0
- package/src/merchants/homegrownCannabis/homegrownCannabis.ts +468 -0
- package/src/merchants/init-merchant.sh +53 -0
- package/src/merchants/jackArcher/jackArcher.ts +974 -0
- package/src/merchants/jordanCraig/jordanCraig.ts +1927 -0
- package/src/merchants/kindredBravely/kindredBravely.ts +529 -0
- package/src/merchants/kutFromTheKloth/kutFromTheKloth.ts +418 -0
- package/src/merchants/larryAndSerges/larryAndSerges.ts +314 -0
- package/src/merchants/leapsAndRebounds/leapsAndRebounds.ts +424 -0
- package/src/merchants/longevityrx/longevityrx.ts +368 -0
- package/src/merchants/lookOptic/lookOptic.ts +323 -0
- package/src/merchants/mantraBrand/mantraBrand.ts +838 -0
- package/src/merchants/medterra/medterra.ts +670 -0
- package/src/merchants/modells/modells.ts +546 -0
- package/src/merchants/pressedFloral/pressedFloral.ts +734 -0
- package/src/merchants/skinPerfection/skinPerfection.ts +379 -0
- package/src/merchants/snapSupplements/snapSupplements.ts +325 -0
- package/src/merchants/spanx/spanx.ts +810 -0
- package/src/merchants/spanx/spanxStaging.ts +942 -0
- package/src/merchants/supergoop/supergoop.ts +376 -0
- package/src/merchants/uniqueVintage/uniqueVintage.ts +1314 -0
- package/src/merchants/uniqueVintage/views/useUniqueVintageChatSearch.ts +147 -0
- package/src/merchants/venaCbd/venaCbd.ts +410 -0
- package/src/merchants/westonJonBoucher/westonJonBoucher.ts +473 -0
- package/src/merchants/wineEnthusiast/wineEnthusiast.ts +990 -0
- package/src/merchants/wolfMattress/wolfMattress.ts +411 -0
- package/src/merchants/wolfTactical/wolfTactical.ts +389 -0
- package/src/types/custservice-types.ts +28 -0
- package/src/types/search-filter-types.ts +111 -0
- package/src/types/suggestionBarV2-types.ts +4 -0
- package/src/types/test-types.ts +3 -0
- 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
|
+
};
|