@athoscommerce/snap-preact 1.2.3-beta.2 → 1.2.3-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts.map +1 -1
  2. package/dist/cjs/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.js +4 -1
  3. package/dist/cjs/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +1 -1
  4. package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -1
  5. package/dist/cjs/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +8 -2
  6. package/dist/cjs/components/src/components/Organisms/Chat/Chat.d.ts +1 -0
  7. package/dist/cjs/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -1
  8. package/dist/cjs/components/src/components/Organisms/Chat/Chat.js +238 -53
  9. package/dist/cjs/components/src/components/Organisms/Chat/ChatResult.d.ts +2 -3
  10. package/dist/cjs/components/src/components/Organisms/Chat/ChatResult.d.ts.map +1 -1
  11. package/dist/cjs/components/src/components/Organisms/Chat/ChatResult.js +26 -7
  12. package/dist/cjs/components/src/components/Organisms/Chat/MessageUser.d.ts.map +1 -1
  13. package/dist/cjs/components/src/components/Organisms/Chat/MessageUser.js +54 -23
  14. package/dist/cjs/components/src/components/Organisms/Chat/ResultsDisplay.d.ts.map +1 -1
  15. package/dist/cjs/components/src/components/Organisms/Chat/ResultsDisplay.js +14 -6
  16. package/dist/cjs/components/src/components/Organisms/Facet/Facet.js +2 -1
  17. package/dist/cjs/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.js +2 -1
  18. package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.d.ts.map +1 -1
  19. package/dist/esm/components/src/components/Molecules/ChatInspirationResultMessage/ChatInspirationResultMessage.js +4 -1
  20. package/dist/esm/components/src/components/Molecules/ChatProductComparisonMessage/ChatProductComparisonMessage.js +1 -1
  21. package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.d.ts.map +1 -1
  22. package/dist/esm/components/src/components/Molecules/ChatProductQueryMessage/ChatProductQueryMessage.js +8 -2
  23. package/dist/esm/components/src/components/Organisms/Chat/Chat.d.ts +1 -0
  24. package/dist/esm/components/src/components/Organisms/Chat/Chat.d.ts.map +1 -1
  25. package/dist/esm/components/src/components/Organisms/Chat/Chat.js +231 -49
  26. package/dist/esm/components/src/components/Organisms/Chat/ChatResult.d.ts +2 -3
  27. package/dist/esm/components/src/components/Organisms/Chat/ChatResult.d.ts.map +1 -1
  28. package/dist/esm/components/src/components/Organisms/Chat/ChatResult.js +26 -7
  29. package/dist/esm/components/src/components/Organisms/Chat/MessageUser.d.ts.map +1 -1
  30. package/dist/esm/components/src/components/Organisms/Chat/MessageUser.js +51 -21
  31. package/dist/esm/components/src/components/Organisms/Chat/ResultsDisplay.d.ts.map +1 -1
  32. package/dist/esm/components/src/components/Organisms/Chat/ResultsDisplay.js +14 -6
  33. package/dist/esm/components/src/components/Organisms/Facet/Facet.js +1 -1
  34. package/dist/esm/components/src/components/Organisms/FacetsHorizontal/FacetsHorizontal.js +1 -1
  35. package/package.json +12 -12
@@ -170,11 +170,11 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
170
170
  padding: '12px 16px',
171
171
  fontSize: '14px',
172
172
  color: '#333',
173
- boxShadow: '0 2px 8px rgba(0, 0, 0, 0.1)',
173
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.2)',
174
174
  cursor: 'pointer',
175
175
  lineHeight: 1.4,
176
176
  '&:hover': {
177
- boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
177
+ boxShadow: '0 4px 12px rgba(0, 0, 0, 0.2)',
178
178
  },
179
179
  },
180
180
  },
@@ -220,6 +220,7 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
220
220
  display: 'flex',
221
221
  '.ss__chat__header__title__logo': {
222
222
  height: '44px',
223
+ width: 'auto',
223
224
  },
