@envive-ai/react-toolkit-v3 0.3.27 → 0.3.29

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 (188) hide show
  1. package/dist/AnimatedText/AnimatedText.d.cts +3 -3
  2. package/dist/AnimatedText/AnimatedText.d.ts +3 -3
  3. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.cts +2 -2
  4. package/dist/CSSVariablesEditor/CssVariablesEditorComponent.d.ts +2 -2
  5. package/dist/Carousel/Carousel.cjs +1 -1
  6. package/dist/Carousel/Carousel.d.cts +2 -2
  7. package/dist/Carousel/Carousel.d.ts +2 -2
  8. package/dist/Carousel/Carousel.js +1 -1
  9. package/dist/Carousel/components/Container.cjs +2 -2
  10. package/dist/Carousel/components/Container.js +2 -2
  11. package/dist/ChatFooter/ChatFooter.cjs +2 -1
  12. package/dist/ChatFooter/ChatFooter.d.cts +2 -2
  13. package/dist/ChatFooter/ChatFooter.d.ts +2 -2
  14. package/dist/ChatFooter/ChatFooter.js +2 -1
  15. package/dist/ChatFooter/components/Layout.cjs +2 -2
  16. package/dist/ChatFooter/components/Layout.js +2 -2
  17. package/dist/ChatFooter/components/index.d.cts +6 -5
  18. package/dist/ChatFooter/components/index.d.ts +6 -5
  19. package/dist/ChatFooter/types/types.d.cts +3 -2
  20. package/dist/ChatFooter/types/types.d.ts +3 -2
  21. package/dist/ChatHeader/ChatHeader.d.cts +2 -2
  22. package/dist/ChatHeader/ChatHeader.d.ts +2 -2
  23. package/dist/ChatHeader/components/Handle.cjs +2 -2
  24. package/dist/ChatHeader/components/Handle.js +2 -2
  25. package/dist/ChatHeader/components/Toggle.cjs +3 -3
  26. package/dist/ChatHeader/components/Toggle.js +3 -3
  27. package/dist/ChatPreview/ChatPreview.cjs +1 -1
  28. package/dist/ChatPreview/ChatPreview.d.cts +2 -2
  29. package/dist/ChatPreview/ChatPreview.d.ts +2 -2
  30. package/dist/ChatPreview/ChatPreview.js +1 -1
  31. package/dist/ChatPreviewComparison/ChatPreviewComparison.cjs +1 -1
  32. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.cts +2 -2
  33. package/dist/ChatPreviewComparison/ChatPreviewComparison.d.ts +2 -2
  34. package/dist/ChatPreviewComparison/ChatPreviewComparison.js +1 -1
  35. package/dist/ChatPreviewComparison/components/Headline.cjs +2 -2
  36. package/dist/ChatPreviewComparison/components/Headline.js +2 -2
  37. package/dist/ChatPreviewComparison/components/Layout.cjs +4 -4
  38. package/dist/ChatPreviewComparison/components/Layout.js +4 -4
  39. package/dist/ChatPreviewComparison/components/Message.cjs +2 -2
  40. package/dist/ChatPreviewComparison/components/Message.js +2 -2
  41. package/dist/ChatPreviewLoading/ChatPreviewLoading.d.cts +2 -2
  42. package/dist/Container/Container.d.cts +218 -218
  43. package/dist/Container/Container.d.ts +51 -51
  44. package/dist/DesignTokens/DesignTokensComponent.d.cts +2 -2
  45. package/dist/DesignTokens/DesignTokensComponent.d.ts +2 -2
  46. package/dist/Disclaimer/components/Container.cjs +2 -2
  47. package/dist/Disclaimer/components/Container.js +2 -2
  48. package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.cts +2 -2
  49. package/dist/DocumentRetrievalCard/DocumentRetrievalCard.d.ts +2 -2
  50. package/dist/DocumentRetrievalCard/components/Layout.cjs +2 -2
  51. package/dist/DocumentRetrievalCard/components/Layout.js +2 -2
  52. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.cjs +1 -1
  53. package/dist/DocumentRetrievalCard/components/ViewArticleButton/components/Icon.js +1 -1
  54. package/dist/DocumentRetrievalCard/components/ViewArticleButton.cjs +1 -1
  55. package/dist/DocumentRetrievalCard/components/ViewArticleButton.js +1 -1
  56. package/dist/FloatingButton/FloatingButton.d.cts +2 -2
  57. package/dist/FloatingButton/FloatingButton.d.ts +2 -2
  58. package/dist/FloatingChat/FloatingChat.d.cts +2 -2
  59. package/dist/FloatingChat/FloatingChat.d.ts +2 -2
  60. package/dist/FloatingChat/components/ChatMessages.cjs +1 -1
  61. package/dist/FloatingChat/components/ChatMessages.js +1 -1
  62. package/dist/FloatingChat/components/Layout.cjs +3 -3
  63. package/dist/FloatingChat/components/Layout.js +3 -3
  64. package/dist/FloatingChat/components/ResultsGridView.cjs +1 -1
  65. package/dist/FloatingChat/components/ResultsGridView.js +1 -1
  66. package/dist/FloatingChat/components/SalesAgentBadgeContent.cjs +1 -1
  67. package/dist/FloatingChat/components/SalesAgentBadgeContent.js +1 -1
  68. package/dist/FullPageSalesAgent/FullPageSalesAgent.cjs +44 -10
  69. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.cts +2 -2
  70. package/dist/FullPageSalesAgent/FullPageSalesAgent.d.ts +2 -2
  71. package/dist/FullPageSalesAgent/FullPageSalesAgent.js +44 -10
  72. package/dist/FullPageSalesAgent/components/Layout.cjs +24 -27
  73. package/dist/FullPageSalesAgent/components/Layout.js +24 -27
  74. package/dist/FullPageSalesAgent/components/WelcomeOverlay.cjs +37 -20
  75. package/dist/FullPageSalesAgent/components/WelcomeOverlay.js +37 -20
  76. package/dist/FullPageSalesAgent/hooks/useWelcomeOverlayProducts.cjs +1 -0
  77. package/dist/FullPageSalesAgent/hooks/useWelcomeOverlayProducts.js +1 -0
  78. package/dist/Image/Image.d.cts +2 -2
  79. package/dist/Image/Image.d.ts +2 -2
  80. package/dist/ImageGallery/ImageGallery.d.cts +2 -2
  81. package/dist/ImageGallery/ImageGallery.d.ts +2 -2
  82. package/dist/ImageGallery/components/Layout.cjs +1 -1
  83. package/dist/ImageGallery/components/Layout.js +1 -1
  84. package/dist/MarkdownProcessor/MarkdownProcessor.d.cts +2 -2
  85. package/dist/MarkdownProcessor/MarkdownProcessor.d.ts +2 -2
  86. package/dist/Message/components/LinkButton.cjs +2 -2
  87. package/dist/Message/components/LinkButton.js +2 -2
  88. package/dist/OrderLookupCard/OrderLookupCard.cjs +1 -1
  89. package/dist/OrderLookupCard/OrderLookupCard.js +1 -1
  90. package/dist/ProductCard/ProductCard.cjs +2 -2
  91. package/dist/ProductCard/ProductCard.d.cts +2 -2
  92. package/dist/ProductCard/ProductCard.d.ts +2 -2
  93. package/dist/ProductCard/ProductCard.js +2 -2
  94. package/dist/PromptButton/PromptButton.d.cts +2 -2
  95. package/dist/PromptButton/PromptButton.d.ts +2 -2
  96. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.cts +2 -2
  97. package/dist/PromptButtonCarouselWithImage/PromptButtonCarouselWithImage.d.ts +2 -2
  98. package/dist/PromptButtonCarouselWithImage/components/PromptButtonsCarousel.cjs +1 -1
  99. package/dist/PromptButtonCarouselWithImage/components/PromptButtonsCarousel.js +1 -1
  100. package/dist/PromptCarousel/PromptCarousel.cjs +3 -3
  101. package/dist/PromptCarousel/PromptCarousel.d.cts +2 -2
  102. package/dist/PromptCarousel/PromptCarousel.d.ts +2 -2
  103. package/dist/PromptCarousel/PromptCarousel.js +3 -3
  104. package/dist/ReviewCard/ReviewCard.d.cts +2 -2
  105. package/dist/ReviewCard/ReviewCard.d.ts +2 -2
  106. package/dist/ReviewCard/components/Container.cjs +2 -2
  107. package/dist/ReviewCard/components/Container.js +2 -2
  108. package/dist/ReviewCard/components/ReadMoreButton.cjs +1 -1
  109. package/dist/ReviewCard/components/ReadMoreButton.js +1 -1
  110. package/dist/ReviewCard/components/index.d.cts +6 -6
  111. package/dist/ReviewCard/components/index.d.ts +6 -6
  112. package/dist/SalesAgentProductCard/SalesAgentProductCard.d.cts +2 -2
  113. package/dist/SalesAgentProductCard/SalesAgentProductCard.d.ts +2 -2
  114. package/dist/SalesAgentProductCard/components/Container.cjs +2 -2
  115. package/dist/SalesAgentProductCard/components/Container.js +2 -2
  116. package/dist/SalesAgentProductCard/components/index.d.cts +6 -6
  117. package/dist/SalesAgentProductCard/components/index.d.ts +8 -8
  118. package/dist/SocialProof/SocialProof.cjs +1 -1
  119. package/dist/SocialProof/SocialProof.d.cts +2 -2
  120. package/dist/SocialProof/SocialProof.d.ts +2 -2
  121. package/dist/SocialProof/SocialProof.js +1 -1
  122. package/dist/SocialProof/components/Headline.cjs +3 -3
  123. package/dist/SocialProof/components/Headline.js +3 -3
  124. package/dist/SocialProof/components/LayoutFourHorizontal.cjs +1 -1
  125. package/dist/SocialProof/components/LayoutFourHorizontal.js +1 -1
  126. package/dist/SocialProof/components/Subheadline.cjs +1 -1
  127. package/dist/SocialProof/components/Subheadline.js +1 -1
  128. package/dist/SparkleAnimation/SparkleAnimation.d.cts +2 -2
  129. package/dist/SparkleAnimation/SparkleAnimation.d.ts +2 -2
  130. package/dist/Stack/Stack.d.cts +2 -2
  131. package/dist/Stack/Stack.d.ts +2 -2
  132. package/dist/TextField/TextField.cjs +7 -3
  133. package/dist/TextField/TextField.d.cts +1 -0
  134. package/dist/TextField/TextField.d.ts +1 -0
  135. package/dist/TextField/TextField.js +7 -3
  136. package/dist/TextField/components/Input.cjs +31 -2
  137. package/dist/TextField/components/Input.js +31 -2
  138. package/dist/TextField/components/Layout.cjs +7 -2
  139. package/dist/TextField/components/Layout.js +7 -2
  140. package/dist/TextField/hooks/useAutoResize.cjs +17 -0
  141. package/dist/TextField/hooks/useAutoResize.js +16 -0
  142. package/dist/TextField/hooks/usePlaceholderAnimation.cjs +58 -0
  143. package/dist/TextField/hooks/usePlaceholderAnimation.js +57 -0
  144. package/dist/TextField/types/index.d.cts +7 -1
  145. package/dist/TextField/types/index.d.ts +7 -1
  146. package/dist/TitledPromptCarousel/TitledPromptCarousel.cjs +1 -1
  147. package/dist/TitledPromptCarousel/TitledPromptCarousel.d.cts +2 -2
  148. package/dist/TitledPromptCarousel/TitledPromptCarousel.js +1 -1
  149. package/dist/Tokens/index.cjs +1 -1
  150. package/dist/Tokens/index.js +1 -1
  151. package/dist/TypingAnimation/TypingAnimation.cjs +1 -1
  152. package/dist/TypingAnimation/TypingAnimation.d.cts +2 -2
  153. package/dist/TypingAnimation/TypingAnimation.d.ts +2 -2
  154. package/dist/TypingAnimation/TypingAnimation.js +1 -1
  155. package/dist/TypingAnimation/hooks/useGetTypographyVariant.cjs +1 -1
  156. package/dist/TypingAnimation/hooks/useGetTypographyVariant.js +1 -1
  157. package/dist/Typography/Typography.d.cts +4 -4
  158. package/dist/Typography/Typography.d.ts +4 -4
  159. package/dist/WelcomeMessage/components/Container.cjs +2 -2
  160. package/dist/WelcomeMessage/components/Container.js +2 -2
  161. package/dist/WidgetTextField/WidgetTextField.cjs +4 -4
  162. package/dist/WidgetTextField/WidgetTextField.d.cts +2 -2
  163. package/dist/WidgetTextField/WidgetTextField.d.ts +2 -2
  164. package/dist/WidgetTextField/WidgetTextField.js +1 -1
  165. package/dist/WidgetTextField/components/Container.cjs +2 -2
  166. package/dist/WidgetTextField/components/Container.js +2 -2
  167. package/dist/WidgetTextField/components/Icon.cjs +1 -1
  168. package/dist/WidgetTextField/components/Icon.js +1 -1
  169. package/dist/WidgetWrapper/WidgetWrapper.d.cts +2 -2
  170. package/dist/WidgetWrapper/WidgetWrapper.d.ts +2 -2
  171. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.cts +2 -2
  172. package/dist/WidgetWrapperWithTitle/WidgetWrapperWithTitle.d.ts +2 -2
  173. package/dist/styles.css +1 -1
  174. package/package.json +1 -1
  175. package/src/components/ChatFooter/ChatFooter.tsx +1 -0
  176. package/src/components/ChatFooter/components/TextField.tsx +4 -1
  177. package/src/components/ChatFooter/types/types.ts +3 -2
  178. package/src/components/FullPageSalesAgent/FullPageSalesAgent.tsx +76 -9
  179. package/src/components/FullPageSalesAgent/components/Layout.tsx +4 -1
  180. package/src/components/FullPageSalesAgent/components/WelcomeOverlay.tsx +52 -17
  181. package/src/components/FullPageSalesAgent/hooks/useWelcomeOverlayProducts.ts +1 -0
  182. package/src/components/TextField/TextField.tsx +7 -1
  183. package/src/components/TextField/components/Input.tsx +43 -9
  184. package/src/components/TextField/components/Layout.tsx +7 -1
  185. package/src/components/TextField/hooks/useAutoResize.ts +14 -0
  186. package/src/components/TextField/hooks/usePlaceholderAnimation.ts +67 -0
  187. package/src/components/TextField/hooks/useTextFieldSubmit.ts +1 -1
  188. package/src/components/TextField/types/index.ts +7 -1
