@athoscommerce/snap-preact 1.7.0 → 1.7.1-beta.129
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/dist/cjs/components/src/components/Atoms/Button/Button.d.ts +1 -1
- package/dist/cjs/components/src/components/Atoms/Button/Button.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.d.ts +7 -0
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.js +85 -0
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Atoms/ChatLoadingIndicator/index.js +17 -0
- package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.d.ts +4 -0
- package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Atoms/Dropdown/Dropdown.js +51 -14
- package/dist/cjs/components/src/components/Atoms/Icon/paths.d.ts +21 -0
- package/dist/cjs/components/src/components/Atoms/Icon/paths.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Atoms/Icon/paths.js +21 -0
- package/dist/cjs/components/src/components/Molecules/CalloutBadge/CalloutBadge.js +3 -3
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts +20 -0
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.js +187 -0
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatAttachmentContext/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts +18 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.js +175 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/ChatMessageText.d.ts +21 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/ChatMessageText.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/ChatMessageText.js +211 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageText/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.d.ts +16 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.js +255 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatMessageUser/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +143 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts +25 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +487 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/ChatResult.d.ts +11 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/ChatResult.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/ChatResult.js +200 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatResult/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.d.ts +12 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.js +123 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatResultsDisplay/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.d.ts +15 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.js +99 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Molecules/ChatSuggestedQuestions/index.js +17 -0
- package/dist/cjs/components/src/components/Molecules/OverlayBadge/OverlayBadge.js +2 -2
- package/dist/cjs/components/src/components/Molecules/Result/Result.d.ts +5 -3
- package/dist/cjs/components/src/components/Molecules/Result/Result.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Molecules/Result/Result.js +15 -9
- package/dist/cjs/components/src/components/Molecules/SearchInput/SearchInput.d.ts +2 -0
- package/dist/cjs/components/src/components/Molecules/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Molecules/SearchInput/SearchInput.js +19 -2
- package/dist/cjs/components/src/components/Molecules/Slideout/Slideout.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Molecules/Slideout/Slideout.js +21 -26
- package/dist/cjs/components/src/components/Molecules/Slideshow/Slideshow.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Molecules/Slideshow/Slideshow.js +5 -1
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.d.ts +30 -0
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.js +1047 -0
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.styles.d.ts +16 -0
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.styles.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/Chat.styles.js +1186 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatFileUpload.d.ts +32 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatFileUpload.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatFileUpload.js +104 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatGestures.d.ts +40 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatGestures.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatGestures.js +98 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatOverflow.d.ts +44 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatOverflow.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/hooks/useChatOverflow.js +116 -0
- package/dist/cjs/components/src/components/Organisms/Chat/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Organisms/Chat/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Organisms/Chat/index.js +17 -0
- package/dist/cjs/components/src/components/Organisms/Facet/Facet.js +2 -1
- package/dist/cjs/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.js +2 -1
- package/dist/cjs/components/src/components/Organisms/Results/Results.js +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.d.ts +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.js +3 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteModal/AutocompleteModal.d.ts +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteModal/AutocompleteModal.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteModal/AutocompleteModal.js +3 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.d.ts +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.d.ts.map +1 -1
- package/dist/cjs/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.js +3 -1
- package/dist/cjs/components/src/components/Templates/Chat/Chat.d.ts +12 -0
- package/dist/cjs/components/src/components/Templates/Chat/Chat.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Templates/Chat/Chat.js +55 -0
- package/dist/cjs/components/src/components/Templates/Chat/index.d.ts +2 -0
- package/dist/cjs/components/src/components/Templates/Chat/index.d.ts.map +1 -0
- package/dist/cjs/components/src/components/Templates/Chat/index.js +17 -0
- package/dist/cjs/components/src/index.d.ts +11 -0
- package/dist/cjs/components/src/index.d.ts.map +1 -1
- package/dist/cjs/components/src/index.js +13 -1
- package/dist/cjs/components/src/providers/withTracking.d.ts +2 -2
- package/dist/cjs/components/src/providers/withTracking.d.ts.map +1 -1
- package/dist/cjs/components/src/setupTests.js +17 -0
- package/dist/cjs/components/src/themes/base/components/templates/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/base/components/templates/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/base/components/templates/chat.js +6 -0
- package/dist/cjs/components/src/themes/base/components/templates/index.d.ts.map +1 -1
- package/dist/cjs/components/src/themes/base/components/templates/index.js +5 -4
- package/dist/cjs/components/src/themes/bocachica/components/templates/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/bocachica/components/templates/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/bocachica/components/templates/chat.js +6 -0
- package/dist/cjs/components/src/themes/bocachica/components/templates/index.d.ts.map +1 -1
- package/dist/cjs/components/src/themes/bocachica/components/templates/index.js +5 -4
- package/dist/cjs/components/src/themes/pike/components/templates/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/pike/components/templates/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/pike/components/templates/chat.js +6 -0
- package/dist/cjs/components/src/themes/pike/components/templates/index.d.ts.map +1 -1
- package/dist/cjs/components/src/themes/pike/components/templates/index.js +5 -4
- package/dist/cjs/components/src/themes/snapnco/components/templates/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/snapnco/components/templates/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/snapnco/components/templates/chat.js +6 -0
- package/dist/cjs/components/src/themes/snapnco/components/templates/index.d.ts.map +1 -1
- package/dist/cjs/components/src/themes/snapnco/components/templates/index.js +5 -4
- package/dist/cjs/components/src/themes/snappy/components/templates/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/snappy/components/templates/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/snappy/components/templates/chat.js +6 -0
- package/dist/cjs/components/src/themes/snappy/components/templates/index.d.ts.map +1 -1
- package/dist/cjs/components/src/themes/snappy/components/templates/index.js +5 -4
- package/dist/cjs/components/src/themes/themeComponents/chat.d.ts +4 -0
- package/dist/cjs/components/src/themes/themeComponents/chat.d.ts.map +1 -0
- package/dist/cjs/components/src/themes/themeComponents/chat.js +14 -0
- package/dist/cjs/components/src/utilities/Colour/Colour.d.ts +11 -0
- package/dist/cjs/components/src/utilities/Colour/Colour.d.ts.map +1 -1
- package/dist/cjs/components/src/utilities/Colour/Colour.js +32 -0
- package/dist/cjs/components/src/utilities/snapify.d.ts +3 -2
- package/dist/cjs/components/src/utilities/snapify.d.ts.map +1 -1
- package/dist/cjs/components/src/utilities/snapify.js +48 -0
- package/dist/cjs/src/Snap.d.ts +2 -1
- package/dist/cjs/src/Snap.d.ts.map +1 -1
- package/dist/cjs/src/Snap.js +134 -24
- package/dist/cjs/src/Templates/SnapTemplates.d.ts +2 -1
- package/dist/cjs/src/Templates/SnapTemplates.d.ts.map +1 -1
- package/dist/cjs/src/Templates/SnapTemplates.js +67 -5
- package/dist/cjs/src/Templates/Stores/LibraryStore.d.ts +2 -0
- package/dist/cjs/src/Templates/Stores/LibraryStore.d.ts.map +1 -1
- package/dist/cjs/src/Templates/Stores/LibraryStore.js +19 -0
- package/dist/cjs/src/Templates/Stores/TemplateStore.d.ts +19 -4
- package/dist/cjs/src/Templates/Stores/TemplateStore.d.ts.map +1 -1
- package/dist/cjs/src/Templates/Stores/TemplateStore.js +2 -1
- package/dist/cjs/src/Templates/Stores/library/components/Chat.d.ts +2 -0
- package/dist/cjs/src/Templates/Stores/library/components/Chat.d.ts.map +1 -0
- package/dist/cjs/src/Templates/Stores/library/components/Chat.js +5 -0
- package/dist/cjs/src/Templates/Stores/library/languages/es.d.ts.map +1 -1
- package/dist/cjs/src/Templates/Stores/library/languages/es.js +5 -0
- package/dist/cjs/src/Templates/Stores/library/languages/fr.d.ts.map +1 -1
- package/dist/cjs/src/Templates/Stores/library/languages/fr.js +5 -0
- package/dist/cjs/src/create/createChatController.d.ts +5 -0
- package/dist/cjs/src/create/createChatController.d.ts.map +1 -0
- package/dist/cjs/src/create/createChatController.js +43 -0
- package/dist/cjs/src/setupEvents.d.ts +13 -0
- package/dist/cjs/src/setupEvents.d.ts.map +1 -1
- package/dist/cjs/src/setupEvents.js +87 -0
- package/dist/cjs/src/types.d.ts +15 -3
- package/dist/cjs/src/types.d.ts.map +1 -1
- package/dist/esm/components/src/components/Atoms/Button/Button.d.ts +1 -1
- package/dist/esm/components/src/components/Atoms/Button/Button.d.ts.map +1 -1
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.d.ts +7 -0
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.d.ts.map +1 -0
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/ChatLoadingIndicator.js +67 -0
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/index.d.ts +2 -0
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Atoms/ChatLoadingIndicator/index.js +1 -0
- package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.d.ts +4 -0
- package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.d.ts.map +1 -1
- package/dist/esm/components/src/components/Atoms/Dropdown/Dropdown.js +53 -11
- package/dist/esm/components/src/components/Atoms/Icon/paths.d.ts +21 -0
- package/dist/esm/components/src/components/Atoms/Icon/paths.d.ts.map +1 -1
- package/dist/esm/components/src/components/Atoms/Icon/paths.js +21 -0
- package/dist/esm/components/src/components/Molecules/CalloutBadge/CalloutBadge.js +1 -1
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts +20 -0
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/ChatAttachmentContext.js +169 -0
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatAttachmentContext/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts +18 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.js +170 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/ChatMessageText.d.ts +21 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/ChatMessageText.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/ChatMessageText.js +192 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageText/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.d.ts +16 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/ChatMessageUser.js +223 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatMessageUser/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts +17 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +121 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts +25 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +460 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/ChatResult.d.ts +11 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/ChatResult.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/ChatResult.js +184 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResult/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.d.ts +12 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/ChatResultsDisplay.js +102 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatResultsDisplay/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.d.ts +15 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/ChatSuggestedQuestions.js +80 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/index.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Molecules/ChatSuggestedQuestions/index.js +1 -0
- package/dist/esm/components/src/components/Molecules/OverlayBadge/OverlayBadge.js +1 -1
- package/dist/esm/components/src/components/Molecules/Result/Result.d.ts +5 -3
- package/dist/esm/components/src/components/Molecules/Result/Result.d.ts.map +1 -1
- package/dist/esm/components/src/components/Molecules/Result/Result.js +12 -7
- package/dist/esm/components/src/components/Molecules/SearchInput/SearchInput.d.ts +2 -0
- package/dist/esm/components/src/components/Molecules/SearchInput/SearchInput.d.ts.map +1 -1
- package/dist/esm/components/src/components/Molecules/SearchInput/SearchInput.js +35 -2
- package/dist/esm/components/src/components/Molecules/Slideout/Slideout.d.ts.map +1 -1
- package/dist/esm/components/src/components/Molecules/Slideout/Slideout.js +22 -27
- package/dist/esm/components/src/components/Molecules/Slideshow/Slideshow.d.ts.map +1 -1
- package/dist/esm/components/src/components/Molecules/Slideshow/Slideshow.js +5 -1
- package/dist/esm/components/src/components/Organisms/Chat/Chat.d.ts +30 -0
- package/dist/esm/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/Chat.js +956 -0
- package/dist/esm/components/src/components/Organisms/Chat/Chat.styles.d.ts +16 -0
- package/dist/esm/components/src/components/Organisms/Chat/Chat.styles.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/Chat.styles.js +1227 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatFileUpload.d.ts +32 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatFileUpload.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatFileUpload.js +54 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatGestures.d.ts +40 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatGestures.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatGestures.js +92 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatOverflow.d.ts +44 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatOverflow.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/hooks/useChatOverflow.js +110 -0
- package/dist/esm/components/src/components/Organisms/Chat/index.d.ts +2 -0
- package/dist/esm/components/src/components/Organisms/Chat/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Organisms/Chat/index.js +1 -0
- package/dist/esm/components/src/components/Organisms/Facet/Facet.js +1 -1
- package/dist/esm/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.js +1 -1
- package/dist/esm/components/src/components/Organisms/Results/Results.js +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.d.ts +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.d.ts.map +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteFixed/AutocompleteFixed.js +4 -1
- package/dist/esm/components/src/components/Templates/AutocompleteModal/AutocompleteModal.d.ts +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteModal/AutocompleteModal.d.ts.map +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteModal/AutocompleteModal.js +4 -1
- package/dist/esm/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.d.ts +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.d.ts.map +1 -1
- package/dist/esm/components/src/components/Templates/AutocompleteSlideout/AutocompleteSlideout.js +4 -1
- package/dist/esm/components/src/components/Templates/Chat/Chat.d.ts +12 -0
- package/dist/esm/components/src/components/Templates/Chat/Chat.d.ts.map +1 -0
- package/dist/esm/components/src/components/Templates/Chat/Chat.js +42 -0
- package/dist/esm/components/src/components/Templates/Chat/index.d.ts +2 -0
- package/dist/esm/components/src/components/Templates/Chat/index.d.ts.map +1 -0
- package/dist/esm/components/src/components/Templates/Chat/index.js +1 -0
- package/dist/esm/components/src/index.d.ts +11 -0
- package/dist/esm/components/src/index.d.ts.map +1 -1
- package/dist/esm/components/src/index.js +11 -0
- package/dist/esm/components/src/providers/withTracking.d.ts +2 -2
- package/dist/esm/components/src/providers/withTracking.d.ts.map +1 -1
- package/dist/esm/components/src/setupTests.js +14 -0
- package/dist/esm/components/src/themes/base/components/templates/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/base/components/templates/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/base/components/templates/chat.js +3 -0
- package/dist/esm/components/src/themes/base/components/templates/index.d.ts.map +1 -1
- package/dist/esm/components/src/themes/base/components/templates/index.js +5 -0
- package/dist/esm/components/src/themes/bocachica/components/templates/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/bocachica/components/templates/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/bocachica/components/templates/chat.js +3 -0
- package/dist/esm/components/src/themes/bocachica/components/templates/index.d.ts.map +1 -1
- package/dist/esm/components/src/themes/bocachica/components/templates/index.js +5 -0
- package/dist/esm/components/src/themes/pike/components/templates/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/pike/components/templates/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/pike/components/templates/chat.js +3 -0
- package/dist/esm/components/src/themes/pike/components/templates/index.d.ts.map +1 -1
- package/dist/esm/components/src/themes/pike/components/templates/index.js +5 -0
- package/dist/esm/components/src/themes/snapnco/components/templates/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/snapnco/components/templates/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/snapnco/components/templates/chat.js +3 -0
- package/dist/esm/components/src/themes/snapnco/components/templates/index.d.ts.map +1 -1
- package/dist/esm/components/src/themes/snapnco/components/templates/index.js +5 -0
- package/dist/esm/components/src/themes/snappy/components/templates/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/snappy/components/templates/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/snappy/components/templates/chat.js +3 -0
- package/dist/esm/components/src/themes/snappy/components/templates/index.d.ts.map +1 -1
- package/dist/esm/components/src/themes/snappy/components/templates/index.js +5 -0
- package/dist/esm/components/src/themes/themeComponents/chat.d.ts +4 -0
- package/dist/esm/components/src/themes/themeComponents/chat.d.ts.map +1 -0
- package/dist/esm/components/src/themes/themeComponents/chat.js +11 -0
- package/dist/esm/components/src/utilities/Colour/Colour.d.ts +11 -0
- package/dist/esm/components/src/utilities/Colour/Colour.d.ts.map +1 -1
- package/dist/esm/components/src/utilities/Colour/Colour.js +30 -0
- package/dist/esm/components/src/utilities/snapify.d.ts +3 -2
- package/dist/esm/components/src/utilities/snapify.d.ts.map +1 -1
- package/dist/esm/components/src/utilities/snapify.js +29 -2
- package/dist/esm/src/Snap.d.ts +2 -1
- package/dist/esm/src/Snap.d.ts.map +1 -1
- package/dist/esm/src/Snap.js +72 -0
- package/dist/esm/src/Templates/SnapTemplates.d.ts +2 -1
- package/dist/esm/src/Templates/SnapTemplates.d.ts.map +1 -1
- package/dist/esm/src/Templates/SnapTemplates.js +55 -0
- package/dist/esm/src/Templates/Stores/LibraryStore.d.ts +2 -0
- package/dist/esm/src/Templates/Stores/LibraryStore.d.ts.map +1 -1
- package/dist/esm/src/Templates/Stores/LibraryStore.js +6 -0
- package/dist/esm/src/Templates/Stores/TemplateStore.d.ts +19 -4
- package/dist/esm/src/Templates/Stores/TemplateStore.d.ts.map +1 -1
- package/dist/esm/src/Templates/Stores/TemplateStore.js +2 -1
- package/dist/esm/src/Templates/Stores/library/components/Chat.d.ts +2 -0
- package/dist/esm/src/Templates/Stores/library/components/Chat.d.ts.map +1 -0
- package/dist/esm/src/Templates/Stores/library/components/Chat.js +1 -0
- package/dist/esm/src/Templates/Stores/library/languages/es.d.ts.map +1 -1
- package/dist/esm/src/Templates/Stores/library/languages/es.js +5 -0
- package/dist/esm/src/Templates/Stores/library/languages/fr.d.ts.map +1 -1
- package/dist/esm/src/Templates/Stores/library/languages/fr.js +5 -0
- package/dist/esm/src/create/createChatController.d.ts +5 -0
- package/dist/esm/src/create/createChatController.d.ts.map +1 -0
- package/dist/esm/src/create/createChatController.js +29 -0
- package/dist/esm/src/setupEvents.d.ts +13 -0
- package/dist/esm/src/setupEvents.d.ts.map +1 -1
- package/dist/esm/src/setupEvents.js +40 -0
- package/dist/esm/src/types.d.ts +15 -3
- package/dist/esm/src/types.d.ts.map +1 -1
- package/package.json +14 -12
|
@@ -0,0 +1,956 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "@emotion/react/jsx-runtime";
|
|
2
|
+
import classnames from 'classnames';
|
|
3
|
+
import deepmerge from 'deepmerge';
|
|
4
|
+
import { observer } from 'mobx-react-lite';
|
|
5
|
+
import { useTheme, CacheProvider, useTreePath } from '../../../providers';
|
|
6
|
+
import { Colour, mergeProps, mergeStyles } from '../../../utilities';
|
|
7
|
+
import { chatDefaultStyles } from './Chat.styles';
|
|
8
|
+
import { useChatGestures } from './hooks/useChatGestures';
|
|
9
|
+
import { useChatOverflow } from './hooks/useChatOverflow';
|
|
10
|
+
import { useChatFileUpload } from './hooks/useChatFileUpload';
|
|
11
|
+
import { Button } from '../../Atoms/Button';
|
|
12
|
+
import { useRef, useEffect, useMemo, useState } from 'preact/hooks';
|
|
13
|
+
import { ChatMessageUser } from '../../Molecules/ChatMessageUser';
|
|
14
|
+
import { ChatMessageText } from '../../Molecules/ChatMessageText';
|
|
15
|
+
import { ChatSuggestedQuestions } from '../../Molecules/ChatSuggestedQuestions';
|
|
16
|
+
import { ChatAttachmentContext } from '../../Molecules/ChatAttachmentContext';
|
|
17
|
+
import { Image } from '../../Atoms/Image';
|
|
18
|
+
import { ChatLoadingIndicator } from '../../Atoms/ChatLoadingIndicator';
|
|
19
|
+
import { Dropdown, FacetSlider, Icon, Overlay, useMediaQuery } from '../../..';
|
|
20
|
+
import { ChatInspirationResultMessage } from '../../Molecules/ChatInspirationResultMessage';
|
|
21
|
+
import { ChatProductComparisonMessage, } from '../../Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage';
|
|
22
|
+
import { ChatProductQueryMessage, } from '../../Molecules/ChatProductQueryMessage/ChatProductQueryMessage';
|
|
23
|
+
// Fallback footer copy when chat becomes disabled mid-session and store.error has
|
|
24
|
+
// been cleared (e.g. by createChat) — mirrors the controller's unavailable message.
|
|
25
|
+
const CHAT_UNAVAILABLE_MESSAGE = 'Service is temporarily unavailable. In the meantime, feel free to use the search bar above to find what you need!';
|
|
26
|
+
export const ChatOrganism = observer((properties) => {
|
|
27
|
+
const globalTheme = useTheme();
|
|
28
|
+
const globalTreePath = useTreePath();
|
|
29
|
+
const isMobile = useMediaQuery('(max-width: 767px)');
|
|
30
|
+
const defaultProps = {
|
|
31
|
+
treePath: globalTreePath,
|
|
32
|
+
logo: 'https://cdn.athoscommerce.net/snap/images/Athos%20Commerce_Icon_white.svg',
|
|
33
|
+
title: 'Athos Conversational Assistant',
|
|
34
|
+
subtitle: 'Your Guided Discovery Expert',
|
|
35
|
+
multiselectFacets: false,
|
|
36
|
+
disableBubbleSuggestedQuestions: isMobile ? true : false,
|
|
37
|
+
primaryColorBg: '#253B80',
|
|
38
|
+
primaryColorFg: '#fff',
|
|
39
|
+
primaryAccentColorBg: '#feeeae',
|
|
40
|
+
primaryAccentColorFg: '#000000',
|
|
41
|
+
secondaryAccentColorBg: '#000000',
|
|
42
|
+
secondaryAccentColorFg: '#ffffff',
|
|
43
|
+
position: 'right',
|
|
44
|
+
hideMessageTypeIndicatorText: false,
|
|
45
|
+
buttonBelowMessage: false,
|
|
46
|
+
};
|
|
47
|
+
let props = mergeProps('chat', globalTheme, defaultProps, properties);
|
|
48
|
+
const { className, internalClassName, controller, logo, title, subtitle, avatar, offset, multiselectFacets, disableBubbleSuggestedQuestions, primaryColorBg, primaryColorFg, primaryAccentColorBg, primaryAccentColorFg, secondaryAccentColorBg, secondaryAccentColorFg, position, hideMessageTypeIndicatorText, buttonBelowMessage, disableStyles, treePath, } = props;
|
|
49
|
+
const colorPrimary = primaryColorBg;
|
|
50
|
+
const colorPrimaryText = primaryColorFg;
|
|
51
|
+
const colorPrimaryAccent = primaryAccentColorBg;
|
|
52
|
+
const colorPrimaryAccentText = primaryAccentColorFg;
|
|
53
|
+
const colorSecondaryAccent = secondaryAccentColorBg;
|
|
54
|
+
const colorSecondaryAccentText = secondaryAccentColorFg;
|
|
55
|
+
const chatPosition = position === 'right' ? 'right' : 'left';
|
|
56
|
+
const isValidImageUrl = (value) => {
|
|
57
|
+
try {
|
|
58
|
+
const { protocol } = new URL(value);
|
|
59
|
+
return protocol === 'http:' || protocol === 'https:' || protocol === 'data:';
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
// A single-character avatar renders as a capitalized letter; a longer value renders
|
|
66
|
+
// as an image when it's a valid image URL. Anything else renders no avatar.
|
|
67
|
+
const avatarCharacter = avatar && avatar.length === 1 ? avatar.toUpperCase() : undefined;
|
|
68
|
+
const avatarImage = avatar && avatar.length > 1 && isValidImageUrl(avatar) ? avatar : undefined;
|
|
69
|
+
const { store } = controller;
|
|
70
|
+
// Track that the widget has been visible so a mid-session disable (status check
|
|
71
|
+
// flips chatEnabled to false) keeps it mounted with the unavailable message
|
|
72
|
+
// instead of vanishing. A disabled status at bootstrap still renders nothing.
|
|
73
|
+
const wasVisibleRef = useRef(false);
|
|
74
|
+
const themeDefaults = {
|
|
75
|
+
components: {},
|
|
76
|
+
};
|
|
77
|
+
// merge deeply the themeDefaults with the theme props and the displaySettings theme props (do not merge arrays, but replace them)
|
|
78
|
+
const theme = deepmerge(themeDefaults, props?.theme || {}, { arrayMerge: (destinationArray, sourceArray) => sourceArray });
|
|
79
|
+
props = {
|
|
80
|
+
...props,
|
|
81
|
+
theme,
|
|
82
|
+
};
|
|
83
|
+
const subProps = {
|
|
84
|
+
messageUser: {
|
|
85
|
+
disableStyles,
|
|
86
|
+
theme: props.theme,
|
|
87
|
+
treePath,
|
|
88
|
+
primaryColor: colorPrimary,
|
|
89
|
+
primaryColorText: colorPrimaryText,
|
|
90
|
+
hideMessageTypeIndicatorText,
|
|
91
|
+
},
|
|
92
|
+
messageText: {
|
|
93
|
+
disableStyles,
|
|
94
|
+
theme: props.theme,
|
|
95
|
+
treePath,
|
|
96
|
+
primaryColor: colorPrimary,
|
|
97
|
+
primaryColorText: colorPrimaryText,
|
|
98
|
+
primaryAccentColor: colorPrimaryAccent,
|
|
99
|
+
primaryAccentColorText: colorPrimaryAccentText,
|
|
100
|
+
buttonBelowMessage,
|
|
101
|
+
},
|
|
102
|
+
suggestedQuestions: {
|
|
103
|
+
disableStyles,
|
|
104
|
+
theme: props.theme,
|
|
105
|
+
treePath,
|
|
106
|
+
primaryColor: colorPrimary,
|
|
107
|
+
},
|
|
108
|
+
attachmentContext: {
|
|
109
|
+
disableStyles,
|
|
110
|
+
theme: props.theme,
|
|
111
|
+
treePath,
|
|
112
|
+
},
|
|
113
|
+
loadingIndicator: {
|
|
114
|
+
disableStyles,
|
|
115
|
+
theme: props.theme,
|
|
116
|
+
treePath,
|
|
117
|
+
},
|
|
118
|
+
inspirationResultMessage: {
|
|
119
|
+
disableStyles,
|
|
120
|
+
theme: props.theme,
|
|
121
|
+
treePath,
|
|
122
|
+
},
|
|
123
|
+
productComparisonMessage: {
|
|
124
|
+
disableStyles,
|
|
125
|
+
theme: props.theme,
|
|
126
|
+
treePath,
|
|
127
|
+
},
|
|
128
|
+
productQueryMessage: {
|
|
129
|
+
disableStyles,
|
|
130
|
+
theme: props.theme,
|
|
131
|
+
treePath,
|
|
132
|
+
primaryColor: colorPrimary,
|
|
133
|
+
primaryColorText: colorPrimaryText,
|
|
134
|
+
},
|
|
135
|
+
};
|
|
136
|
+
const messagesEndRef = useRef(null);
|
|
137
|
+
const messagesContainerRef = useRef(null);
|
|
138
|
+
const headerRef = useRef(null);
|
|
139
|
+
const footerRef = useRef(null);
|
|
140
|
+
const chatRef = useRef(null);
|
|
141
|
+
// Ref for the mobile secondary panel — needed for swipe-to-dismiss measurements
|
|
142
|
+
const secondaryRef = useRef(null);
|
|
143
|
+
const [mobileProductInfoOpen, setMobileProductInfoOpen] = useState(false);
|
|
144
|
+
const [footerHeight, setFooterHeight] = useState(0);
|
|
145
|
+
const dropEnabled = store.features.imageSearch.enabled && !store.loading && !store.blocked && !store.currentChat?.sessionLimitReached;
|
|
146
|
+
const { fileInputRef, isDraggingFile, dragHandlers } = useChatFileUpload({
|
|
147
|
+
enabled: !!dropEnabled,
|
|
148
|
+
onUpload: (files) => controller.upload(files),
|
|
149
|
+
});
|
|
150
|
+
// productQuery messages are filtered out of the main chat render (they only drive the side-chat panel),
|
|
151
|
+
// so don't auto-scroll when one is added — otherwise clicking a product in the side chat scrolls the main thread.
|
|
152
|
+
const visibleChat = (store.currentChat?.chat || []).filter((m) => m.messageType !== 'productQuery');
|
|
153
|
+
const visibleChatLength = visibleChat.length;
|
|
154
|
+
const lastVisibleMessage = visibleChat[visibleChat.length - 1];
|
|
155
|
+
const { showNewMessages, setShowNewMessages, messagesOverflow, scrollToBottom, scrollToBottomIfNear } = useChatOverflow({
|
|
156
|
+
messagesContainerRef,
|
|
157
|
+
headerRef,
|
|
158
|
+
open: store.open,
|
|
159
|
+
loading: store.loading,
|
|
160
|
+
visibleChatLength,
|
|
161
|
+
lastVisibleMessage,
|
|
162
|
+
});
|
|
163
|
+
// Re-focus the chat input on desktop after a search completes.
|
|
164
|
+
// The input is disabled while loading, which causes the browser to drop focus.
|
|
165
|
+
// On mobile we intentionally skip this so the virtual keyboard dismisses.
|
|
166
|
+
// Only fire on the loading true → false transition — running on mount steals
|
|
167
|
+
// focus away from the Storybook controls whenever the story re-renders.
|
|
168
|
+
const prevLoadingRef = useRef(store.loading);
|
|
169
|
+
useEffect(() => {
|
|
170
|
+
if (prevLoadingRef.current && !store.loading && !isMobile && store.open) {
|
|
171
|
+
controller.focusInput();
|
|
172
|
+
}
|
|
173
|
+
prevLoadingRef.current = store.loading;
|
|
174
|
+
}, [store.loading]);
|
|
175
|
+
// const subProps: ChatSubProps = {};
|
|
176
|
+
const activeMessage = store.currentChat?.activeMessage;
|
|
177
|
+
// On mobile, only auto-open the secondary panel for the first productComparison
|
|
178
|
+
// message in the chat session. Subsequent comparison results show a "Show Details"
|
|
179
|
+
// button instead (handled in MessageText via sideChatLabel toggle).
|
|
180
|
+
const isFirstMobileComparison = (() => {
|
|
181
|
+
if (!isMobile || activeMessage?.messageType !== 'productComparison')
|
|
182
|
+
return true;
|
|
183
|
+
// If the user explicitly activated a comparison (via "Show Details" or the
|
|
184
|
+
// view-side-chat button), always allow it regardless of position in the chat
|
|
185
|
+
if (store.currentChat?.activeMessageId)
|
|
186
|
+
return true;
|
|
187
|
+
const comparisonMessages = (store.currentChat?.chat || []).filter((m) => m.messageType === 'productComparison');
|
|
188
|
+
return comparisonMessages.length <= 1;
|
|
189
|
+
})();
|
|
190
|
+
const sideChatTypes = isMobile
|
|
191
|
+
? ['inspirationResult', ...(isFirstMobileComparison ? ['productComparison'] : []), ...(mobileProductInfoOpen ? ['productQuery'] : [])]
|
|
192
|
+
: ['inspirationResult', 'productComparison', 'productQuery'];
|
|
193
|
+
const shouldShowSideChat = activeMessage && sideChatTypes.includes(activeMessage?.messageType) && store.currentChat?.dismissedSideChatMessageId !== activeMessage.id;
|
|
194
|
+
const hasSideChat = !!shouldShowSideChat;
|
|
195
|
+
const styling = useMemo(() => mergeStyles({
|
|
196
|
+
mobile: isMobile,
|
|
197
|
+
offset,
|
|
198
|
+
hasSideChat,
|
|
199
|
+
footerHeight,
|
|
200
|
+
primaryColorBg: colorPrimary,
|
|
201
|
+
primaryColorFg: colorPrimaryText,
|
|
202
|
+
primaryAccentColorBg: colorPrimaryAccent,
|
|
203
|
+
primaryAccentColorFg: colorPrimaryAccentText,
|
|
204
|
+
secondaryAccentColorBg: colorSecondaryAccent,
|
|
205
|
+
secondaryAccentColorFg: colorSecondaryAccentText,
|
|
206
|
+
position: chatPosition,
|
|
207
|
+
}, chatDefaultStyles), [
|
|
208
|
+
isMobile,
|
|
209
|
+
offset,
|
|
210
|
+
hasSideChat,
|
|
211
|
+
footerHeight,
|
|
212
|
+
colorPrimary,
|
|
213
|
+
colorPrimaryText,
|
|
214
|
+
colorPrimaryAccent,
|
|
215
|
+
colorPrimaryAccentText,
|
|
216
|
+
colorSecondaryAccent,
|
|
217
|
+
colorSecondaryAccentText,
|
|
218
|
+
chatPosition,
|
|
219
|
+
]);
|
|
220
|
+
// Lock body scroll while chat is open so touch/wheel scrolls don't leak to the page behind
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (!store.open)
|
|
223
|
+
return;
|
|
224
|
+
const body = document.body;
|
|
225
|
+
const previousOverflow = body.style.overflow;
|
|
226
|
+
const previousOverscroll = body.style.overscrollBehavior;
|
|
227
|
+
body.style.overflow = 'hidden';
|
|
228
|
+
body.style.overscrollBehavior = 'contain';
|
|
229
|
+
return () => {
|
|
230
|
+
body.style.overflow = previousOverflow;
|
|
231
|
+
body.style.overscrollBehavior = previousOverscroll;
|
|
232
|
+
};
|
|
233
|
+
}, [store.open]);
|
|
234
|
+
// Close chat on Escape key (or dismiss side chat first when it's open)
|
|
235
|
+
useEffect(() => {
|
|
236
|
+
if (!store.open)
|
|
237
|
+
return;
|
|
238
|
+
const onKeyDown = (e) => {
|
|
239
|
+
if (e.key !== 'Escape')
|
|
240
|
+
return;
|
|
241
|
+
if (store.currentChat?.activeMessage && shouldShowSideChat) {
|
|
242
|
+
store.currentChat?.dismissSideChat();
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
controller.handlers.button.click();
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
document.addEventListener('keydown', onKeyDown);
|
|
249
|
+
return () => document.removeEventListener('keydown', onKeyDown);
|
|
250
|
+
}, [store.open, shouldShowSideChat]);
|
|
251
|
+
// Track the primary footer height so the mobile secondary panel can sit above it
|
|
252
|
+
useEffect(() => {
|
|
253
|
+
const el = footerRef.current;
|
|
254
|
+
if (!el)
|
|
255
|
+
return;
|
|
256
|
+
const observer = new ResizeObserver((entries) => {
|
|
257
|
+
for (const entry of entries) {
|
|
258
|
+
setFooterHeight(entry.borderBoxSize?.[0]?.blockSize ?? entry.target.getBoundingClientRect().height);
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
observer.observe(el);
|
|
262
|
+
return () => observer.disconnect();
|
|
263
|
+
}, [store.open, store.currentChat?.isExpired]);
|
|
264
|
+
// Prevent the background page from scrolling while the chat is open on touch
|
|
265
|
+
// devices. iOS Safari (real iPad/iPhone) rubber-band-scrolls the body when a
|
|
266
|
+
// touchmove inside `.ss__chat__messages` reaches the top/bottom — and
|
|
267
|
+
// `overscroll-behavior: contain` is ignored there.
|
|
268
|
+
//
|
|
269
|
+
// We don't touch body styles (locking via `position: fixed` on `<body>` leaves
|
|
270
|
+
// iPad Safari in a stuck viewport state where the page can't scroll after the
|
|
271
|
+
// lock is released). Instead we install a `touchmove` handler that:
|
|
272
|
+
// - allows the touchmove if it's inside a scrollable region of the chat
|
|
273
|
+
// and that region isn't at its scroll edge in the swipe direction;
|
|
274
|
+
// - calls `preventDefault()` otherwise — which is what blocks both
|
|
275
|
+
// background scrolling and iOS rubber-band overscroll.
|
|
276
|
+
// Cleanup just removes the listeners, so nothing can leak into the page.
|
|
277
|
+
useEffect(() => {
|
|
278
|
+
if (!store.open)
|
|
279
|
+
return;
|
|
280
|
+
if (typeof document === 'undefined')
|
|
281
|
+
return;
|
|
282
|
+
let startX = 0;
|
|
283
|
+
let startY = 0;
|
|
284
|
+
let scrollEl = null;
|
|
285
|
+
// Walk up from the touch target to find the first ancestor inside the chat
|
|
286
|
+
// that is actually scrollable in either axis. Stop at the chat root so we
|
|
287
|
+
// never consider elements outside the chat.
|
|
288
|
+
const findScrollableInChat = (start) => {
|
|
289
|
+
const chatRoot = chatRef.current;
|
|
290
|
+
if (!chatRoot)
|
|
291
|
+
return null;
|
|
292
|
+
let el = start;
|
|
293
|
+
while (el && chatRoot.contains(el)) {
|
|
294
|
+
const style = getComputedStyle(el);
|
|
295
|
+
const scrollableY = (style.overflowY === 'auto' || style.overflowY === 'scroll') && el.scrollHeight > el.clientHeight;
|
|
296
|
+
const scrollableX = (style.overflowX === 'auto' || style.overflowX === 'scroll') && el.scrollWidth > el.clientWidth;
|
|
297
|
+
if (scrollableY || scrollableX) {
|
|
298
|
+
return el;
|
|
299
|
+
}
|
|
300
|
+
if (el === chatRoot)
|
|
301
|
+
break;
|
|
302
|
+
el = el.parentElement;
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
};
|
|
306
|
+
const onTouchStart = (e) => {
|
|
307
|
+
if (!e.touches[0])
|
|
308
|
+
return;
|
|
309
|
+
startX = e.touches[0].clientX;
|
|
310
|
+
startY = e.touches[0].clientY;
|
|
311
|
+
scrollEl = findScrollableInChat(e.target);
|
|
312
|
+
};
|
|
313
|
+
const onTouchMove = (e) => {
|
|
314
|
+
const target = e.target;
|
|
315
|
+
const chatRoot = chatRef.current;
|
|
316
|
+
const inChat = !!(chatRoot && target && chatRoot.contains(target));
|
|
317
|
+
// Touches outside the chat shouldn't scroll the background.
|
|
318
|
+
if (!inChat) {
|
|
319
|
+
if (e.cancelable)
|
|
320
|
+
e.preventDefault();
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
// Inside the chat but no scrollable container — block to prevent body scroll.
|
|
324
|
+
if (!scrollEl) {
|
|
325
|
+
if (e.cancelable)
|
|
326
|
+
e.preventDefault();
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
// Inside a scrollable container — only block when at the edge in the
|
|
330
|
+
// swipe direction, so internal scrolling continues to work. Detect the
|
|
331
|
+
// dominant axis so horizontally-scrollable rows (e.g. the facets row)
|
|
332
|
+
// also get to handle their own touch gestures.
|
|
333
|
+
const currentX = e.touches[0]?.clientX ?? startX;
|
|
334
|
+
const currentY = e.touches[0]?.clientY ?? startY;
|
|
335
|
+
const deltaX = currentX - startX;
|
|
336
|
+
const deltaY = currentY - startY;
|
|
337
|
+
const horizontal = Math.abs(deltaX) > Math.abs(deltaY);
|
|
338
|
+
if (horizontal) {
|
|
339
|
+
const scrollsX = scrollEl.scrollWidth > scrollEl.clientWidth;
|
|
340
|
+
if (!scrollsX) {
|
|
341
|
+
if (e.cancelable)
|
|
342
|
+
e.preventDefault();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
const atLeft = scrollEl.scrollLeft <= 0;
|
|
346
|
+
const atRight = scrollEl.scrollLeft + scrollEl.clientWidth >= scrollEl.scrollWidth - 1;
|
|
347
|
+
if ((atLeft && deltaX > 0) || (atRight && deltaX < 0)) {
|
|
348
|
+
if (e.cancelable)
|
|
349
|
+
e.preventDefault();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
const scrollsY = scrollEl.scrollHeight > scrollEl.clientHeight;
|
|
354
|
+
if (!scrollsY) {
|
|
355
|
+
if (e.cancelable)
|
|
356
|
+
e.preventDefault();
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
const atTop = scrollEl.scrollTop <= 0;
|
|
360
|
+
const atBottom = scrollEl.scrollTop + scrollEl.clientHeight >= scrollEl.scrollHeight - 1;
|
|
361
|
+
if ((atTop && deltaY > 0) || (atBottom && deltaY < 0)) {
|
|
362
|
+
if (e.cancelable)
|
|
363
|
+
e.preventDefault();
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
document.addEventListener('touchstart', onTouchStart, { passive: true });
|
|
368
|
+
document.addEventListener('touchmove', onTouchMove, { passive: false });
|
|
369
|
+
return () => {
|
|
370
|
+
document.removeEventListener('touchstart', onTouchStart);
|
|
371
|
+
document.removeEventListener('touchmove', onTouchMove);
|
|
372
|
+
};
|
|
373
|
+
}, [store.open]);
|
|
374
|
+
const HistoryButton = (props) => (_jsx(Button, { className: "ss__chat__header__button--history", disabled: props.disabled, icon: {
|
|
375
|
+
icon: 'history',
|
|
376
|
+
title: 'Chat History',
|
|
377
|
+
} }));
|
|
378
|
+
const HistoryPopup = (props) => (_jsxs("div", { className: "ss__chat__history", children: [_jsxs("div", { className: "ss__chat__history__header", children: [_jsx("h4", { children: "History" }), _jsx("div", { className: "ss__chat__history__header__buttons", children: _jsx(Button, { content: "clear", disabled: store.loading || store.blocked, onClick: () => {
|
|
379
|
+
controller.store.clearHistory();
|
|
380
|
+
props.toggleOpen && props.toggleOpen();
|
|
381
|
+
} }) })] }), store.chats.length > 0 ? (_jsx("div", { className: "ss__chat__history__chats", children: store.chats
|
|
382
|
+
.slice()
|
|
383
|
+
.reverse()
|
|
384
|
+
.map((chat) => (_jsx("div", { className: "ss__chat__history__chats__chat", children: _jsxs(Button, { className: "ss__chat__history__chat__button", onClick: () => {
|
|
385
|
+
controller.store.switchChat(chat.id);
|
|
386
|
+
}, disabled: chat.id === store.currentChatId || store.loading || store.blocked, children: [_jsx("div", { className: "ss__chat__history__chat__button__text", children: (() => {
|
|
387
|
+
const lastUserMessage = [...chat.chat].reverse().find((message) => message.messageType === 'user');
|
|
388
|
+
if (!lastUserMessage)
|
|
389
|
+
return 'New Chat';
|
|
390
|
+
const text = lastUserMessage.text;
|
|
391
|
+
return text.length > 50 ? `${text.slice(0, 50)}...` : text;
|
|
392
|
+
})() }), _jsx("div", { className: "ss__chat__history__chat__button__date", children: chat.createdAt.toLocaleString() })] }) }, chat.id))) })) : null] }));
|
|
393
|
+
const FacetButton = (props) => _jsx(Button, { icon: props.open ? 'angle-down' : 'angle-up', children: props.label });
|
|
394
|
+
const getFacetButtonLabel = (facet) => {
|
|
395
|
+
const base = facet.label || facet.field;
|
|
396
|
+
// Slider facets show the active low-high range in the button when narrowed.
|
|
397
|
+
if (facet.type === 'range') {
|
|
398
|
+
const rangeFacet = facet;
|
|
399
|
+
const active = rangeFacet.active;
|
|
400
|
+
const range = rangeFacet.range;
|
|
401
|
+
if (active && range && (active.low !== range.low || active.high !== range.high)) {
|
|
402
|
+
return `${base} (${active.low ?? '*'}-${active.high ?? '*'})`;
|
|
403
|
+
}
|
|
404
|
+
return base;
|
|
405
|
+
}
|
|
406
|
+
// Count current selections from the urlManager (source of truth for live state).
|
|
407
|
+
// `value.filtered` reflects the server's snapshot at response time and goes stale
|
|
408
|
+
// once the user deselects locally.
|
|
409
|
+
const applied = (facet.values || []).filter((value) => {
|
|
410
|
+
if (!value)
|
|
411
|
+
return false;
|
|
412
|
+
const optionValue = 'low' in value && 'high' in value
|
|
413
|
+
? `${value.low ?? '*'}:${value.high ?? '*'}`
|
|
414
|
+
: value.value || value.label;
|
|
415
|
+
return controller.store.isFacetSelected(facet.field, optionValue);
|
|
416
|
+
}).length;
|
|
417
|
+
return applied > 0 ? `${base} (${applied})` : base;
|
|
418
|
+
};
|
|
419
|
+
// Slider dropdown grows to fill the remaining chat width from its anchor,
|
|
420
|
+
// so the price range slider isn't cramped inside a 150px-wide dropdown.
|
|
421
|
+
const SliderFacetDropdown = (props) => {
|
|
422
|
+
const anchorRef = useRef(null);
|
|
423
|
+
const [width, setWidth] = useState(null);
|
|
424
|
+
useEffect(() => {
|
|
425
|
+
const compute = () => {
|
|
426
|
+
if (!anchorRef.current || !chatRef.current)
|
|
427
|
+
return;
|
|
428
|
+
const anchor = anchorRef.current.getBoundingClientRect();
|
|
429
|
+
const chat = chatRef.current.getBoundingClientRect();
|
|
430
|
+
const remaining = chat.right - anchor.left - 16;
|
|
431
|
+
setWidth(Math.max(220, remaining));
|
|
432
|
+
};
|
|
433
|
+
compute();
|
|
434
|
+
const ro = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(compute) : null;
|
|
435
|
+
if (ro && chatRef.current)
|
|
436
|
+
ro.observe(chatRef.current);
|
|
437
|
+
window.addEventListener('resize', compute);
|
|
438
|
+
return () => {
|
|
439
|
+
if (ro)
|
|
440
|
+
ro.disconnect();
|
|
441
|
+
window.removeEventListener('resize', compute);
|
|
442
|
+
};
|
|
443
|
+
}, []);
|
|
444
|
+
return (_jsx("div", { ref: anchorRef, className: "ss__chat__actions__facet ss__chat__actions__facet--range", children: _jsx(Dropdown, { usePortal: true, dropUp: true, button: _jsx(FacetButton, { label: props.label }), style: {
|
|
445
|
+
'.ss__dropdown__content': {
|
|
446
|
+
width: width ? `${width}px` : 'auto',
|
|
447
|
+
background: 'white',
|
|
448
|
+
boxShadow: '0px 3px 6px 0px rgba(0, 0, 0, 0.2)',
|
|
449
|
+
borderRadius: '6px',
|
|
450
|
+
},
|
|
451
|
+
}, children: _jsx("div", { className: "ss__chat__actions__facet__options", children: _jsx("div", { className: "ss__chat__actions__facet__slider", style: { padding: '12px 16px' }, children: _jsx(FacetSlider, { facet: props.facet, onChange: () => {
|
|
452
|
+
if (!multiselectFacets) {
|
|
453
|
+
// FacetSlider fires onChange before its urlManager update; defer
|
|
454
|
+
// so controller.search() reads the updated filter state. Force
|
|
455
|
+
// productSearch with the current filters so a slider tweak doesn't
|
|
456
|
+
// fall through to `general` when hasPendingFacetChanges happens to
|
|
457
|
+
// be false (e.g. the slider returned to the seeded range).
|
|
458
|
+
queueMicrotask(() => controller.search({
|
|
459
|
+
data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
|
|
460
|
+
}));
|
|
461
|
+
}
|
|
462
|
+
} }) }) }) }) }));
|
|
463
|
+
};
|
|
464
|
+
// TODO: if starting a new chat and it's expired, this button would then disappear
|
|
465
|
+
if (controller.store.chatEnabled) {
|
|
466
|
+
wasVisibleRef.current = true;
|
|
467
|
+
}
|
|
468
|
+
// Hide the widget entirely when chat is disabled at bootstrap (silent disable).
|
|
469
|
+
// When chat becomes disabled mid-session, keep the widget mounted so the footer
|
|
470
|
+
// unavailable message stays visible (with the input disabled).
|
|
471
|
+
if (!controller.store.chatEnabled && !wasVisibleRef.current) {
|
|
472
|
+
return _jsx(_Fragment, {});
|
|
473
|
+
}
|
|
474
|
+
// Dismiss the secondary panel and clear any product attachments tied to the side-chat
|
|
475
|
+
// (discuss product flow), while preserving productSimilar/productComparison attachments.
|
|
476
|
+
const dismissSideChat = () => {
|
|
477
|
+
const productAttachmentsToRemove = (store.currentChat?.attachments.attached || []).filter((item) => item.type === 'product' && item.requestType !== 'productSimilar' && item.requestType !== 'productComparison');
|
|
478
|
+
productAttachmentsToRemove.forEach((item) => store.currentChat?.attachments.remove(item.id));
|
|
479
|
+
setMobileProductInfoOpen(false);
|
|
480
|
+
store.currentChat?.dismissSideChat();
|
|
481
|
+
};
|
|
482
|
+
const { swipeOffset, swipeAnimating, swipeHandlers } = useChatGestures({
|
|
483
|
+
panelRef: secondaryRef,
|
|
484
|
+
shouldShowSideChat: !!shouldShowSideChat,
|
|
485
|
+
activeMessageId: activeMessage?.id,
|
|
486
|
+
onDismiss: dismissSideChat,
|
|
487
|
+
});
|
|
488
|
+
const requestType = store.currentChat?.requestType;
|
|
489
|
+
const loadingVerbs = useMemo(() => {
|
|
490
|
+
switch (requestType) {
|
|
491
|
+
case 'productQuery':
|
|
492
|
+
return ['Thinking', 'Searching', 'Analyzing', 'Gathering details'];
|
|
493
|
+
case 'productSearch':
|
|
494
|
+
return ['Searching', 'Browsing catalog', 'Finding products', 'Curating results'];
|
|
495
|
+
case 'productComparison':
|
|
496
|
+
return ['Comparing', 'Analyzing', 'Evaluating', 'Weighing options'];
|
|
497
|
+
case 'imageSearch':
|
|
498
|
+
return ['Analyzing image', 'Identifying', 'Searching', 'Matching'];
|
|
499
|
+
case 'productSimilar':
|
|
500
|
+
return ['Finding similar items', 'Searching', 'Matching', 'Curating'];
|
|
501
|
+
case 'inspirationRequest':
|
|
502
|
+
return ['Brainstorming', 'Imagining', 'Curating', 'Inspiring'];
|
|
503
|
+
case 'general':
|
|
504
|
+
default:
|
|
505
|
+
return undefined;
|
|
506
|
+
}
|
|
507
|
+
}, [requestType]);
|
|
508
|
+
const isWelcomeState = (!store.currentChat?.chat || store.currentChat.chat.length === 0) && !!store.welcomeMessage && !store.currentChat?.isExpired;
|
|
509
|
+
return (_jsx(CacheProvider, { children: _jsxs(_Fragment, { children: [_jsxs("div", { ref: chatRef, className: classnames('ss__chat', {
|
|
510
|
+
'ss__chat--open': store.open,
|
|
511
|
+
'ss__chat--minimized': !store.open,
|
|
512
|
+
'ss__chat--mobile': isMobile,
|
|
513
|
+
'ss__chat--welcome': isWelcomeState,
|
|
514
|
+
}, className, internalClassName), ...styling, children: [!disableBubbleSuggestedQuestions && !store.open && !store.currentChat && store.suggestedQuestions?.length > 0 && (_jsx("div", { className: "ss__chat__suggested-questions", children: store.suggestedQuestions.map((question) => (_jsx("div", { className: "ss__chat__suggested-questions__item", onClick: () => {
|
|
515
|
+
controller.openChat(question);
|
|
516
|
+
}, children: question }, question))) })), _jsx("button", { type: "button", className: 'ss__chat__bubble', "aria-label": store.open ? 'Close chat' : 'Open chat', "aria-expanded": store.open, onClick: () => controller.handlers.button.click(), children: _jsx(Icon, { icon: "chat", title: "Open Chat" }) }), store.open && shouldShowSideChat && activeMessage ? (_jsxs("div", { ref: secondaryRef, className: classnames('ss__chat__secondary'), role: "region", "aria-label": "Conversation details", style: isMobile
|
|
517
|
+
? {
|
|
518
|
+
// Touch swipe animations need to be inline to prevent visual jumps
|
|
519
|
+
transform: swipeOffset > 0 ? `translateY(${swipeOffset}px)` : undefined,
|
|
520
|
+
transition: swipeAnimating ? 'transform 0.3s ease-out' : 'none',
|
|
521
|
+
}
|
|
522
|
+
: undefined, children: [isMobile && (_jsx("div", { className: "ss__chat__secondary__drag-handle", ...swipeHandlers, children: _jsx("div", { className: "ss__chat__secondary__drag-handle__indicator" }) })), _jsxs("div", { className: 'ss__chat__header', onTouchStart: isMobile ? swipeHandlers.onTouchStart : undefined, onTouchMove: isMobile ? swipeHandlers.onTouchMove : undefined, onTouchEnd: isMobile ? swipeHandlers.onTouchEnd : undefined, children: [_jsxs("div", { className: "ss__chat__header__title", children: [_jsx("div", { className: "ss__chat__header__title__primary", children: {
|
|
523
|
+
inspirationResult: 'Inspiration Scenarios',
|
|
524
|
+
productComparison: 'Product Comparison',
|
|
525
|
+
productQuery: '',
|
|
526
|
+
}[activeMessage.messageType] || null }), _jsx("div", { className: "ss__chat__header__title__secondary", children: {
|
|
527
|
+
inspirationResult: 'Choose a style direction to explore',
|
|
528
|
+
productComparison: `Comparing ${activeMessage?.searchResults?.length || ''} products`,
|
|
529
|
+
productQuery: '',
|
|
530
|
+
}[activeMessage.messageType] || null })] }), _jsx("div", { className: "ss__chat__header__buttons", children: _jsx(Button, { className: "ss__chat__header__button--close", "aria-label": "Close chat", icon: { icon: 'close2', title: 'Close Chat' }, onClick: () => dismissSideChat() }) })] }), _jsx("div", { className: "ss__chat__content", children: _jsx("div", { className: 'ss__chat__messages', children: {
|
|
531
|
+
inspirationResult: (_jsx(ChatInspirationResultMessage, { ...subProps.inspirationResultMessage, chatItem: activeMessage, controller: controller, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined })),
|
|
532
|
+
productComparison: (_jsx(ChatProductComparisonMessage, { ...subProps.productComparisonMessage, chatItem: activeMessage, controller: controller })),
|
|
533
|
+
productQuery: (_jsx(ChatProductQueryMessage, { ...subProps.productQueryMessage, chatItem: activeMessage, controller: controller, displayFields: controller.config.settings?.quickview?.displayFields, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
534
|
+
}[activeMessage.messageType] || null }) })] })) : null, store.open ? (_jsxs("div", { className: 'ss__chat__primary',
|
|
535
|
+
// On mobile, interacting with the primary chat (header or anywhere within)
|
|
536
|
+
// dismisses the secondary panel. Capture phase so it fires even when an
|
|
537
|
+
// inner control stops propagation.
|
|
538
|
+
onClickCapture: isMobile && shouldShowSideChat ? () => dismissSideChat() : undefined, children: [_jsxs("div", { className: 'ss__chat__header', children: [_jsxs("div", { className: "ss__chat__header__title", children: [logo ? _jsx(Image, { className: "ss__chat__header__title__logo", src: logo, alt: title || 'Chat logo' }) : null, _jsxs("div", { className: "ss__chat__header__title__text", children: [title ? _jsx("div", { className: "ss__chat__header__title__text__primary", children: title }) : null, subtitle ? _jsx("div", { className: "ss__chat__header__title__text__secondary", children: subtitle }) : null] })] }), _jsxs("div", { className: "ss__chat__header__buttons", children: [_jsx(Button
|
|
539
|
+
// disabled={store.currentChat?.chat && store.currentChat.chat.length <= 1}
|
|
540
|
+
, {
|
|
541
|
+
// disabled={store.currentChat?.chat && store.currentChat.chat.length <= 1}
|
|
542
|
+
className: "ss__chat__header__button--new", "aria-label": "New chat", icon: { icon: controller.store.initChatLoading ? 'spinner' : 'plus2', title: 'New Chat' }, onClick: () => controller.store.createChat() }), store.chats.length > 1 && (_jsx(Dropdown, { disabled: store.loading || store.blocked, className: "ss__chat__header__dropdown-history", button: _jsx(HistoryButton, {}), children: _jsx(HistoryPopup, {}) })), _jsx(Button, { className: "ss__chat__header__button--close", "aria-label": "Close chat", icon: { icon: 'close2', title: 'Close Chat' }, onClick: () => controller.handlers.button.click() })] })] }), _jsxs("div", { className: "ss__chat__content", children: [(() => {
|
|
543
|
+
const feedbackAfterMessages = controller.config.settings?.feedbackAfterMessages;
|
|
544
|
+
const systemMessages = (store.currentChat?.chat || []).filter((msg) => msg.messageType !== 'user' && msg.messageType !== 'productQuery');
|
|
545
|
+
const currentRating = store.currentChat?.feedback.rating;
|
|
546
|
+
const hasRated = !!currentRating;
|
|
547
|
+
const shouldShowFeedback = feedbackAfterMessages &&
|
|
548
|
+
systemMessages.length >= feedbackAfterMessages &&
|
|
549
|
+
!store.currentChat?.feedback.dismissed &&
|
|
550
|
+
(!hasRated || store.currentChat?.feedback.justGiven);
|
|
551
|
+
return shouldShowFeedback ? (_jsxs("div", { className: "ss__chat__session-feedback", children: [_jsx("div", { className: "ss__chat__session-feedback__icon", children: _jsx(Icon, { icon: "chat", size: "16px" }) }), _jsx("span", { className: "ss__chat__session-feedback__label", children: hasRated ? 'Thank you for your feedback' : "How's your experience so far?" }), _jsxs("div", { className: `ss__chat__session-feedback__actions${hasRated ? ' ss__chat__session-feedback__actions--rated' : ''}`, children: [(!hasRated || currentRating === 'UP') && (_jsx("span", { onClick: () => !hasRated && controller.handleFeedback('UP'), children: _jsx(Icon, { icon: 'thumbs-up', size: "16px", title: 'Thumbs Up' }) })), (!hasRated || currentRating === 'DOWN') && (_jsx("span", { onClick: () => !hasRated && controller.handleFeedback('DOWN'), children: _jsx(Icon, { icon: 'thumbs-down', size: "16px", title: 'Thumbs Down' }) }))] }), !hasRated && (_jsx("span", { className: "ss__chat__session-feedback__close", onClick: () => {
|
|
552
|
+
if (store.currentChat) {
|
|
553
|
+
store.currentChat.feedback.dismissed = true;
|
|
554
|
+
store.currentChat.save();
|
|
555
|
+
}
|
|
556
|
+
}, children: _jsx(Icon, { icon: "close-thin", size: "14px" }) }))] })) : null;
|
|
557
|
+
})(), _jsx("div", { ref: headerRef, className: classnames('ss__chat__content__header', {
|
|
558
|
+
'ss__chat__content__header--overlay': messagesOverflow,
|
|
559
|
+
}), children: store.currentChat?.comparisons.compared && store.currentChat.comparisons.compared.length > 0 && (_jsxs("div", { className: 'ss__chat__content__header__comparisons', children: [_jsxs("div", { className: 'ss__chat__content__header__comparisons__header', children: [_jsxs("div", { className: 'ss__chat__content__header__comparisons__header__title', children: [_jsx(Icon, { className: 'ss__chat__content__header__comparisons__header__title__icon', icon: 'clipboard' }), _jsxs("span", { className: 'ss__chat__content__header__comparisons__header__title__text', children: ["Compare Products (", store.currentChat?.comparisons.compared.length, "/", store.currentChat?.comparisons.maxItems, ")"] })] }), _jsx("div", { className: 'ss__chat__content__header__comparisons__header__actions', children: _jsx(Button, { onClick: () => store.currentChat?.comparisons.reset(), children: "clear" }) })] }), _jsxs("div", { className: 'ss__chat__content__header__comparisons__content', children: [_jsx("div", { className: 'ss__chat__content__header__comparisons__content__items', children: Array.from({ length: store.currentChat?.comparisons.maxItems }).map((_, index) => {
|
|
560
|
+
const comparisonItem = store.currentChat?.comparisons.compared[index];
|
|
561
|
+
return (_jsx("div", { className: classnames('ss__chat__content__header__comparisons__content__comparison', {
|
|
562
|
+
'ss__chat__content__header__comparisons__content__comparison--placeholder': !comparisonItem,
|
|
563
|
+
}), children: comparisonItem ? (_jsxs(_Fragment, { children: [_jsx(Image
|
|
564
|
+
// onClick={() => {
|
|
565
|
+
// controller?.productQuickView(product as any);
|
|
566
|
+
// }}
|
|
567
|
+
, {
|
|
568
|
+
// onClick={() => {
|
|
569
|
+
// controller?.productQuickView(product as any);
|
|
570
|
+
// }}
|
|
571
|
+
alt: (comparisonItem?.result?.display || comparisonItem?.result)?.mappings?.core?.name || '', src: (comparisonItem?.result?.display || comparisonItem?.result)?.mappings?.core?.imageUrl ||
|
|
572
|
+
(comparisonItem?.result?.display || comparisonItem?.result)?.mappings?.core?.parentImageUrl ||
|
|
573
|
+
'' }), _jsx("div", { className: "ss__chat__content__header__comparisons__content__comparison__remove", onClick: () => {
|
|
574
|
+
store.currentChat?.comparisons.remove(comparisonItem.result.id);
|
|
575
|
+
}, children: _jsx(Icon, { icon: 'close-thin', size: '12px' }) })] })) : (_jsxs(_Fragment, { children: [_jsx(Icon, { icon: 'plus-thin' }), _jsx("div", { className: 'ss__chat__content__header__comparisons__content__comparison--placeholder__text', children: "Add" })] })) }, index));
|
|
576
|
+
}) }), _jsx("div", { className: classnames('ss__chat__content__header__comparisons__action', {
|
|
577
|
+
'ss__chat__content__header__comparisons__action--hidden': (store.currentChat?.comparisons.compared.length || 0) < 2,
|
|
578
|
+
}), children: _jsx(Button, { onClick: () => controller.search(), icon: { icon: 'compare', title: 'Compare' }, children: "Compare" }) })] })] })) }), _jsxs("div", { className: 'ss__chat__messages', ref: messagesContainerRef, role: "log", "aria-live": "polite", "aria-relevant": "additions", "aria-label": "Chat messages", style: visibleChatLength ? { scrollbarGutter: 'stable' } : undefined, children: [(!store.currentChat?.chat || store.currentChat.chat.length === 0) && store.welcomeMessage && !store.currentChat?.isExpired && (_jsxs("div", { className: "ss__chat__welcome", children: [_jsx("div", { className: "ss__chat__welcome__message", children: store.welcomeMessage }), _jsx(ChatSuggestedQuestions, { ...subProps.suggestedQuestions, questions: store.suggestedQuestions, controller: controller })] })), store.currentChat?.chat
|
|
579
|
+
.filter((chatItem) => chatItem.messageType !== 'productQuery')
|
|
580
|
+
.map((chatItem) => {
|
|
581
|
+
const showAvatar = !!(avatarCharacter || avatarImage) && chatItem.messageType !== 'user';
|
|
582
|
+
const messageContent = {
|
|
583
|
+
user: (_jsx(ChatMessageUser, { ...subProps.messageUser, chatItem: chatItem, controller: controller, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
584
|
+
text: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
585
|
+
content: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
586
|
+
productSearchResult: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
587
|
+
inspirationResult: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
588
|
+
productAnswer: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
589
|
+
productComparison: (() => {
|
|
590
|
+
// On mobile, only the first productComparison in the session
|
|
591
|
+
// auto-opens the secondary panel. Subsequent ones get a
|
|
592
|
+
// prominent "Show Details" button instead.
|
|
593
|
+
const comparisonMessages = (store.currentChat?.chat || []).filter((m) => m.messageType === 'productComparison');
|
|
594
|
+
const isFirst = comparisonMessages.length > 0 && comparisonMessages[0].id === chatItem.id;
|
|
595
|
+
return (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, showDetailsButton: isMobile && !isFirst, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText }));
|
|
596
|
+
})(),
|
|
597
|
+
productRecommendation: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onProductQuickView: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
598
|
+
actions: (_jsx(ChatSuggestedQuestions, { actions: chatItem.actions, controller: controller })),
|
|
599
|
+
errorResponse: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
600
|
+
topicDrift: (_jsx(ChatMessageText, { ...subProps.messageText, chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, sideChatOpen: !!shouldShowSideChat, primaryColor: colorPrimary, primaryColorText: colorPrimaryText })),
|
|
601
|
+
productQuery: null,
|
|
602
|
+
}[chatItem.messageType] || null;
|
|
603
|
+
return (_jsxs("div", { className: classnames(`ss__chat__message`, `ss__chat__message--${chatItem.messageType}`, {
|
|
604
|
+
'ss__chat__message--with-avatar': showAvatar,
|
|
605
|
+
}), children: [showAvatar && (_jsx("div", { className: classnames('ss__chat__message__avatar', {
|
|
606
|
+
'ss__chat__message__avatar--image': !!avatarImage,
|
|
607
|
+
}), "aria-hidden": "true", children: avatarImage ? _jsx(Image, { src: avatarImage, alt: "" }) : avatarCharacter })), showAvatar ? _jsx("div", { className: "ss__chat__message__content", children: messageContent }) : messageContent] }, chatItem.id));
|
|
608
|
+
}), _jsx("div", { className: "ss__chat__messages__end", ref: messagesEndRef }), showNewMessages && messagesOverflow && (_jsxs("div", { className: "ss__chat__new-messages", onClick: () => {
|
|
609
|
+
scrollToBottom();
|
|
610
|
+
setShowNewMessages(false);
|
|
611
|
+
}, children: [_jsx(Icon, { icon: "angle-down", size: "12px" }), _jsx("span", { children: "New messages" })] }))] }), _jsx(ChatLoadingIndicator, { ...subProps.loadingIndicator, loading: store.loading, verbs: loadingVerbs }), !store.currentChat?.isExpired ? (_jsxs("div", { className: "ss__chat__content__footer", ref: footerRef, ...dragHandlers, children: [(store.error || store.chatEnabled === false) && (_jsx("div", { className: "ss__chat__error", children: store.error?.message || CHAT_UNAVAILABLE_MESSAGE })), store.currentChat?.sessionLimitReached && (_jsxs("div", { className: 'ss__chat__topic-drift', children: [_jsx(Icon, { icon: "info", size: "18px", className: 'ss__chat__topic-drift__icon--info' }), _jsxs("div", { className: 'ss__chat__topic-drift__text', children: [_jsx("span", { children: "Chat is currently unavailable due to high volume." }), _jsx("span", { children: "In the meantime, feel free to use the search bar above to find what you need!" })] }), _jsx(Button, { className: 'ss__chat__topic-drift__button', onClick: () => {
|
|
612
|
+
controller.store.createChat();
|
|
613
|
+
}, children: "New Session" })] })), store.facets && store.facets.length > 0 && (_jsx("div", { className: 'ss__chat__actions', children: _jsxs("div", { className: "ss__chat__actions__wrap", children: [_jsxs("div", { className: "ss__chat__actions__facets-header", children: [_jsx("div", { className: "ss__chat__actions--title", children: "Filter by:" }), multiselectFacets && store.hasPendingFacetChanges && (_jsx("div", { className: 'ss__chat__actions__facets-apply ss__chat__actions__facets-apply--active', children: _jsx(Button, { onClick: () => {
|
|
614
|
+
controller.search();
|
|
615
|
+
}, children: store.pendingFacetCount > 0 ? `Apply (${store.pendingFacetCount})` : 'Clear facets' }) }))] }), _jsx("div", { className: "ss__chat__actions--facets", children: store.facets.slice(0, 10).map((facet, idx) => {
|
|
616
|
+
// range/slider facets render via FacetSlider; everything else needs at least one value to be useful
|
|
617
|
+
if (facet.type !== 'range' && !facet.values?.length)
|
|
618
|
+
return null;
|
|
619
|
+
if (facet.type === 'range') {
|
|
620
|
+
return _jsx(SliderFacetDropdown, { facet: facet, label: getFacetButtonLabel(facet) }, idx);
|
|
621
|
+
}
|
|
622
|
+
return (_jsx("div", { className: `ss__chat__actions__facet ss__chat__actions__facet--${facet.type}`, children: _jsx(Dropdown, { usePortal: true, dropUp: true, boundaryRef: chatRef, button: _jsx(FacetButton, { label: getFacetButtonLabel(facet) }), style: {
|
|
623
|
+
'.ss__dropdown__content': {
|
|
624
|
+
width: '150px',
|
|
625
|
+
maxHeight: '200px',
|
|
626
|
+
overflowY: 'auto',
|
|
627
|
+
background: 'white',
|
|
628
|
+
boxShadow: '0px 3px 6px 0px rgba(0, 0, 0, 0.2)',
|
|
629
|
+
borderRadius: '6px',
|
|
630
|
+
...(multiselectFacets
|
|
631
|
+
? {
|
|
632
|
+
'.ss__chat__actions__facet__option--checkbox': {
|
|
633
|
+
display: 'flex',
|
|
634
|
+
alignItems: 'center',
|
|
635
|
+
gap: '8px',
|
|
636
|
+
padding: '6px 10px',
|
|
637
|
+
cursor: 'pointer',
|
|
638
|
+
width: '100%',
|
|
639
|
+
boxSizing: 'border-box',
|
|
640
|
+
background: '#fff',
|
|
641
|
+
border: 'none',
|
|
642
|
+
borderBottom: '1px solid #f0f0f0',
|
|
643
|
+
'&:hover': {
|
|
644
|
+
background: new Colour(colorPrimary).lightenHex(0.95),
|
|
645
|
+
},
|
|
646
|
+
'&:last-child': {
|
|
647
|
+
borderBottom: 'none',
|
|
648
|
+
},
|
|
649
|
+
'.ss__chat__actions__facet__option__checkbox': {
|
|
650
|
+
width: '16px',
|
|
651
|
+
height: '16px',
|
|
652
|
+
borderRadius: '3px',
|
|
653
|
+
border: '2px solid #ccc',
|
|
654
|
+
display: 'flex',
|
|
655
|
+
alignItems: 'center',
|
|
656
|
+
justifyContent: 'center',
|
|
657
|
+
flexShrink: 0,
|
|
658
|
+
'&.ss__chat__actions__facet__option__checkbox--checked': {
|
|
659
|
+
background: colorPrimary,
|
|
660
|
+
borderColor: colorPrimary,
|
|
661
|
+
color: colorPrimaryText,
|
|
662
|
+
svg: {
|
|
663
|
+
fill: colorPrimaryText,
|
|
664
|
+
stroke: colorPrimaryText,
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
'.ss__chat__actions__facet__option__label': {
|
|
669
|
+
fontSize: '14px',
|
|
670
|
+
color: '#333',
|
|
671
|
+
flex: '1 1 auto',
|
|
672
|
+
textAlign: 'left',
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
}
|
|
676
|
+
: {
|
|
677
|
+
'.ss__button': {
|
|
678
|
+
width: '100%',
|
|
679
|
+
border: 'none',
|
|
680
|
+
borderRadius: 0,
|
|
681
|
+
boxSizing: 'border-box',
|
|
682
|
+
background: '#fff',
|
|
683
|
+
'&:hover': {
|
|
684
|
+
background: new Colour(colorPrimary).lightenHex(0.95),
|
|
685
|
+
},
|
|
686
|
+
'&.ss__chat__actions__facet__option--selected': {
|
|
687
|
+
fontWeight: 'bold',
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
}),
|
|
691
|
+
},
|
|
692
|
+
}, children: _jsx("div", { className: "ss__chat__actions__facet__options", children: facet.type === 'range-buckets'
|
|
693
|
+
? facet.values.map((option) => {
|
|
694
|
+
const rangeOption = option;
|
|
695
|
+
const optionValue = `${rangeOption.low ?? '*'}:${rangeOption.high ?? '*'}`;
|
|
696
|
+
if (multiselectFacets) {
|
|
697
|
+
const isSelected = controller.store.isFacetSelected(facet.field, optionValue);
|
|
698
|
+
return (_jsxs("div", { className: "ss__chat__actions__facet__option--checkbox", onClick: () => {
|
|
699
|
+
if (isSelected) {
|
|
700
|
+
controller.store.removeFacet(facet.field, optionValue);
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
controller.store.addFacet({
|
|
704
|
+
key: facet.field,
|
|
705
|
+
value: optionValue,
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
}, children: [_jsx("div", { className: classnames('ss__chat__actions__facet__option__checkbox', {
|
|
709
|
+
'ss__chat__actions__facet__option__checkbox--checked': isSelected,
|
|
710
|
+
}), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: rangeOption.label })] }, optionValue));
|
|
711
|
+
}
|
|
712
|
+
return (_jsx(Button, { className: classnames({
|
|
713
|
+
'ss__chat__actions__facet__option--selected': controller.store.isFacetSelected(facet.field, optionValue),
|
|
714
|
+
}), onClick: () => {
|
|
715
|
+
// Non-multiselect: clicking an already-selected value toggles it off.
|
|
716
|
+
// `addFacet` is idempotent against an existing urlManager value, so without
|
|
717
|
+
// the toggle the state wouldn't change, `hasPendingFacetChanges` would stay
|
|
718
|
+
// false, and the params getter would fall through to `general`.
|
|
719
|
+
if (controller.store.isFacetSelected(facet.field, optionValue)) {
|
|
720
|
+
controller.store.removeFacet(facet.field, optionValue);
|
|
721
|
+
}
|
|
722
|
+
else {
|
|
723
|
+
controller.store.addFacet({
|
|
724
|
+
key: facet.field,
|
|
725
|
+
value: optionValue,
|
|
726
|
+
});
|
|
727
|
+
}
|
|
728
|
+
// Force productSearch with the current filter state rather than relying
|
|
729
|
+
// on the params getter's hasPendingFacetChanges promotion — a facet click
|
|
730
|
+
// in the chat is unambiguously a search intent.
|
|
731
|
+
controller.search({
|
|
732
|
+
data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
|
|
733
|
+
});
|
|
734
|
+
}, children: rangeOption.label }, optionValue));
|
|
735
|
+
})
|
|
736
|
+
: facet.values.map((option) => {
|
|
737
|
+
const valueOption = option;
|
|
738
|
+
const optionValue = valueOption.value || valueOption.label;
|
|
739
|
+
if (multiselectFacets) {
|
|
740
|
+
const isSelected = controller.store.isFacetSelected(facet.field, optionValue);
|
|
741
|
+
return (_jsxs("div", { className: "ss__chat__actions__facet__option--checkbox", onClick: () => {
|
|
742
|
+
if (isSelected) {
|
|
743
|
+
controller.store.removeFacet(facet.field, optionValue);
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
controller.store.addFacet({
|
|
747
|
+
key: facet.field,
|
|
748
|
+
value: optionValue,
|
|
749
|
+
});
|
|
750
|
+
}
|
|
751
|
+
}, children: [_jsx("div", { className: classnames('ss__chat__actions__facet__option__checkbox', {
|
|
752
|
+
'ss__chat__actions__facet__option__checkbox--checked': isSelected,
|
|
753
|
+
}), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: valueOption.label })] }, optionValue));
|
|
754
|
+
}
|
|
755
|
+
return (_jsx(Button, { className: classnames({
|
|
756
|
+
'ss__chat__actions__facet__option--selected': controller.store.isFacetSelected(facet.field, optionValue),
|
|
757
|
+
}), onClick: () => {
|
|
758
|
+
// Non-multiselect: clicking an already-selected value toggles it off.
|
|
759
|
+
// `addFacet` is idempotent against an existing urlManager value, so without
|
|
760
|
+
// the toggle the state wouldn't change, `hasPendingFacetChanges` would stay
|
|
761
|
+
// false, and the params getter would fall through to `general`.
|
|
762
|
+
if (controller.store.isFacetSelected(facet.field, optionValue)) {
|
|
763
|
+
controller.store.removeFacet(facet.field, optionValue);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
controller.store.addFacet({
|
|
767
|
+
key: facet.field,
|
|
768
|
+
value: optionValue,
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
// Force productSearch with the current filter state rather than relying
|
|
772
|
+
// on the params getter's hasPendingFacetChanges promotion — a facet click
|
|
773
|
+
// in the chat is unambiguously a search intent.
|
|
774
|
+
controller.search({
|
|
775
|
+
data: { requestType: 'productSearch', searchFilters: controller.store.searchFilters },
|
|
776
|
+
});
|
|
777
|
+
}, children: valueOption.label }, optionValue));
|
|
778
|
+
}) }) }, facet.field) }, idx));
|
|
779
|
+
}) })] }) })), (() => {
|
|
780
|
+
const activeComparisonSearchResults = shouldShowSideChat && activeMessage?.messageType === 'productComparison'
|
|
781
|
+
? activeMessage.searchResults || []
|
|
782
|
+
: null;
|
|
783
|
+
const showCommittedComparisons = !activeComparisonSearchResults &&
|
|
784
|
+
store.currentChat?.comparisons.committed &&
|
|
785
|
+
store.currentChat.comparisons.committed.length > 0 &&
|
|
786
|
+
(activeMessage?.messageType === 'user' ||
|
|
787
|
+
activeMessage?.messageType === 'productComparison' ||
|
|
788
|
+
activeMessage?.messageType === 'productQuery');
|
|
789
|
+
const visibleAttachments = store.currentChat?.attachments.attached.filter((item) => item.state === 'attached' || item.state === 'active' || item.state === 'loading' || item.state === 'error') || [];
|
|
790
|
+
const productAttachments = visibleAttachments.filter((item) => item.type === 'product' &&
|
|
791
|
+
item.requestType !== 'productSimilar' &&
|
|
792
|
+
item.requestType !== 'productComparison');
|
|
793
|
+
const imageAttachments = visibleAttachments.filter((item) => item.type === 'image');
|
|
794
|
+
const comparisonItems = activeComparisonSearchResults
|
|
795
|
+
? activeComparisonSearchResults.map((result) => {
|
|
796
|
+
const d = result?.display || result;
|
|
797
|
+
return {
|
|
798
|
+
id: result?.id,
|
|
799
|
+
name: d?.mappings?.core?.name || '',
|
|
800
|
+
imageUrl: d?.mappings?.core?.thumbnailImageUrl || d?.mappings?.core?.imageUrl || d?.mappings?.core?.parentImageUrl || '',
|
|
801
|
+
onClick: () => {
|
|
802
|
+
controller.productQuickView(result);
|
|
803
|
+
if (isMobile)
|
|
804
|
+
setMobileProductInfoOpen(true);
|
|
805
|
+
},
|
|
806
|
+
};
|
|
807
|
+
})
|
|
808
|
+
: showCommittedComparisons
|
|
809
|
+
? (store.currentChat?.comparisons.committed || []).map((comparisonItem) => {
|
|
810
|
+
const d = comparisonItem?.result?.display || comparisonItem?.result;
|
|
811
|
+
return {
|
|
812
|
+
id: comparisonItem.result?.id,
|
|
813
|
+
name: d?.mappings?.core?.name || '',
|
|
814
|
+
imageUrl: d?.mappings?.core?.thumbnailImageUrl || d?.mappings?.core?.imageUrl || d?.mappings?.core?.parentImageUrl || '',
|
|
815
|
+
onClick: () => {
|
|
816
|
+
controller.productQuickView(comparisonItem.result);
|
|
817
|
+
if (isMobile)
|
|
818
|
+
setMobileProductInfoOpen(true);
|
|
819
|
+
},
|
|
820
|
+
};
|
|
821
|
+
})
|
|
822
|
+
: [];
|
|
823
|
+
const productItems = productAttachments.map((item) => {
|
|
824
|
+
const remove = () => {
|
|
825
|
+
store.currentChat?.attachments.remove(item.id);
|
|
826
|
+
setMobileProductInfoOpen(false);
|
|
827
|
+
store.currentChat?.dismissSideChat();
|
|
828
|
+
};
|
|
829
|
+
return {
|
|
830
|
+
id: item.id,
|
|
831
|
+
name: item.name || '',
|
|
832
|
+
imageUrl: item.thumbnailUrl || '',
|
|
833
|
+
onClick: remove,
|
|
834
|
+
onRemove: remove,
|
|
835
|
+
};
|
|
836
|
+
});
|
|
837
|
+
const imageItems = imageAttachments.map((item) => ({
|
|
838
|
+
id: item.id,
|
|
839
|
+
name: item.fileName || 'Image',
|
|
840
|
+
imageUrl: item.base64 || item.thumbnailUrl || '',
|
|
841
|
+
isLoading: item.state === 'loading',
|
|
842
|
+
hasError: !!item.error,
|
|
843
|
+
errorMessage: item.error?.message,
|
|
844
|
+
onRemove: () => store.currentChat?.attachments.remove(item.id),
|
|
845
|
+
}));
|
|
846
|
+
const productTitle = productItems.length > 1 ? 'Compare these products' : 'Ask about this product';
|
|
847
|
+
const hasImageError = imageItems.some((item) => item.hasError);
|
|
848
|
+
const imageTitle = imageItems.length > 0 && !hasImageError ? 'Find products similar to this image:' : '';
|
|
849
|
+
return (_jsxs(_Fragment, { children: [comparisonItems.length > 0 && (_jsx(ChatAttachmentContext, { ...subProps.attachmentContext, title: 'Compare these products', items: comparisonItems, onClose: () => {
|
|
850
|
+
store.currentChat?.comparisons.resetCommitted();
|
|
851
|
+
store.currentChat?.dismissSideChat();
|
|
852
|
+
} })), productItems.length > 0 && (_jsx(ChatAttachmentContext, { ...subProps.attachmentContext, title: productTitle, items: productItems, onClose: () => {
|
|
853
|
+
productAttachments.forEach((item) => store.currentChat?.attachments.remove(item.id));
|
|
854
|
+
setMobileProductInfoOpen(false);
|
|
855
|
+
store.currentChat?.dismissSideChat();
|
|
856
|
+
} })), imageItems.length > 0 && _jsx(ChatAttachmentContext, { ...subProps.attachmentContext, title: imageTitle, items: imageItems })] }));
|
|
857
|
+
})(), (() => {
|
|
858
|
+
const drift = store.currentChat?.topicDrift;
|
|
859
|
+
if (!drift)
|
|
860
|
+
return null;
|
|
861
|
+
// recommendedAction is the prescription; driftType is the diagnosis.
|
|
862
|
+
// Hide the banner when the backend says to keep flowing, or when no drift was detected.
|
|
863
|
+
if (drift.recommendedAction === 'CONTINUE' || drift.driftType === 'NO_DRIFT')
|
|
864
|
+
return null;
|
|
865
|
+
const isScopeRedirect = drift.recommendedAction === 'SCOPE_REDIRECT';
|
|
866
|
+
const primaryText = isScopeRedirect ? "I'm here to help with shopping" : 'Looking for something new?';
|
|
867
|
+
const secondaryText = isScopeRedirect
|
|
868
|
+
? 'Try asking about products, comparisons, or recommendations'
|
|
869
|
+
: 'Start a fresh session for better assistance, or keep going in this one?';
|
|
870
|
+
const buttonText = isScopeRedirect ? '' : 'New Session';
|
|
871
|
+
return (_jsxs("div", { className: 'ss__chat__topic-drift', children: [_jsx(Icon, { icon: "info", size: "18px", className: 'ss__chat__topic-drift__icon--info' }), _jsxs("div", { className: 'ss__chat__topic-drift__text', children: [_jsx("span", { children: primaryText }), _jsx("span", { children: secondaryText })] }), buttonText && (_jsx(Button, { className: 'ss__chat__topic-drift__button', onClick: () => {
|
|
872
|
+
if (isScopeRedirect) {
|
|
873
|
+
store.currentChat?.dismissTopicDrift();
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
// Capture image attachments from the current chat before handleTopicDrift /
|
|
877
|
+
// createChat tear down the conversation — an imageSearch that drifted needs
|
|
878
|
+
// the same image attached to the carried-over message in the new session,
|
|
879
|
+
// otherwise params would fall back to a `general` request without the image.
|
|
880
|
+
const previousImageAttachments = (store.currentChat?.attachments.attached || [])
|
|
881
|
+
.filter((item) => item.type === 'image' && item.state !== 'error')
|
|
882
|
+
.map((item) => {
|
|
883
|
+
const img = item;
|
|
884
|
+
return {
|
|
885
|
+
type: 'image',
|
|
886
|
+
base64: img.base64,
|
|
887
|
+
fileName: img.fileName,
|
|
888
|
+
imageId: img.imageId,
|
|
889
|
+
imageUrl: img.imageUrl,
|
|
890
|
+
thumbnailUrl: img.thumbnailUrl,
|
|
891
|
+
// Already uploaded — skip the 'loading' default state.
|
|
892
|
+
state: 'attached',
|
|
893
|
+
};
|
|
894
|
+
});
|
|
895
|
+
const messageText = store.currentChat?.handleTopicDrift();
|
|
896
|
+
if (messageText) {
|
|
897
|
+
controller.store.createChat();
|
|
898
|
+
previousImageAttachments.forEach((cfg) => {
|
|
899
|
+
controller.store.currentChat?.attachments.add(cfg);
|
|
900
|
+
});
|
|
901
|
+
controller.search({ data: { message: messageText } });
|
|
902
|
+
}
|
|
903
|
+
}, children: buttonText })), _jsx("span", { onClick: () => {
|
|
904
|
+
store.currentChat?.dismissTopicDrift();
|
|
905
|
+
}, children: _jsx(Icon, { icon: "close-thin", size: "14px", className: 'ss__chat__topic-drift__icon--close' }) })] }));
|
|
906
|
+
})(), _jsxs("div", { className: 'ss__chat__input', children: [_jsxs("div", { className: 'ss__chat__input__input', children: [_jsx("input", { type: "text", name: "ss-chat-input", disabled: store.loading || store.blocked || store.currentChat?.sessionLimitReached || store.chatEnabled === false, placeholder: (() => {
|
|
907
|
+
const comparedCount = store.currentChat?.comparisons.compared.length || 0;
|
|
908
|
+
const committedCount = store.currentChat?.comparisons.committed.length || 0;
|
|
909
|
+
const attached = store.currentChat?.attachments.attached.filter((item) => item.state === 'attached' || item.state === 'active') || [];
|
|
910
|
+
const attachedProducts = attached.filter((item) => item.type === 'product');
|
|
911
|
+
const attachedImages = attached.filter((item) => item.type === 'image' && !item.error);
|
|
912
|
+
if (attachedProducts.length === 1) {
|
|
913
|
+
return 'Ask about this product...';
|
|
914
|
+
}
|
|
915
|
+
if (comparedCount > 1 || attachedProducts.length > 1) {
|
|
916
|
+
return 'What would you like to compare?';
|
|
917
|
+
}
|
|
918
|
+
if (committedCount > 0) {
|
|
919
|
+
return 'Ask about the compared products...';
|
|
920
|
+
}
|
|
921
|
+
if (attachedImages.length > 0) {
|
|
922
|
+
return 'Ask about this image...';
|
|
923
|
+
}
|
|
924
|
+
if (comparedCount === 1) {
|
|
925
|
+
return 'Add another product to compare...';
|
|
926
|
+
}
|
|
927
|
+
return 'Type your message...';
|
|
928
|
+
})(), onInput: (e) => controller.handlers.input.input(e), onKeyDown: (e) => {
|
|
929
|
+
controller.handlers.input.enterKey(e);
|
|
930
|
+
if (e.keyCode === 13 && isMobile && mobileProductInfoOpen) {
|
|
931
|
+
setMobileProductInfoOpen(false);
|
|
932
|
+
}
|
|
933
|
+
}, value: controller.store.inputValue }), store.features.imageSearch.enabled && (_jsxs(_Fragment, { children: [_jsx(Button, { className: 'ss__chat__upload-button', disabled: store.loading || store.blocked || store.currentChat?.sessionLimitReached || store.chatEnabled === false, onClick: () => fileInputRef.current?.click(), icon: { icon: 'image', title: 'Upload Image' } }), _jsx("input", { ref: fileInputRef, onChange: async (e) => {
|
|
934
|
+
await controller.upload(e.target.files);
|
|
935
|
+
// reset value
|
|
936
|
+
e.target.value = '';
|
|
937
|
+
}, type: "file", accept: "image/*", className: "ss__chat__input__input__file" })] }))] }), _jsx("div", { className: 'ss__chat__input__actions', children: (() => {
|
|
938
|
+
const hasImageAttachment = (store.currentChat?.attachments.attached || []).some((item) => item.type === 'image' && !item.error);
|
|
939
|
+
const messageEmpty = !controller.store.inputValue.trim();
|
|
940
|
+
const sendDisabled = store.blocked ||
|
|
941
|
+
store.currentChat?.sessionLimitReached ||
|
|
942
|
+
store.chatEnabled === false ||
|
|
943
|
+
(hasImageAttachment && messageEmpty) ||
|
|
944
|
+
messageEmpty;
|
|
945
|
+
return (_jsx(Button, { className: "ss__chat__send-button", icon: { icon: 'send', title: 'Send Message' }, disabled: sendDisabled, onClick: () => {
|
|
946
|
+
if (sendDisabled)
|
|
947
|
+
return;
|
|
948
|
+
controller.search();
|
|
949
|
+
if (isMobile && mobileProductInfoOpen) {
|
|
950
|
+
setMobileProductInfoOpen(false);
|
|
951
|
+
}
|
|
952
|
+
} }));
|
|
953
|
+
})() })] }), _jsx("div", { className: 'ss__chat__disclaimer', children: _jsxs("i", { children: ["Powered by Athos Commerce. AI-powered assistant. It sometimes make mistakes. Avoid sharing personal data.", ' ', _jsx("a", { href: "https://athoscommerce.com/legal/privacy-policy/", target: "_blank", rel: "noopener noreferrer", children: "Privacy Policy" })] }) }), isDraggingFile && (_jsxs("div", { className: "ss__chat__content__footer__drop-overlay", children: [_jsx(Icon, { icon: "image", size: "24px" }), _jsx("span", { children: "Drop image to upload" })] }))] })) : (_jsxs("div", { className: 'ss__chat__expired', children: [_jsx("div", { className: 'ss__chat__expired__message', children: "This chat is expired. Please start a new chat." }), _jsx(Button, { className: 'ss__chat__expired__button', onClick: () => {
|
|
954
|
+
controller.store.createChat();
|
|
955
|
+
}, children: "New Session" })] }))] })] })) : null] }), _jsx(Overlay, { style: { zIndex: 1001 }, color: "transparent", active: store.open, onClick: () => controller.handlers.button.click() })] }) }));
|
|
956
|
+
});
|