224
225
  '.ss__chat__header__title__text': {
225
226
  display: 'flex',
@@ -390,7 +391,19 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
390
391
  borderBottomRightRadius: mobile ? 0 : '12px',
391
392
  borderBottomLeftRadius: mobile ? 0 : '12px',
392
393
  flexGrow: 1,
394
+ position: 'relative',
393
395
  '.ss__chat__content__header': {
396
+ // In overlay mode (only applied when messages overflow), the comparisons
397
+ // tray floats over the messages so toggling it doesn't shift the layout
398
+ // or push the latest message out of view. When messages don't overflow,
399
+ // the tray stays in flow so older messages remain reachable above it.
400
+ '&.ss__chat__content__header--overlay': {
401
+ position: 'absolute',
402
+ top: 0,
403
+ left: 0,
404
+ right: 0,
405
+ zIndex: 20,
406
+ },
394
407
  // '.ss__chat__attachments': {
395
408
  // '.ss__chat__attachment': {
396
409
  // borderRadius: 0,
@@ -435,9 +448,9 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
435
448
  alignItems: 'stretch',
436
449
  '.ss__chat__content__header__comparisons__content__items': {
437
450
  display: 'flex',
438
- gap: '1em',
439
451
  flex: '1 1 calc(100% - 100px)',
440
452
  justifyContent: 'space-between',
453
+ gap: '0.5em',
441
454
  },
442
455
  '.ss__chat__content__header__comparisons__content__comparison': {
443
456
  background: '#fff',
@@ -446,7 +459,7 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
446
459
  display: 'flex',
447
460
  flexDirection: 'column',
448
461
  gap: '0.5em',
449
- flex: '0 1 20%',
462
+ flex: '0 1 25%',
450
463
  position: 'relative',
451
464
  '&.ss__chat__content__header__comparisons__content__comparison--placeholder': {
452
465
  border: '2px dashed #ddd',
@@ -511,6 +524,32 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
511
524
  '.ss__chat__messages__end': {
512
525
  height: '1px',
513
526
  },
527
+ '.ss__chat__new-messages': {
528
+ position: 'sticky',
529
+ bottom: '10px',
530
+ display: 'flex',
531
+ alignItems: 'center',
532
+ justifyContent: 'center',
533
+ gap: '6px',
534
+ margin: '0 auto',
535
+ width: 'fit-content',
536
+ padding: '6px 16px',
537
+ background: colorPrimary,
538
+ color: '#fff',
539
+ borderRadius: '999px',
540
+ fontSize: '13px',
541
+ fontWeight: 500,
542
+ cursor: 'pointer',
543
+ boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
544
+ zIndex: 5,
545
+ '&:hover': {
546
+ background: '#1a2a5c',
547
+ },
548
+ svg: {
549
+ fill: '#fff',
550
+ stroke: '#fff',
551
+ },
552
+ },
514
553
  '.ss__chat__message': {
515
554
  marginBottom: '30px',
516
555
  ul: {
@@ -537,7 +576,7 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
537
576
  '.ss__chat__welcome': {
538
577
  display: 'flex',
539
578
  flexDirection: 'column',
540
- height: '100%',
579
+ minHeight: '100%',
541
580
  '.ss__chat__welcome__message': {
542
581
  fontSize: '15px',
543
582
  lineHeight: 1.5,
@@ -675,13 +714,19 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
675
714
  display: 'flex',
676
715
  justifyContent: 'flex-end',
677
716
  padding: '0 2px',
717
+ // reserve the slot so toggling the button's visibility doesn't reflow the header
718
+ visibility: 'hidden',
719
+ '&.ss__chat__actions__facets-apply--active': {
720
+ visibility: 'visible',
721
+ },
678
722
  '.ss__button': {
679
723
  background: '#253B80',
680
724
  color: '#fff',
681
725
  border: 'none',
682
726
  borderRadius: '999px',
683
- padding: '6px 16px',
684
- fontSize: '14px',
727
+ padding: '2px 10px',
728
+ fontSize: '12px',
729
+ lineHeight: '1.4',
685
730
  cursor: 'pointer',
686
731
  '&:not(.ss__button--disabled):hover': {
687
732
  background: '#1a2a5c',
@@ -926,6 +971,7 @@ const defaultStyles = ({ mobile, offset, hasSideChat, footerHeight }) => {
926
971
  padding: '0.5em 0',
927
972
  margin: '0 0 0 1em',
928
973
  flex: '1 0 auto',
974
+ border: 'none',
929
975
  '&::placeholder': {
930
976
  color: '#999',
931
977
  opacity: 0.7,
@@ -983,9 +1029,10 @@ export const Chat = observer((properties) => {
983
1029
  title: 'Athos Conversational Assistant',
984
1030
  subtitle: 'Ready to assist you',
985
1031
  multiselectFacets: true,
1032
+ disableBubbleSuggestedQuestions: false,
986
1033
  };
987
1034
  let props = mergeProps('facets', globalTheme, defaultProps, properties);
988
- const { className, internalClassName, controller, logo, title, subtitle, offset, multiselectFacets } = props;
1035
+ const { className, internalClassName, controller, logo, title, subtitle, offset, multiselectFacets, disableBubbleSuggestedQuestions } = props;
989
1036
  const { store } = controller;
990
1037
  const themeDefaults = {
991
1038
  components: {},
@@ -999,6 +1046,7 @@ export const Chat = observer((properties) => {
999
1046
  const fileInputRef = useRef(null);
1000
1047
  const messagesEndRef = useRef(null);
1001
1048
  const messagesContainerRef = useRef(null);
1049
+ const headerRef = useRef(null);
1002
1050
  const footerRef = useRef(null);
1003
1051
  // Swipe-to-dismiss state for mobile secondary panel
1004
1052
  const secondaryRef = useRef(null);
@@ -1010,22 +1058,124 @@ export const Chat = observer((properties) => {
1010
1058
  const [swipeAnimating, setSwipeAnimating] = useState(false);
1011
1059
  const [mobileProductInfoOpen, setMobileProductInfoOpen] = useState(false);
1012
1060
  const [footerHeight, setFooterHeight] = useState(0);
1061
+ const [showNewMessages, setShowNewMessages] = useState(false);
1062
+ const [messagesOverflow, setMessagesOverflow] = useState(false);
1063
+ const isNearBottomRef = useRef(true);
1013
1064
  const updateSwipeOffset = (value) => {
1014
1065
  swipeOffsetRef.current = value;
1015
1066
  setSwipeOffsetRaw(value);
1016
1067
  };
1017
1068
  const scrollToBottom = () => {
1018
- messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
1069
+ // Scroll the messages container directly (instead of scrollIntoView on the
1070
+ // bottom anchor) so the browser never walks up the ancestor chain. On small
1071
+ // viewports a smooth scrollIntoView could otherwise animate the document/
1072
+ // visual viewport and visually push the fixed-position chat off-screen.
1073
+ const container = messagesContainerRef.current;
1074
+ if (container) {
1075
+ container.scrollTo({ top: container.scrollHeight, behavior: 'smooth' });
1076
+ }
1077
+ };
1078
+ // Conditional scroll: only scrolls if the user is already near the bottom.
1079
+ // Passed to child components (e.g. image onLoad) so they don't yank the user
1080
+ // back to the bottom when they have scrolled up to read older messages.
1081
+ const scrollToBottomIfNear = () => {
1082
+ if (isNearBottomRef.current) {
1083
+ scrollToBottom();
1084
+ }
1019
1085
  };
1020
- // Auto-scroll to bottom when new messages are added or chat state changes
1086
+ // productQuery messages are filtered out of the main chat render (they only drive the side-chat panel),
1087
+ // so don't auto-scroll when one is added — otherwise clicking a product in the side chat scrolls the main thread.
1088
+ const visibleChatLength = (store.currentChat?.chat || []).filter((m) => m.messageType !== 'productQuery').length;
1089
+ // Scroll to bottom when chat opens
1021
1090
  useEffect(() => {
1022
- // if (store.open && messagesContainerRef.current) {
1023
- // messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
1024
- // }
1025
1091
  if (store.open) {
1026
1092
  scrollToBottom();
1093
+ setShowNewMessages(false);
1094
+ isNearBottomRef.current = true;
1027
1095
  }
1028
- }, [store.currentChat?.chat.length, store.loading, store.open]);
1096
+ }, [store.open]);
1097
+ // Handle new messages — auto-scroll for user messages, show popup for response messages when scrolled up
1098
+ useEffect(() => {
1099
+ if (!store.open)
1100
+ return;
1101
+ const chat = store.currentChat?.chat || [];
1102
+ const visibleChat = chat.filter((m) => m.messageType !== 'productQuery');
1103
+ const lastMessage = visibleChat[visibleChat.length - 1];
1104
+ if (!lastMessage)
1105
+ return;
1106
+ if (lastMessage.messageType === 'user') {
1107
+ scrollToBottom();
1108
+ setShowNewMessages(false);
1109
+ }
1110
+ else {
1111
+ if (isNearBottomRef.current) {
1112
+ scrollToBottom();
1113
+ setShowNewMessages(false);
1114
+ }
1115
+ else {
1116
+ setShowNewMessages(true);
1117
+ }
1118
+ }
1119
+ }, [visibleChatLength]);
1120
+ // Scroll to show loading indicator when user sends a message
1121
+ useEffect(() => {
1122
+ if (store.open && store.loading) {
1123
+ scrollToBottom();
1124
+ }
1125
+ }, [store.loading]);
1126
+ // Track scroll position to dismiss the "New messages" popup when user scrolls to bottom
1127
+ useEffect(() => {
1128
+ const container = messagesContainerRef.current;
1129
+ if (!container)
1130
+ return;
1131
+ const handleScroll = () => {
1132
+ const threshold = 100;
1133
+ const isNearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < threshold;
1134
+ isNearBottomRef.current = isNearBottom;
1135
+ if (isNearBottom) {
1136
+ setShowNewMessages(false);
1137
+ }
1138
+ };
1139
+ container.addEventListener('scroll', handleScroll);
1140
+ return () => container.removeEventListener('scroll', handleScroll);
1141
+ }, [store.open]);
1142
+ // Track whether the messages container is overflowing so the comparisons header
1143
+ // can switch between in-flow (short conversations — top content stays reachable)
1144
+ // and overlay (overflowing — avoid layout shift when toggling the tray).
1145
+ // Hysteresis: when already in overlay, only return to in-flow if the content
1146
+ // would still fit *with* the header re-inserted — otherwise the two states
1147
+ // fight each other and the layout flickers.
1148
+ useEffect(() => {
1149
+ const container = messagesContainerRef.current;
1150
+ if (!container || typeof ResizeObserver === 'undefined')
1151
+ return;
1152
+ const update = () => {
1153
+ setMessagesOverflow((prev) => {
1154
+ const headerHeight = headerRef.current?.offsetHeight || 0;
1155
+ if (prev) {
1156
+ // Currently overlay: header isn't taking flow space. Only switch
1157
+ // back to in-flow if the content would fit with the header added.
1158
+ return container.scrollHeight > container.clientHeight - headerHeight + 1;
1159
+ }
1160
+ // Currently in-flow: header already takes space — straightforward check.
1161
+ return container.scrollHeight > container.clientHeight + 1;
1162
+ });
1163
+ };
1164
+ update();
1165
+ const observer = new ResizeObserver(update);
1166
+ observer.observe(container);
1167
+ if (headerRef.current)
1168
+ observer.observe(headerRef.current);
1169
+ return () => observer.disconnect();
1170
+ }, [store.open, visibleChatLength]);
1171
+ // Re-focus the chat input on desktop after a search completes.
1172
+ // The input is disabled while loading, which causes the browser to drop focus.
1173
+ // On mobile we intentionally skip this so the virtual keyboard dismisses.
1174
+ useEffect(() => {
1175
+ if (!store.loading && !isMobile && store.open) {
1176
+ controller.focusInput();
1177
+ }
1178
+ }, [store.loading]);
1029
1179
  // const subProps: ChatSubProps = {};
1030
1180
  const activeMessage = store.currentChat?.activeMessage;
1031
1181
  // On mobile, only auto-open the secondary panel for the first productComparison
@@ -1090,6 +1240,26 @@ export const Chat = observer((properties) => {
1090
1240
  return text.length > 50 ? `${text.slice(0, 50)}...` : text;
1091
1241
  })() }), _jsx("div", { className: "ss__chat__history__chat__button__date", children: chat.createdAt.toLocaleString() })] }) }, chat.id))) })) : null] }));