@@ -65,6 +65,31 @@ export const FullPageSalesAgent = ({
65
65
  const { trackWidgetInteraction } = useWidgetInteraction();
66
66
  const { onDrag, onHover, onMouseDown, onMouseUp, onTouchStart, onTouchEnd } =
67
67
  usePromptCarouselAnalytics(WidgetInteractionComponent.FULL_PAGE_SALES_AGENT, text => text);
68
+ const getOverlaySuggestionId = useCallback(
69
+ (text: string) => {
70
+ const rawValues = hardcopyContent?.rawValues;
71
+ if (rawValues) {
72
+ for (const raw of Object.values(rawValues)) {
73
+ const list = Array.isArray(raw) ? raw : [raw];
74
+ const found = list.find(s => s.value === text);
75
+ if (found) return found.id;
76
+ }
77
+ }
78
+ return text;
79
+ },
80
+ [hardcopyContent?.rawValues],
81
+ );
82
+ const {
83
+ onDrag: onOverlayDrag,
84
+ onHover: onOverlayHover,
85
+ onMouseDown: onOverlayMouseDown,
86
+ onMouseUp: onOverlayMouseUp,
87
+ onTouchStart: onOverlayTouchStart,
88
+ onTouchEnd: onOverlayTouchEnd,
89
+ } = usePromptCarouselAnalytics(
90
+ WidgetInteractionComponent.FULL_PAGE_SALES_AGENT,
91
+ getOverlaySuggestionId,
92
+ );
68
93
  const setListeningToSpeech = useSetAtom(listeningToSpeechAtom);
69
94
 
70
95
  const {
@@ -116,12 +141,17 @@ export const FullPageSalesAgent = ({
116
141
  chatFooterTextFieldPlaceholderText,
117
142
  disclaimerText,
118
143
  welcomeOverlayTitle,
144
+ welcomeOverlayAnimatedPlaceholder,
119
145
  welcomeOverlayPromptSuggestions,
120
146
  welcomeOverlayProductCarouselTitle,
121
147
  welcomeOverlayProductIds,
122
148
  } = hardcopyContent?.values ?? {};
123
149
 
124
- const overlayEnabled = typeof welcomeOverlayTitle === 'string' && !!welcomeOverlayTitle;
150
+ const overlayTitle =
151
+ Array.isArray(welcomeOverlayTitle) && welcomeOverlayTitle.length > 0
152
+ ? welcomeOverlayTitle[0]
153
+ : welcomeOverlayTitle;
154
+ const overlayEnabled = typeof overlayTitle === 'string' && !!welcomeOverlayTitle;
125
155
 
126
156
  const [showOverlay, setShowOverlay] = useState(overlayEnabled);
127
157
  const { footerStyles, footerClasses } = useGetFooterStyles({ showOverlay });
@@ -230,12 +260,28 @@ export const FullPageSalesAgent = ({
230
260
  );
231
261
 
232
262
  const handleOverlayDismiss = useCallback(() => {
263
+ trackWidgetInteraction({
264
+ eventName: EnviveMetricsEventName.WidgetInteraction,
265
+ trigger: {
266
+ widget: WidgetInteractionComponent.FULL_PAGE_SALES_AGENT,
267
+ widget_interaction: WidgetInteractionType.OVERLAY_DISMISSED,
268
+ },
269
+ });
233
270
  setShowOverlay(false);
234
- }, []);
271
+ }, [trackWidgetInteraction]);
235
272
 
236
- // TODO: Migrate to Widget and submit analytics for stringId tracking
237
273
  const handleOverlaySuggestionClick = useCallback(
238
274
  (buttonText: string) => {
275
+ trackWidgetInteraction({
276
+ eventName: EnviveMetricsEventName.WidgetInteraction,
277
+ trigger: {
278
+ widget: WidgetInteractionComponent.FULL_PAGE_SALES_AGENT,
279
+ widget_interaction: WidgetInteractionType.SUGGESTION_CLICKED,
280
+ widget_interaction_data: {
281
+ suggestion_clicked: { suggestion_id: getOverlaySuggestionId(buttonText) },
282
+ },
283
+ },
284
+ });
239
285
  onTypedMessageSubmitted({
240
286
  query: buttonText,
241
287
  userTyped: true,
@@ -246,7 +292,14 @@ export const FullPageSalesAgent = ({
246
292
  setGeneralSuggestions([]);
247
293
  setShowOverlay(false);
248
294
  },
249
- [onTypedMessageSubmitted, setListeningToSpeech, setAnswerSuggestions, setGeneralSuggestions],
295
+ [
296
+ getOverlaySuggestionId,
297
+ trackWidgetInteraction,
298
+ onTypedMessageSubmitted,
299
+ setListeningToSpeech,
300
+ setAnswerSuggestions,
301
+ setGeneralSuggestions,
302
+ ],
250
303
  );
251
304
 
252
305
  const promptSuggestions = useMemo(() => {
@@ -270,7 +323,11 @@ export const FullPageSalesAgent = ({
270
323
  isPendingResponse || isResponseStreaming || generalSuggestions.length === 0
271
324
  }
272
325
  hideEnviveWatermark={showOverlay || !showEnviveLogo}
273
- textFieldPlaceholderText={chatFooterTextFieldPlaceholderText as string}
326
+ textFieldPlaceholderText={
327
+ showOverlay
328
+ ? welcomeOverlayAnimatedPlaceholder
329
+ : (chatFooterTextFieldPlaceholderText as string)
330
+ }
274
331
  promptSuggestions={promptSuggestions}
275
332
  handleButtonClick={buttonText => {
276
333
  const suggestion = suggestions.find(s => s.content === buttonText && !s.isAnswer);
@@ -381,6 +438,10 @@ export const FullPageSalesAgent = ({
381
438
  </motion.div>
382
439
  );
383
440
 
441
+ const productCarouselTitle = Array.isArray(welcomeOverlayProductCarouselTitle)
442
+ ? welcomeOverlayProductCarouselTitle[0]
443
+ : welcomeOverlayProductCarouselTitle;
444
+
384
445
  return (
385
446
  <LayoutGroup>
386
447
  <FullPageSAComponents.Layout
@@ -400,21 +461,27 @@ export const FullPageSalesAgent = ({
400
461
  headerContainer={headerContainer}
401
462
  autoHeight={autoHeight}
402
463
  scrollContainerRef={scrollContainerRef}
403
- backgroundStyle={backgroundStyle}
404
464
  overlay={
405
- overlayEnabled ? (
465
+ overlayEnabled && showOverlay ? (
406
466
  <WelcomeOverlay
407
467
  show={showOverlay}
408
468
  theme={resolvedTheme}
409
469
  suggestionButtonType={suggestionButtonType}
410
470
  sparkleColor={sparkleColor}
411
- title={welcomeOverlayTitle as string}
471
+ title={overlayTitle}
412
472
  promptSuggestions={overlayPromptSuggestions}
413
- productCarouselTitle={welcomeOverlayProductCarouselTitle as string | undefined}
473
+ productCarouselTitle={productCarouselTitle}
414
474
  productIds={overlayProductIds}
415
475
  backgroundStyle={backgroundStyle}
416
476
  chatFooter={showOverlay ? motionFooter : null}
417
477
  onSuggestionClick={handleOverlaySuggestionClick}
478
+ onSuggestionHover={onOverlayHover}
479
+ onSuggestionDrag={onOverlayDrag}
480
+ onSuggestionMouseDown={onOverlayMouseDown}
481
+ onSuggestionMouseUp={onOverlayMouseUp}
482
+ onSuggestionTouchStart={onOverlayTouchStart}
483
+ onSuggestionTouchEnd={onOverlayTouchEnd}
484
+ onProductCardClick={handleProductCardClick}
418
485
  />
419
486
  ) : null
420
487
  }
@@ -49,6 +49,10 @@ export const Layout = ({
49
49
  autoHeight,
50
50
  });
51
51
 
52
+ if (overlay) {
53
+ return overlay;
54
+ }
55
+
52
56
  return (
53
57
  <div
54
58
  className={classNames(containerClasses, 'envive-tw-relative')}
@@ -89,7 +93,6 @@ export const Layout = ({
89
93
  )}
90
94
  {footer}
91
95
  </div>
92
- {overlay}
93
96
  </div>
94
97
  );
95
98
  };
@@ -6,6 +6,7 @@ import { SparkleAnimation } from 'src/components/SparkleAnimation';
6
6
  import { PromptButtonVariant } from '../../PromptButton';
7
7
  import { AnimationSpeed, PromptCarousel, PromptCarouselRows } from '../../PromptCarousel';
8
8
  import { SalesAgentProductCardsCarousel } from '../../FloatingChat/components/SalesAgentProductCardsCarousel';
9
+ import { SalesAgentProductCardProps } from '../../SalesAgentProductCard/types/types';
9
10
  import { useWelcomeOverlayProducts } from '../hooks/useWelcomeOverlayProducts';
10
11
 
11
12
  export interface WelcomeOverlayProps {
@@ -22,6 +23,13 @@ export interface WelcomeOverlayProps {
22
23
  /** The ChatFooter node wrapped in motion.div layoutId — shared-element anchor for the dismiss animation. */
23
24
  chatFooter: React.ReactNode;
24
25
  onSuggestionClick: (text: string) => void;
26
+ onSuggestionHover?: (text: string) => void;
27
+ onSuggestionDrag?: () => void;
28
+ onSuggestionMouseDown?: (text: string) => void;
29
+ onSuggestionMouseUp?: (text: string) => void;
30
+ onSuggestionTouchStart?: (text: string) => void;
31
+ onSuggestionTouchEnd?: (text: string) => void;
32
+ onProductCardClick?: (product: SalesAgentProductCardProps) => void;
25
33
  }
26
34
 
27
35
  export const WelcomeOverlay = ({
@@ -36,28 +44,27 @@ export const WelcomeOverlay = ({
36
44
  sparkleColor,
37
45
  backgroundStyle = { backgroundColor: 'white' },
38
46
  onSuggestionClick,
47
+ onSuggestionHover,
48
+ onSuggestionDrag,
49
+ onSuggestionMouseDown,
50
+ onSuggestionMouseUp,
51
+ onSuggestionTouchStart,
52
+ onSuggestionTouchEnd,
53
+ onProductCardClick,
39
54
  }: WelcomeOverlayProps) => {
40
55
  const { products, isLoading } = useWelcomeOverlayProducts(productIds);
41
56
  const showProductCarousel = !isLoading && products.length > 0 && !!productCarouselTitle;
42
57
 
43
58
  const welcomeOverlayClassNames = classNames([
59
+ 'envive-overlay',
44
60
  'envive-tw-inset-0',
45
61
  'envive-tw-absolute',
46
62
  'envive-tw-z-20',
47
63
  'envive-tw-flex',
48
64
  'envive-tw-w-full',
49
- 'envive-tw-h-full',
50
- 'envive-tw-max-w-[768px]',
51
65
  'envive-tw-flex-col',
52
66
  ]);
53
67
 
54
- const titleClassNames = classNames([
55
- 'envive-tw-flex',
56
- 'envive-tw-flex-row',
57
- 'envive-tw-gap-2',
58
- 'envive-tw-items-center',
59
- ]);
60
-
61
68
  const titleBoxClassNames = classNames([
62
69
  'envive-tw-flex',
63
70
  'envive-tw-py-4',
@@ -70,6 +77,22 @@ export const WelcomeOverlay = ({
70
77
  'envive-tw-border',
71
78
  'envive-tw-rounded-global-custom',
72
79
  ]);
80
+ const overlayContentClassNames = classNames([
81
+ 'envive-tw-inset-0',
82
+ 'envive-tw-mx-auto',
83
+ 'envive-tw-flex',
84
+ 'envive-tw-w-full',
85
+ 'envive-tw-max-w-[768px]',
86
+ 'envive-tw-flex-1',
87
+ 'envive-tw-flex-col',
88
+ 'envive-tw-gap-6',
89
+ 'envive-tw-overflow-y-auto',
90
+ 'envive-tw-overflow-x-hidden',
91
+ 'envive-tw-px-4',
92
+ 'envive-tw-pb-4',
93
+ 'envive-tw-pt-6',
94
+ 'lg:envive-tw-gap-10',
95
+ ]);
73
96
  return (
74
97
  <AnimatePresence>
75
98
  {show && (
@@ -80,15 +103,20 @@ export const WelcomeOverlay = ({
80
103
  initial={{ opacity: 1 }}
81
104
  exit={{ opacity: 0, transition: { duration: 0.5, ease: 'easeIn' } }}
82
105
  >
83
- <div className="envive-tw-flex envive-tw-flex-1 envive-tw-flex-col envive-tw-gap-6 envive-tw-overflow-y-auto envive-tw-overflow-x-hidden envive-tw-px-4 envive-tw-pb-4 envive-tw-pt-6 lg:envive-tw-gap-10">
106
+ <div className={overlayContentClassNames}>
84
107
  <div className={titleBoxClassNames}>
85
- <div className={titleClassNames}>
86
- <Typography variant={TypographyVariant.T3_MD}>{title}</Typography>
87
- <SparkleAnimation
88
- color={sparkleColor}
89
- animate={false}
90
- />
91
- </div>
108
+ <Typography
109
+ variant={TypographyVariant.T3_MD}
110
+ as="span"
111
+ >
112
+ {title}
113
+ <span className="envive-tw-ml-1 envive-tw-inline-block envive-tw-align-top">
114
+ <SparkleAnimation
115
+ color={sparkleColor}
116
+ animate={false}
117
+ />
118
+ </span>
119
+ </Typography>
92
120
  {chatFooter}
93
121
  {promptSuggestions.length > 0 && (
94
122
  <PromptCarousel
@@ -98,6 +126,12 @@ export const WelcomeOverlay = ({
98
126
  promptCarouselRows={PromptCarouselRows.ALWAYS_TWO}
99
127
  animationSpeed={AnimationSpeed.NONE}
100
128
  handleButtonClick={onSuggestionClick}
129
+ handleButtonHover={onSuggestionHover}
130
+ handleButtonDrag={onSuggestionDrag}
131
+ handleButtonMouseDown={onSuggestionMouseDown}
132
+ handleButtonMouseUp={onSuggestionMouseUp}
133
+ handleButtonTouchStart={onSuggestionTouchStart}
134
+ handleButtonTouchEnd={onSuggestionTouchEnd}
101
135
  />
102
136
  )}
103
137
  </div>
@@ -110,6 +144,7 @@ export const WelcomeOverlay = ({
110
144
  hideBadgeCount
111
145
  products={products}
112
146
  hideNavigation={false}
147
+ onProductCardClick={onProductCardClick}
113
148
  />
114
149
  </div>
115
150
  )}
@@ -7,6 +7,7 @@ type RetrievedProduct = Awaited<
7
7
  >['products'][0];
8
8
 
9
9
  const mapToCardProps = (p: RetrievedProduct): SalesAgentProductCardProps => ({
10
+ id: p.id,
10
11
  productName: p.title ?? '',
11
12
  currentPrice: p.salePrice ?? 0,
12
13
  previousPrice: p.originalPrice === p.salePrice ? undefined : p.originalPrice,
@@ -8,6 +8,7 @@ import { useGetSkeletonProperties } from './hooks/useGetSkeletonProperties';
8
8
  import { useTextFieldFocus } from './hooks/useTextFieldFocus';
9
9
  import { useTextFieldSubmit } from './hooks/useTextFieldSubmit';
10
10
  import { useTextFieldValue } from './hooks/useTextFieldValue';
11
+ import { usePlaceholderAnimation } from './hooks/usePlaceholderAnimation';
11
12
  import { useVoiceInput } from './hooks/useVoiceInput';
12
13
  import type { TextFieldProps } from './types';
13
14
 
@@ -30,10 +31,13 @@ export const TextField = ({
30
31
  style,
31
32
  ariaLabel,
32
33
  isLoading = false,
34
+ multiline = false,
33
35
  enableVoiceInput = false,
34
36
  onTranscriptionStarted,
35
37
  onTranscriptionCompleted,
36
38
  }: TextFieldProps): JSX.Element => {
39
+ const resolvedPlaceholder = usePlaceholderAnimation(placeholder);
40
+
37
41
  const { currentValue, hasValue, handleChange, resetValue } = useTextFieldValue(
38
42
  controlledValue,
39
43
  onChange,
@@ -88,7 +92,7 @@ export const TextField = ({
88
92
  const input = (
89
93
  <TextFieldComponents.Input
90
94
  theme={resolvedTheme}
91
- placeholder={isLoading ? '' : placeholder}
95
+ placeholder={isLoading ? '' : resolvedPlaceholder}
92
96
  value={isLoading ? '' : currentValue}
93
97
  onChange={handleChange}
94
98
  onKeyDown={handleKeyDown}
@@ -97,6 +101,7 @@ export const TextField = ({
97
101
  disabled={disabled || isLoading}
98
102
  ariaLabel={ariaLabel}
99
103
  className={inputClassName}
104
+ multiline={multiline}
100
105
  />
101
106
  );
102
107
 
@@ -138,6 +143,7 @@ export const TextField = ({
138
143
  isFocused={isFocused}
139
144
  hasValue={hasValue}
140
145
  disabled={disabled}
146
+ multiline={multiline}
141
147
  id={id}
142
148
  testId={testId}
143
149
  className={classNames(className, skeletonClass, disabled && 'envive-tw-cursor-not-allowed')}
@@ -1,6 +1,7 @@
1
1
  import classNames from 'classnames';
2
2
  import { getInputClasses } from '../utils/getInputClasses';
3
3
  import { createInputChangeHandler } from '../utils/createInputChangeHandler';
4
+ import { useAutoResize } from '../hooks/useAutoResize';
4
5
  import { Theme } from '../../../../tokens/theme/theme';
5
6
 
6
7
  type InputProps = {
@@ -8,10 +9,11 @@ type InputProps = {
8
9
  placeholder: string;
9
10
  value?: string;
10
11
  onChange?: (value: string) => void;
11
- onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
12
- onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
13
- onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
12
+ onKeyDown?: (event: React.KeyboardEvent<HTMLElement>) => void;
13
+ onFocus?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
14
+ onBlur?: (event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => void;
14
15
  disabled?: boolean;
16
+ multiline?: boolean;
15
17
  id?: string;
16
18
  testId?: string;
17
19
  className?: string;
@@ -28,6 +30,7 @@ export const Input = ({
28
30
  onFocus,
29
31
  onBlur,
30
32
  disabled = false,
33
+ multiline = false,
31
34
  id,
32
35
  testId,
33
36
  className,
@@ -36,6 +39,42 @@ export const Input = ({
36
39
  }: InputProps): JSX.Element => {
37
40
  const { inputClasses } = getInputClasses(theme);
38
41
  const handleChange = createInputChangeHandler(disabled, onChange);
42
+ const textareaRef = useAutoResize(multiline ? value : undefined);
43
+
44
+ const sharedClasses = classNames(
45
+ inputClasses,
46
+ 'focus-visible:envive-tw-shadow-none focus-visible:envive-tw-outline-none',
47
+ className,
48
+ disabled && 'envive-tw-cursor-not-allowed',
49
+ );
50
+
51
+ if (multiline) {
52
+ return (
53
+ <textarea
54
+ ref={textareaRef}
55
+ id={id}
56
+ data-testid={testId}
57
+ placeholder={placeholder}
58
+ value={value}
59
+ rows={1}
60
+ onChange={e => {
61
+ if (!disabled) onChange?.(e.target.value);
62
+ }}
63
+ onKeyDown={e => {
64
+ if (e.key === 'Enter' && !e.shiftKey) {
65
+ onKeyDown?.(e);
66
+ }
67
+ }}
68
+ onFocus={onFocus}
69
+ onBlur={onBlur}
70
+ disabled={disabled}
71
+ className={sharedClasses}
72
+ style={{ resize: 'none', overflow: 'hidden', padding: 0, ...style }}
73
+ aria-label={ariaLabel}
74
+ aria-disabled={disabled}
75
+ />
76
+ );
77
+ }
39
78
 
40
79
  return (
41
80
  <input
@@ -49,12 +88,7 @@ export const Input = ({
49
88
  onFocus={onFocus}
50
89
  onBlur={onBlur}
51
90
  disabled={disabled}
52
- className={classNames(
53
- inputClasses,
54
- 'focus-visible:envive-tw-shadow-none focus-visible:envive-tw-outline-none',
55
- className,
56
- disabled && 'envive-tw-cursor-not-allowed',
57
- )}
91
+ className={sharedClasses}
58
92
  style={style}
59
93
  aria-label={ariaLabel}
60
94
  aria-disabled={disabled}
@@ -11,6 +11,7 @@ type LayoutProps = {
11
11
  isFocused: boolean;
12
12
  hasValue: boolean;
13
13
  disabled?: boolean;
14
+ multiline?: boolean;
14
15
  id?: string;
15
16
  testId?: string;
16
17
  className?: string;
@@ -25,6 +26,7 @@ export const Layout = ({
25
26
  isFocused,
26
27
  hasValue,
27
28
  disabled = false,
29
+ multiline = false,
28
30
  id,
29
31
  testId,
30
32
  className,
@@ -61,7 +63,11 @@ export const Layout = ({
61
63
  disabled && containerDisabledClasses,
62
64
  className,
63
65
  )}
64
- style={style}
66
+ style={
67
+ multiline
68
+ ? { height: 'auto', minHeight: '40px', alignItems: 'flex-end', ...style }
69
+ : style
70
+ }
65
71
  >
66
72
  {input}
67
73
  {sendIcon}
@@ -0,0 +1,14 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ export const useAutoResize = (value: string | undefined): React.RefObject<HTMLTextAreaElement> => {
4
+ const ref = useRef<HTMLTextAreaElement>(null);
5
+
6
+ useEffect(() => {
7
+ const el = ref.current;
8
+ if (!el) return;
9
+ el.style.height = 'auto';
10
+ el.style.height = `${el.scrollHeight}px`;
11
+ }, [value]);
12
+
13
+ return ref;
14
+ };
@@ -0,0 +1,67 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ import { useEffect, useRef, useState } from 'react';
3
+
4
+ const DEFAULT_TYPING_DURATION = 400;
5
+ const DEFAULT_TRANSITION = 3000;
6
+ const CLEAR_PAUSE = 200;
7
+
8
+ export const usePlaceholderAnimation = (
9
+ placeholder: string | string[],
10
+ typingDuration = DEFAULT_TYPING_DURATION,
11
+ transition = DEFAULT_TRANSITION,
12
+ ): string => {
13
+ const [current, setCurrent] = useState(() => (Array.isArray(placeholder) ? '' : placeholder));
14
+ const timeoutsRef = useRef<NodeJS.Timeout[]>([]);
15
+ const isMountedRef = useRef(true);
16
+
17
+ useEffect(() => {
18
+ if (!Array.isArray(placeholder)) {
19
+ setCurrent(placeholder);
20
+ return () => {};
21
+ }
22
+
23
+ if (placeholder.length === 0) {
24
+ setCurrent('');
25
+ return () => {};
26
+ }
27
+
28
+ isMountedRef.current = true;
29
+ timeoutsRef.current = [];
30
+
31
+ const delay = (ms: number): Promise<void> =>
32
+ new Promise(resolve => {
33
+ const id = setTimeout(resolve, ms);
34
+ timeoutsRef.current.push(id);
35
+ });
36
+
37
+ const animateChars = async (text: string) => {
38
+ const charDelay = text.length > 1 ? typingDuration / (text.length - 1) : 0;
39
+ for (let i = 0; i < text.length; i += 1) {
40
+ await delay(charDelay);
41
+ if (isMountedRef.current) setCurrent(text.substring(0, i + 1));
42
+ }
43
+ };
44
+
45
+ const run = async () => {
46
+ while (isMountedRef.current) {
47
+ for (const element of placeholder) {
48
+ if (!isMountedRef.current) return;
49
+ await animateChars(element);
50
+ await delay(transition);
51
+ if (isMountedRef.current) setCurrent('');
52
+ await delay(CLEAR_PAUSE);
53
+ }
54
+ }
55
+ };
56
+
57
+ run();
58
+
59
+ return () => {
60
+ isMountedRef.current = false;
61
+ timeoutsRef.current.forEach(clearTimeout);
62
+ timeoutsRef.current = [];
63
+ };
64
+ }, [placeholder, typingDuration, transition]);
65
+
66
+ return current;
67
+ };
@@ -20,7 +20,7 @@ export const useTextFieldSubmit = (
20
20
  }, [onSubmit, hasValue, disabled, isLoading, currentValue, resetValue]);
21
21
 
22
22
  const handleKeyDown = useCallback(
23
- (event: React.KeyboardEvent<HTMLInputElement>) => {
23
+ (event: React.KeyboardEvent<HTMLElement>) => {
24
24
  if (event.key === 'Enter' && hasValue && !disabled && !isLoading) {
25
25
  event.preventDefault();
26
26
  handleSubmit();
@@ -8,8 +8,9 @@ export type TextFieldProps = {
8
8
  theme?: Theme;
9
9
  /**
10
10
  * Placeholder text displayed when the input is empty.
11
+ * Pass an array of strings to cycle through them with a typing animation.
11
12
  */
12
- placeholder: string;
13
+ placeholder: string | string[];
13
14
  /**
14
15
  * Controlled value of the input. If not provided, the component manages its own state.
15
16
  */
@@ -47,6 +48,11 @@ export type TextFieldProps = {
47
48
  * @default false
48
49
  */
49
50
  isLoading?: boolean;
51
+ /**
52
+ * When true the input expands to multiple lines instead of scrolling horizontally.
53
+ * @default false
54
+ */
55
+ multiline?: boolean;
50
56
  /**
51
57
  * Enable voice input button
52
58
  * @default false