1092
1242
  const FacetButton = (props) => _jsx(Button, { icon: props.open ? 'angle-down' : 'angle-up', children: props.label });
1243
+ const getFacetButtonLabel = (facet) => {
1244
+ const applied = new Set();
1245
+ // Count values marked as filtered (already applied on the server)
1246
+ (facet.values || []).forEach((value) => {
1247
+ if (value?.filtered) {
1248
+ if ('low' in value && 'high' in value) {
1249
+ applied.add(`${value.low ?? '*'}:${value.high ?? '*'}`);
1250
+ }
1251
+ else {
1252
+ applied.add(value.value);
1253
+ }
1254
+ }
1255
+ });
1256
+ // Count pending facet attachments (newly selected, not yet sent)
1257
+ (controller.store.currentChat?.attachments.items || [])
1258
+ .filter((item) => item.type === 'facet' && item.key === facet.field && (item.state === 'active' || item.state === 'attached'))
1259
+ .forEach((item) => applied.add(item.value));
1260
+ const base = facet.label || facet.field;
1261
+ return applied.size > 0 ? `${base} (${applied.size})` : base;
1262
+ };
1093
1263
  // TODO: if starting a new chat and it's expired, this button would then disappear
1094
1264
  if (!controller.store.chatEnabled) {
1095
1265
  return _jsx(_Fragment, {});
@@ -1178,7 +1348,7 @@ export const Chat = observer((properties) => {
1178
1348
  'ss__chat--open': store.open,
1179
1349
  'ss__chat--minimized': !store.open,
1180
1350
  'ss__chat--mobile': isMobile,
1181
- }, className, internalClassName), ...styling, children: [!store.open && !store.currentChat && store.suggestedQuestions?.length > 0 && (_jsx("div", { className: "ss__chat__suggested-questions", children: store.suggestedQuestions.map((question, index) => (_jsx("div", { className: "ss__chat__suggested-questions__item", onClick: () => {
1351
+ }, 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, index) => (_jsx("div", { className: "ss__chat__suggested-questions__item", onClick: () => {
1182
1352
  controller.openChat(question);
1183
1353
  }, children: question }, index))) })), _jsx("div", { className: 'ss__chat__bubble', 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'), style: isMobile
1184
1354
  ? {
@@ -1192,8 +1362,7 @@ export const Chat = observer((properties) => {
1192
1362
  productQuery: 'Product Information',
1193
1363
  }[activeMessage.messageType] || null }), _jsx("div", { className: "ss__chat__header__title__secondary", children: {
1194
1364
  inspirationResult: 'Choose a style direction to explore',
1195
- productComparison: `Comparing ${activeMessage?.comparisonData?.features.length ||
1196
- activeMessage?.searchResults?.length} products`,
1365
+ productComparison: `Comparing ${activeMessage?.searchResults?.length || ''} products`,
1197
1366
  productQuery: 'Complete product details',
1198
1367
  }[activeMessage.messageType] || null })] }), _jsx("div", { className: "ss__chat__header__buttons", children: _jsx(Button, { className: "ss__chat__header__button--close", icon: { icon: 'close2', title: 'Close Chat' }, onClick: () => {
1199
1368
  // clear any product attachments tied to the side chat (discuss product flow)
@@ -1228,7 +1397,9 @@ export const Chat = observer((properties) => {
1228
1397
  store.currentChat.save();
1229
1398
  }
1230
1399
  }, children: _jsx(Icon, { icon: "close-thin", size: "14px" }) }))] })) : null;
1231
- })(), _jsxs("div", { className: "ss__chat__content", children: [_jsx("div", { className: "ss__chat__content__header", 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) => {
1400
+ })(), _jsxs("div", { className: "ss__chat__content", children: [_jsx("div", { ref: headerRef, className: classnames('ss__chat__content__header', {
1401
+ 'ss__chat__content__header--overlay': messagesOverflow,
1402
+ }), 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) => {
1232
1403
  const comparisonItem = store.currentChat?.comparisons.compared[index];
1233
1404
  return (_jsx("div", { className: classnames('ss__chat__content__header__comparisons__content__comparison', {
1234
1405
  'ss__chat__content__header__comparisons__content__comparison--placeholder': !comparisonItem,
@@ -1245,37 +1416,43 @@ export const Chat = observer((properties) => {
1245
1416
  }, 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));
1246
1417
  }) }), _jsx("div", { className: classnames('ss__chat__content__header__comparisons__action', {
1247
1418
  'ss__chat__content__header__comparisons__action--hidden': (store.currentChat?.comparisons.compared.length || 0) < 2,
1248
- }), children: _jsx(Button, { onClick: () => controller.search(), icon: { icon: 'compare', title: 'Compare' }, children: "Compare" }) })] })] })) }), _jsxs("div", { className: 'ss__chat__messages', ref: messagesContainerRef, children: [(!store.currentChat?.chat || store.currentChat.chat.length === 0) && store.welcomeMessage && (_jsxs("div", { className: "ss__chat__welcome", children: [_jsx("div", { className: "ss__chat__welcome__message", children: store.welcomeMessage }), _jsx(SuggestedQuestions, { questions: store.suggestedQuestions, controller: controller })] })), store.currentChat?.chat
1419
+ }), children: _jsx(Button, { onClick: () => controller.search(), icon: { icon: 'compare', title: 'Compare' }, children: "Compare" }) })] })] })) }), _jsxs("div", { className: 'ss__chat__messages', ref: messagesContainerRef, 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(SuggestedQuestions, { questions: store.suggestedQuestions, controller: controller })] })), store.currentChat?.chat
1249
1420
  .filter((chatItem) => chatItem.messageType !== 'productQuery')
1250
1421
  .map((chatItem, index) => (_jsx("div", { className: `ss__chat__message ss__chat__message--${chatItem.messageType}`, children: {
1251
1422
  user: (_jsx(MessageUser, { chatItem: chatItem, controller: controller, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined })),
1252
- text: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1253
- content: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1254
- productSearchResult: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1255
- inspirationResult: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1256
- productAnswer: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1423
+ text: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1424
+ content: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1425
+ productSearchResult: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1426
+ inspirationResult: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1427
+ productAnswer: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1257
1428
  productComparison: (() => {
1258
1429
  // On mobile, only the first productComparison in the session
1259
1430
  // auto-opens the secondary panel. Subsequent ones get a
1260
1431
  // prominent "Show Details" button instead.
1261
1432
  const comparisonMessages = (store.currentChat?.chat || []).filter((m) => m.messageType === 'productComparison');
1262
1433
  const isFirst = comparisonMessages.length > 0 && comparisonMessages[0].id === chatItem.id;
1263
- return (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, showDetailsButton: isMobile && !isFirst, sideChatOpen: !!shouldShowSideChat }));
1434
+ return (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, showDetailsButton: isMobile && !isFirst, sideChatOpen: !!shouldShowSideChat }));
1264
1435
  })(),
1265
- productRecommendation: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1436
+ productRecommendation: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, onViewProduct: isMobile ? () => setMobileProductInfoOpen(true) : undefined, sideChatOpen: !!shouldShowSideChat })),
1266
1437
  actions: _jsx(SuggestedQuestions, { actions: chatItem.actions, controller: controller }),
1267
- errorResponse: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, sideChatOpen: !!shouldShowSideChat })),
1268
- topicDrift: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottom, sideChatOpen: !!shouldShowSideChat })),
1438
+ errorResponse: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, sideChatOpen: !!shouldShowSideChat })),
1439
+ topicDrift: (_jsx(MessageText, { chatItem: chatItem, controller: controller, scrollToBottom: scrollToBottomIfNear, sideChatOpen: !!shouldShowSideChat })),
1269
1440
  productQuery: null,
1270
- }[chatItem.messageType] || null }, index))), _jsx("div", { className: "ss__chat__messages__end", ref: messagesEndRef })] }), _jsx(ChatLoadingIndicator, { loading: store.loading, verbs: loadingVerbs }), !store.currentChat?.isExpired ? (_jsxs("div", { className: "ss__chat__content__footer", ref: footerRef, children: [store.error && _jsx("div", { className: "ss__chat__error", children: store.error.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: "Session limit reached" }), _jsx("span", { children: "This chat has reached its maximum number of interactions. Start a new session to continue." })] }), _jsx(Button, { className: 'ss__chat__topic-drift__button', onClick: () => {
1441
+ }[chatItem.messageType] || null }, index))), _jsx("div", { className: "ss__chat__messages__end", ref: messagesEndRef }), showNewMessages && messagesOverflow && (_jsxs("div", { className: "ss__chat__new-messages", onClick: () => {
1442
+ scrollToBottom();
1443
+ setShowNewMessages(false);
1444
+ }, children: [_jsx(Icon, { icon: "angle-down", size: "12px" }), _jsx("span", { children: "New messages" })] }))] }), _jsx(ChatLoadingIndicator, { loading: store.loading, verbs: loadingVerbs }), !store.currentChat?.isExpired ? (_jsxs("div", { className: "ss__chat__content__footer", ref: footerRef, children: [store.error && _jsx("div", { className: "ss__chat__error", children: store.error.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: "Session limit reached" }), _jsx("span", { children: "This chat has reached its maximum number of interactions. Start a new session to continue." })] }), _jsx(Button, { className: 'ss__chat__topic-drift__button', onClick: () => {
1271
1445
  controller.store.createChat();
1272
1446
  }, children: "New Session" })] })), store.currentChat?.actions && store.currentChat.actions.length > 0 && (_jsx("div", { className: 'ss__chat__actions', children: store.currentChat?.actions.map((action, index) => (_jsx("div", { className: "ss__chat__actions__wrap", children: {
1273
- facets: action.data.length ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "ss__chat__actions__facets-header", children: [_jsx("div", { className: "ss__chat__actions--title", children: "Filter by:" }), multiselectFacets && (store.currentChat?.attachments.attached.some((item) => item.type === 'facet') || false) && (_jsx("div", { className: "ss__chat__actions__facets-apply", children: _jsx(Button, { onClick: () => {
1447
+ facets: action.data.length ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "ss__chat__actions__facets-header", children: [_jsx("div", { className: "ss__chat__actions--title", children: "Filter by:" }), multiselectFacets && (_jsx("div", { className: `ss__chat__actions__facets-apply${store.currentChat?.attachments.attached.some((item) => item.type === 'facet') ||
1448
+ (store.currentChat?.removedFacets && store.currentChat.removedFacets.length > 0)
1449
+ ? ' ss__chat__actions__facets-apply--active'
1450
+ : ''}`, children: _jsx(Button, { onClick: () => {
1274
1451
  controller.search();
1275
- }, children: "Apply Filters" }) }))] }), _jsx("div", { className: "ss__chat__actions--facets", children: action.data.slice(0, 10).map((facet, idx) => {
1452
+ }, children: "Apply" }) }))] }), _jsx("div", { className: "ss__chat__actions--facets", children: action.data.slice(0, 10).map((facet, idx) => {
1276
1453
  if (!facet.values?.length)
1277
1454
  return null;
1278
- return (_jsx("div", { className: `ss__chat__actions__facet ss__chat__actions__facet--${facet.type}`, children: _jsx(Dropdown, { usePortal: true, dropUp: true, button: _jsx(FacetButton, { label: facet.label || facet.field }), style: {
1455
+ return (_jsx("div", { className: `ss__chat__actions__facet ss__chat__actions__facet--${facet.type}`, children: _jsx(Dropdown, { usePortal: true, dropUp: true, button: _jsx(FacetButton, { label: getFacetButtonLabel(facet) }), style: {
1279
1456
  '.ss__dropdown__content': {
1280
1457
  width: '150px',
1281
1458
  maxHeight: '200px',
@@ -1339,9 +1516,11 @@ export const Chat = observer((properties) => {
1339
1516
  },
1340
1517
  }, children: _jsx("div", { className: "ss__chat__actions__facet__options", children: facet.type === 'range-buckets'
1341
1518
  ? facet.values.map((option) => {
1342
- const optionValue = `${option.low ?? '*'}:${option.high ?? '*'}`;
1519
+ const rangeOption = option;
1520
+ const optionValue = `${rangeOption.low ?? '*'}:${rangeOption.high ?? '*'}`;
1343
1521
  if (multiselectFacets) {
1344
- const isSelected = controller.store.isFacetSelected(facet.field, optionValue);
1522
+ const isSelected = (rangeOption.filtered && !controller.store.isFacetRemoved(facet.field, optionValue)) ||
1523
+ controller.store.isFacetSelected(facet.field, optionValue);
1345
1524
  return (_jsxs("div", { className: "ss__chat__actions__facet__option--checkbox", onClick: () => {
1346
1525
  if (isSelected) {
1347
1526
  controller.store.removeFacet(facet.field, optionValue);
@@ -1351,29 +1530,31 @@ export const Chat = observer((properties) => {
1351
1530
  key: facet.field,
1352
1531
  facetLabel: facet.label,
1353
1532
  value: optionValue,
1354
- label: option.label,
1355
- count: option.count,
1533
+ label: rangeOption.label,
1534
+ count: rangeOption.count,
1356
1535
  });
1357
1536
  }
1358
1537
  }, children: [_jsx("div", { className: classnames('ss__chat__actions__facet__option__checkbox', {
1359
1538
  'ss__chat__actions__facet__option__checkbox--checked': isSelected,
1360
- }), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: option.label })] }, optionValue));
1539
+ }), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: rangeOption.label })] }, optionValue));
1361
1540
  }
1362
1541
  return (_jsx(Button, { onClick: () => {
1363
1542
  controller.store.addFacet({
1364
1543
  key: facet.field,
1365
1544
  facetLabel: facet.label,
1366
1545
  value: optionValue,
1367
- label: option.label,
1368
- count: option.count,
1546
+ label: rangeOption.label,
1547
+ count: rangeOption.count,
1369
1548
  });
1370
1549
  controller.search();
1371
- }, children: option.label }, optionValue));
1550
+ }, children: rangeOption.label }, optionValue));
1372
1551
  })
1373
1552
  : facet.values.map((option) => {
1374
- const optionValue = option.value || option.label;
1553
+ const valueOption = option;
1554
+ const optionValue = valueOption.value || valueOption.label;
1375
1555
  if (multiselectFacets) {
1376
- const isSelected = controller.store.isFacetSelected(facet.field, optionValue);
1556
+ const isSelected = (valueOption.filtered && !controller.store.isFacetRemoved(facet.field, optionValue)) ||
1557
+ controller.store.isFacetSelected(facet.field, optionValue);
1377
1558
  return (_jsxs("div", { className: "ss__chat__actions__facet__option--checkbox", onClick: () => {
1378
1559
  if (isSelected) {
1379
1560
  controller.store.removeFacet(facet.field, optionValue);
@@ -1383,24 +1564,24 @@ export const Chat = observer((properties) => {
1383
1564
  key: facet.field,
1384
1565
  facetLabel: facet.label,
1385
1566
  value: optionValue,
1386
- label: option.label,
1387
- count: option.count,
1567
+ label: valueOption.label,
1568
+ count: valueOption.count,
1388
1569
  });
1389
1570
  }
1390
1571
  }, children: [_jsx("div", { className: classnames('ss__chat__actions__facet__option__checkbox', {
1391
1572
  'ss__chat__actions__facet__option__checkbox--checked': isSelected,
1392
- }), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: option.label })] }, optionValue));
1573
+ }), children: isSelected && _jsx(Icon, { icon: "check-thin", size: "10px" }) }), _jsx("span", { className: "ss__chat__actions__facet__option__label", children: valueOption.label })] }, optionValue));
1393
1574
  }
1394
1575
  return (_jsx(Button, { onClick: () => {
1395
1576
  controller.store.addFacet({
1396
1577
  key: facet.field,
1397
1578
  facetLabel: facet.label,
1398
1579
  value: optionValue,
1399
- label: option.label,
1400
- count: option.count,
1580
+ label: valueOption.label,
1581
+ count: valueOption.count,
1401
1582
  });
1402
1583
  controller.search();
1403
- }, children: option.label }, optionValue));
1584
+ }, children: valueOption.label }, optionValue));
1404
1585
  }) }) }, facet.field) }, idx));
1405
1586
  }) })] })) : null,
1406
1587
  actions: (_jsx("div", { className: "ss__chat__actions--suggested" })),
@@ -1509,6 +1690,7 @@ export const Chat = observer((properties) => {
1509
1690
  const messageText = store.currentChat?.handleTopicDrift();
1510
1691
  if (messageText) {
1511
1692
  controller.store.createChat();
1693
+ controller.search({ data: { message: messageText } });
1512
1694
  }
1513
1695
  }, children: buttonText })), _jsx("span", { onClick: () => {
1514
1696
  store.currentChat?.dismissTopicDrift();
@@ -1547,7 +1729,7 @@ export const Chat = observer((properties) => {
1547
1729
  }, multiple: true, type: "file", accept: "image/*", className: "ss__chat__input__input__file" })] }))] }), _jsx("div", { className: 'ss__chat__input__actions', children: (() => {
1548
1730
  const hasImageAttachment = (store.currentChat?.attachments.attached || []).some((item) => item.type === 'image' && !item.error);
1549
1731
  const messageEmpty = !controller.store.inputValue.trim();
1550
- const sendDisabled = store.blocked || store.currentChat?.sessionLimitReached || (hasImageAttachment && messageEmpty);
1732
+ const sendDisabled = store.blocked || store.currentChat?.sessionLimitReached || (hasImageAttachment && messageEmpty) || messageEmpty;
1551
1733
  return (_jsx(Button, { className: "ss__chat__send-button", icon: { icon: 'send', title: 'Send Message' }, disabled: sendDisabled, onClick: () => {
1552
1734
  if (sendDisabled)
1553
1735
  return;
@@ -1,12 +1,11 @@
1
1
  import { ChatController } from '@athoscommerce/snap-controller';
2
2
  import { Product } from '@athoscommerce/snap-store-mobx';
3
3
  import { ComponentProps } from '../../../types';
4
- export declare const ChatResult: ((properties: ChatResultProps) => JSX.Element) & {
5
- displayName: string;
6
- };
4
+ export declare const ChatResult: (props: ChatResultProps) => import("@emotion/react/jsx-runtime").JSX.Element;
7
5
  export interface ChatResultProps extends ComponentProps {
8
6
  result: Product;
9
7
  controller: ChatController;
10
8
  scrollToBottom: () => void;
9
+ trackingRef?: (el: HTMLElement | null) => void;
11
10
  }
12
11
  //# sourceMappingURL=ChatResult.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatResult.d.ts","sourceRoot":"","sources":["../../../../../../../components/src/components/Organisms/Chat/ChatResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAMzD,OAAO,EAAE,cAAc,EAAe,MAAM,gBAAgB,CAAC;AAyG7D,eAAO,MAAM,UAAU,gBAAyB,eAAe,KAAG,GAAG,CAAC,OAAO;;CAiG3E,CAAC;AAMH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;CAC3B"}
1
+ {"version":3,"file":"ChatResult.d.ts","sourceRoot":"","sources":["../../../../../../../components/src/components/Organisms/Chat/ChatResult.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAMzD,OAAO,EAAE,cAAc,EAAe,MAAM,gBAAgB,CAAC;AAkH7D,eAAO,MAAM,UAAU,8EA+GtB,CAAC;AAMF,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACtD,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC;IAC3B,cAAc,EAAE,MAAM,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;CAC/C"}