@yext/chat-ui-react 0.10.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES +3 -3
- package/lib/bundle.css +1 -1
- package/lib/commonjs/package.json.js +1 -1
- package/lib/commonjs/src/components/ChatInput.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatInput.js +9 -2
- package/lib/commonjs/src/components/ChatInput.js.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.d.ts +4 -0
- package/lib/commonjs/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.js +7 -7
- package/lib/commonjs/src/components/ChatPanel.js.map +1 -1
- package/lib/commonjs/src/components/ChatPopUp.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPopUp.js +19 -8
- package/lib/commonjs/src/components/ChatPopUp.js.map +1 -1
- package/lib/commonjs/src/components/Markdown.d.ts +3 -1
- package/lib/commonjs/src/components/Markdown.d.ts.map +1 -1
- package/lib/commonjs/src/components/Markdown.js +3 -2
- package/lib/commonjs/src/components/Markdown.js.map +1 -1
- package/lib/commonjs/src/components/MessageBubble.d.ts +3 -1
- package/lib/commonjs/src/components/MessageBubble.d.ts.map +1 -1
- package/lib/commonjs/src/components/MessageBubble.js +3 -3
- package/lib/commonjs/src/components/MessageBubble.js.map +1 -1
- package/lib/commonjs/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/commonjs/src/components/MessageSuggestions.js +8 -1
- package/lib/commonjs/src/components/MessageSuggestions.js.map +1 -1
- package/lib/commonjs/src/hooks/useFetchInitialMessage.d.ts.map +1 -1
- package/lib/commonjs/src/hooks/useFetchInitialMessage.js +9 -1
- package/lib/commonjs/src/hooks/useFetchInitialMessage.js.map +1 -1
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.d.ts.map +1 -1
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.js +3 -1
- package/lib/commonjs/src/hooks/useSendMessageWithRetries.js.map +1 -1
- package/lib/esm/index.d.ts +7 -1
- package/lib/esm/package.json.mjs +1 -1
- package/lib/esm/src/components/ChatInput.d.ts.map +1 -1
- package/lib/esm/src/components/ChatInput.mjs +9 -2
- package/lib/esm/src/components/ChatInput.mjs.map +1 -1
- package/lib/esm/src/components/ChatPanel.d.ts +4 -0
- package/lib/esm/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPanel.mjs +7 -7
- package/lib/esm/src/components/ChatPanel.mjs.map +1 -1
- package/lib/esm/src/components/ChatPopUp.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPopUp.mjs +19 -8
- package/lib/esm/src/components/ChatPopUp.mjs.map +1 -1
- package/lib/esm/src/components/Markdown.d.ts +3 -1
- package/lib/esm/src/components/Markdown.d.ts.map +1 -1
- package/lib/esm/src/components/Markdown.mjs +3 -2
- package/lib/esm/src/components/Markdown.mjs.map +1 -1
- package/lib/esm/src/components/MessageBubble.d.ts +3 -1
- package/lib/esm/src/components/MessageBubble.d.ts.map +1 -1
- package/lib/esm/src/components/MessageBubble.mjs +3 -3
- package/lib/esm/src/components/MessageBubble.mjs.map +1 -1
- package/lib/esm/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/esm/src/components/MessageSuggestions.mjs +8 -1
- package/lib/esm/src/components/MessageSuggestions.mjs.map +1 -1
- package/lib/esm/src/hooks/useFetchInitialMessage.d.ts.map +1 -1
- package/lib/esm/src/hooks/useFetchInitialMessage.mjs +9 -1
- package/lib/esm/src/hooks/useFetchInitialMessage.mjs.map +1 -1
- package/lib/esm/src/hooks/useSendMessageWithRetries.d.ts.map +1 -1
- package/lib/esm/src/hooks/useSendMessageWithRetries.mjs +3 -1
- package/lib/esm/src/hooks/useSendMessageWithRetries.mjs.map +1 -1
- package/package.json +3 -3
- package/src/components/ChatInput.tsx +16 -7
- package/src/components/ChatPanel.tsx +35 -16
- package/src/components/ChatPopUp.tsx +27 -8
- package/src/components/Markdown.tsx +5 -1
- package/src/components/MessageBubble.tsx +5 -1
- package/src/components/MessageSuggestions.tsx +12 -5
- package/src/hooks/useFetchInitialMessage.ts +11 -3
- package/src/hooks/useSendMessageWithRetries.ts +34 -27
|
@@ -20,6 +20,8 @@ interface MarkdownProps {
|
|
|
20
20
|
* Defaults to 'CHAT_LINK_CLICK'.
|
|
21
21
|
*/
|
|
22
22
|
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
23
|
+
/** Link target open behavior on click. */
|
|
24
|
+
linkTarget?: string;
|
|
23
25
|
/** A callback which is called when a link is clicked. */
|
|
24
26
|
onLinkClick?: (href?: string) => void;
|
|
25
27
|
}
|
|
@@ -32,6 +34,6 @@ interface MarkdownProps {
|
|
|
32
34
|
*
|
|
33
35
|
* @internal
|
|
34
36
|
*/
|
|
35
|
-
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, onLinkClick, }: MarkdownProps): React.JSX.Element;
|
|
37
|
+
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, linkTarget, onLinkClick, }: MarkdownProps): React.JSX.Element;
|
|
36
38
|
export {};
|
|
37
39
|
//# sourceMappingURL=Markdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAevC;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,UAAU,aAAa;IACrB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAC/C,yDAAyD;IACzD,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAkC,EAClC,WAAW,GACZ,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"Markdown.d.ts","sourceRoot":"","sources":["../../../../src/components/Markdown.tsx"],"names":[],"mappings":"AAOA,OAAO,KAAkB,MAAM,OAAO,CAAC;AAevC;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAMD,UAAU,aAAa;IACrB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,kBAAkB,CAAC;IACtC;;;OAGG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,iBAAiB,CAAC;IAC/C,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAkC,EAClC,UAAU,EACV,WAAW,GACZ,EAAE,aAAa,qBAgDf"}
|
|
@@ -28,7 +28,7 @@ const builtInCssClasses = {
|
|
|
28
28
|
*
|
|
29
29
|
* @internal
|
|
30
30
|
*/
|
|
31
|
-
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", onLinkClick, }) {
|
|
31
|
+
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", linkTarget, onLinkClick, }) {
|
|
32
32
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
33
33
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
34
34
|
const components = useMemo(() => {
|
|
@@ -44,7 +44,7 @@ function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHA
|
|
|
44
44
|
};
|
|
45
45
|
return {
|
|
46
46
|
a: ({ node: _, children, ...props }) => {
|
|
47
|
-
return (React.createElement("a", { ...props, onClick: createClickHandlerFn(props.href), target:
|
|
47
|
+
return (React.createElement("a", { ...props, onClick: createClickHandlerFn(props.href), target: linkTarget, rel: "noopener noreferrer", className: cssClasses.link }, children));
|
|
48
48
|
},
|
|
49
49
|
};
|
|
50
50
|
}, [
|
|
@@ -52,6 +52,7 @@ function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHA
|
|
|
52
52
|
linkClickEvent,
|
|
53
53
|
responseId,
|
|
54
54
|
cssClasses,
|
|
55
|
+
linkTarget,
|
|
55
56
|
onLinkClick,
|
|
56
57
|
]);
|
|
57
58
|
return (React.createElement(ReactMarkdown, { className: cssClasses.container, children: content, remarkPlugins: unifiedPlugins.remark, rehypePlugins: unifiedPlugins.rehype, components: components }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.mjs","sources":["../../../../src/components/Markdown.tsx"],"sourcesContent":["import ReactMarkdown, {\n PluggableList,\n ReactMarkdownOptions,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport rehypeSanitize from \"rehype-sanitize\";\nimport React, { useMemo } from \"react\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useComposedCssClasses } from \"../hooks/useComposedCssClasses\";\n\n// The Remark and Rehype plugins to use in conjunction with ReactMarkdown.\nconst unifiedPlugins: { remark?: PluggableList; rehype: PluggableList } = {\n remark: [\n remarkGfm, //renders Github-Flavored Markdown\n ],\n rehype: [\n rehypeRaw, //to support HTML embedded in markdown\n rehypeSanitize, //to sanitize HTML content\n ],\n};\n\n/**\n * The CSS class interface for the Markdown component.\n *\n * @internal\n */\nexport interface MarkdownCssClasses {\n container?: string;\n link?: string;\n}\n\nconst builtInCssClasses: MarkdownCssClasses = {\n link: \"cursor-pointer\",\n};\n\ninterface MarkdownProps {\n /** Stringified markdown. */\n content: string;\n /** The response ID correlates to the current message. */\n responseId?: string;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MarkdownCssClasses;\n /**\n * Action to report for analytics event when a link is clicked.\n * Defaults to 'CHAT_LINK_CLICK'.\n */\n linkClickEvent?: \"WEBSITE\" | \"CHAT_LINK_CLICK\";\n /** A callback which is called when a link is clicked. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * Renders Github-Flavored Markdown from the Knowledge Graph. This Markdown can include\n * arbitrary HTML. Any HTML will be sanitized according to Rehype's default Schema.\n *\n * @remarks\n * A link click will send a CHAT_LINK_CLICK analytics event\n *\n * @internal\n */\nexport function Markdown({\n content,\n responseId,\n customCssClasses,\n linkClickEvent = \"CHAT_LINK_CLICK\",\n onLinkClick,\n}: MarkdownProps) {\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const components: ReactMarkdownOptions[\"components\"] = useMemo(() => {\n const createClickHandlerFn = (href?: string) => () => {\n reportAnalyticsEvent({\n action: linkClickEvent,\n destinationUrl: href,\n chat: {\n responseId,\n },\n });\n onLinkClick?.(href);\n };\n return {\n a: ({ node: _, children, ...props }) => {\n return (\n <a\n {...props}\n onClick={createClickHandlerFn(props.href)}\n target
|
|
1
|
+
{"version":3,"file":"Markdown.mjs","sources":["../../../../src/components/Markdown.tsx"],"sourcesContent":["import ReactMarkdown, {\n PluggableList,\n ReactMarkdownOptions,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport rehypeSanitize from \"rehype-sanitize\";\nimport React, { useMemo } from \"react\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useComposedCssClasses } from \"../hooks/useComposedCssClasses\";\n\n// The Remark and Rehype plugins to use in conjunction with ReactMarkdown.\nconst unifiedPlugins: { remark?: PluggableList; rehype: PluggableList } = {\n remark: [\n remarkGfm, //renders Github-Flavored Markdown\n ],\n rehype: [\n rehypeRaw, //to support HTML embedded in markdown\n rehypeSanitize, //to sanitize HTML content\n ],\n};\n\n/**\n * The CSS class interface for the Markdown component.\n *\n * @internal\n */\nexport interface MarkdownCssClasses {\n container?: string;\n link?: string;\n}\n\nconst builtInCssClasses: MarkdownCssClasses = {\n link: \"cursor-pointer\",\n};\n\ninterface MarkdownProps {\n /** Stringified markdown. */\n content: string;\n /** The response ID correlates to the current message. */\n responseId?: string;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MarkdownCssClasses;\n /**\n * Action to report for analytics event when a link is clicked.\n * Defaults to 'CHAT_LINK_CLICK'.\n */\n linkClickEvent?: \"WEBSITE\" | \"CHAT_LINK_CLICK\";\n /** Link target open behavior on click. */\n linkTarget?: string;\n /** A callback which is called when a link is clicked. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * Renders Github-Flavored Markdown from the Knowledge Graph. This Markdown can include\n * arbitrary HTML. Any HTML will be sanitized according to Rehype's default Schema.\n *\n * @remarks\n * A link click will send a CHAT_LINK_CLICK analytics event\n *\n * @internal\n */\nexport function Markdown({\n content,\n responseId,\n customCssClasses,\n linkClickEvent = \"CHAT_LINK_CLICK\",\n linkTarget,\n onLinkClick,\n}: MarkdownProps) {\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const components: ReactMarkdownOptions[\"components\"] = useMemo(() => {\n const createClickHandlerFn = (href?: string) => () => {\n reportAnalyticsEvent({\n action: linkClickEvent,\n destinationUrl: href,\n chat: {\n responseId,\n },\n });\n onLinkClick?.(href);\n };\n return {\n a: ({ node: _, children, ...props }) => {\n return (\n <a\n {...props}\n onClick={createClickHandlerFn(props.href)}\n target={linkTarget}\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [\n reportAnalyticsEvent,\n linkClickEvent,\n responseId,\n cssClasses,\n linkTarget,\n onLinkClick,\n ]);\n\n return (\n <ReactMarkdown\n className={cssClasses.container}\n children={content}\n remarkPlugins={unifiedPlugins.remark}\n rehypePlugins={unifiedPlugins.rehype}\n components={components}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAWA;AACA,MAAM,cAAc,GAAsD;AACxE,IAAA,MAAM,EAAE;AACN,QAAA,SAAS;AACV,KAAA;AACD,IAAA,MAAM,EAAE;QACN,SAAS;AACT,QAAA,cAAc;AACf,KAAA;CACF,CAAC;AAYF,MAAM,iBAAiB,GAAuB;AAC5C,IAAA,IAAI,EAAE,gBAAgB;CACvB,CAAC;AAoBF;;;;;;;;AAQG;SACa,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAc,GAAG,iBAAiB,EAClC,UAAU,EACV,WAAW,GACG,EAAA;AACd,IAAA,MAAM,oBAAoB,GAAG,uBAAuB,EAAE,CAAC;IACvD,MAAM,UAAU,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE9E,IAAA,MAAM,UAAU,GAAuC,OAAO,CAAC,MAAK;QAClE,MAAM,oBAAoB,GAAG,CAAC,IAAa,KAAK,MAAK;AACnD,YAAA,oBAAoB,CAAC;AACnB,gBAAA,MAAM,EAAE,cAAc;AACtB,gBAAA,cAAc,EAAE,IAAI;AACpB,gBAAA,IAAI,EAAE;oBACJ,UAAU;AACX,iBAAA;AACF,aAAA,CAAC,CAAC;AACH,YAAA,WAAW,GAAG,IAAI,CAAC,CAAC;AACtB,SAAC,CAAC;QACF,OAAO;AACL,YAAA,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,KAAI;AACrC,gBAAA,QACE,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAA,GACM,KAAK,EACT,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,EACzC,MAAM,EAAE,UAAU,EAClB,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAA,EAEzB,QAAQ,CACP,EACJ;aACH;SACF,CAAC;AACJ,KAAC,EAAE;QACD,oBAAoB;QACpB,cAAc;QACd,UAAU;QACV,UAAU;QACV,UAAU;QACV,WAAW;AACZ,KAAA,CAAC,CAAC;AAEH,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,aAAa,EAAA,EACZ,SAAS,EAAE,UAAU,CAAC,SAAS,EAC/B,QAAQ,EAAE,OAAO,EACjB,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,UAAU,EAAE,UAAU,EAAA,CACtB,EACF;AACJ;;;;"}
|
|
@@ -48,6 +48,8 @@ export interface MessageBubbleProps {
|
|
|
48
48
|
formatTimestamp?: (timestamp: string) => string;
|
|
49
49
|
/** CSS classes for customizing the component styling. */
|
|
50
50
|
customCssClasses?: MessageBubbleCssClasses;
|
|
51
|
+
/** Link target open behavior on click. */
|
|
52
|
+
linkTarget?: string;
|
|
51
53
|
/** A callback which is called when user clicks a link. */
|
|
52
54
|
onLinkClick?: (href?: string) => void;
|
|
53
55
|
}
|
|
@@ -58,5 +60,5 @@ export interface MessageBubbleProps {
|
|
|
58
60
|
*
|
|
59
61
|
* @param props - {@link MessageBubbleProps}
|
|
60
62
|
*/
|
|
61
|
-
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, onLinkClick, }: MessageBubbleProps): React.JSX.Element;
|
|
63
|
+
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, linkTarget, onLinkClick, }: MessageBubbleProps): React.JSX.Element;
|
|
62
64
|
//# sourceMappingURL=MessageBubble.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageBubble.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,EAAiB,MAAM,2BAA2B,CAAC;AAKnE,OAAO,EAAmB,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB,CAAC,EAAE,yBAAyB,CAAC;CACvD;AAuBD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,mBAA0B,EAC1B,aAAoB,EACpB,gBAAgB,EAChB,eAAwC,EACxC,WAAW,GACZ,EAAE,kBAAkB,
|
|
1
|
+
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageBubble.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAkB,MAAM,OAAO,CAAC;AACvC,OAAO,EAAE,OAAO,EAAiB,MAAM,2BAA2B,CAAC;AAKnE,OAAO,EAAmB,yBAAyB,EAAE,MAAM,mBAAmB,CAAC;AAE/E;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,yBAAyB,CAAC,EAAE,yBAAyB,CAAC;CACvD;AAuBD;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;IAChD,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,uBAAuB,CAAC;IAC3C,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,EAC5B,OAAO,EACP,mBAA0B,EAC1B,aAAoB,EACpB,gBAAgB,EAChB,eAAwC,EACxC,UAAU,EACV,WAAW,GACZ,EAAE,kBAAkB,qBA6DpB"}
|
|
@@ -17,7 +17,7 @@ const builtInCssClasses = withStylelessCssClasses("MessageBubble", {
|
|
|
17
17
|
text: "text-[13px] @[480px]:text-base prose overflow-x-auto",
|
|
18
18
|
text__bot: "text-slate-900",
|
|
19
19
|
text__user: "text-white break-words",
|
|
20
|
-
timestamp: "w-fit
|
|
20
|
+
timestamp: "w-fit ml-4 @lg:ml-0 text-slate-400 text-[10px] @[480px]:text-[13px] opacity-0 peer-hover:opacity-100 duration-200 whitespace-pre-wrap",
|
|
21
21
|
timestamp__bot: "",
|
|
22
22
|
timestamp__user: "ml-auto",
|
|
23
23
|
feedbackButtonsCssClasses: {},
|
|
@@ -29,7 +29,7 @@ const builtInCssClasses = withStylelessCssClasses("MessageBubble", {
|
|
|
29
29
|
*
|
|
30
30
|
* @param props - {@link MessageBubbleProps}
|
|
31
31
|
*/
|
|
32
|
-
function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = true, customCssClasses, formatTimestamp = defaultFormatTimestamp, onLinkClick, }) {
|
|
32
|
+
function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = true, customCssClasses, formatTimestamp = defaultFormatTimestamp, linkTarget, onLinkClick, }) {
|
|
33
33
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
34
34
|
const bubbleCssClasses = twMerge(cssClasses.bubble, message.source === MessageSource.USER
|
|
35
35
|
? cssClasses.bubble__user
|
|
@@ -50,7 +50,7 @@ function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = tr
|
|
|
50
50
|
React.createElement("div", { className: subContainerCssClasses },
|
|
51
51
|
React.createElement("div", { className: bubbleCssClasses },
|
|
52
52
|
showFeedbackButtons && message.source === MessageSource.BOT && (React.createElement(FeedbackButtons, { customCssClasses: cssClasses.feedbackButtonsCssClasses, responseId: message.responseId })),
|
|
53
|
-
React.createElement(Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses, onLinkClick: onLinkClick })),
|
|
53
|
+
React.createElement(Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses, linkTarget: linkTarget, onLinkClick: onLinkClick })),
|
|
54
54
|
showTimestamp && (React.createElement("div", { className: timestampCssClasses }, message.timestamp ? formatTimestamp(message.timestamp) : " ")))));
|
|
55
55
|
}
|
|
56
56
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.mjs","sources":["../../../../src/components/MessageBubble.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { Message, MessageSource } from \"@yext/chat-headless-react\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { FeedbackButtons, FeedbackButtonsCssClasses } from \"./FeedbackButtons\";\n\n/**\n * The CSS class interface for the {@link MessageBubble} component.\n *\n * @public\n */\nexport interface MessageBubbleCssClasses {\n topContainer?: string;\n subContainer?: string;\n subContainer__bot?: string;\n subContainer__user?: string;\n bubble?: string;\n bubble__bot?: string;\n bubble__user?: string;\n text?: string;\n text__bot?: string;\n text__user?: string;\n timestamp?: string;\n timestamp__bot?: string;\n timestamp__user?: string;\n feedbackButtonsCssClasses?: FeedbackButtonsCssClasses;\n}\n\nconst builtInCssClasses: MessageBubbleCssClasses =\n withStylelessCssClasses<MessageBubbleCssClasses>(\"MessageBubble\", {\n topContainer: \"w-full animate-fade-in @container\",\n subContainer:\n \"flex flex-col @lg:flex-row @lg:items-center @lg:gap-x-2 @lg:m-1\",\n subContainer__bot: \"\",\n subContainer__user: \"@lg:flex-row-reverse\",\n bubble: \"relative group peer w-fit max-w-[80%] rounded-2xl p-4\",\n bubble__bot: \"bg-gradient-to-tr from-slate-50 to-slate-100\",\n bubble__user:\n \"ml-auto @lg:ml-0 bg-gradient-to-tr from-blue-600 to-blue-700 text-white\",\n text: \"text-[13px] @[480px]:text-base prose overflow-x-auto\",\n text__bot: \"text-slate-900\",\n text__user: \"text-white break-words\",\n timestamp:\n \"w-fit
|
|
1
|
+
{"version":3,"file":"MessageBubble.mjs","sources":["../../../../src/components/MessageBubble.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { Message, MessageSource } from \"@yext/chat-headless-react\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { FeedbackButtons, FeedbackButtonsCssClasses } from \"./FeedbackButtons\";\n\n/**\n * The CSS class interface for the {@link MessageBubble} component.\n *\n * @public\n */\nexport interface MessageBubbleCssClasses {\n topContainer?: string;\n subContainer?: string;\n subContainer__bot?: string;\n subContainer__user?: string;\n bubble?: string;\n bubble__bot?: string;\n bubble__user?: string;\n text?: string;\n text__bot?: string;\n text__user?: string;\n timestamp?: string;\n timestamp__bot?: string;\n timestamp__user?: string;\n feedbackButtonsCssClasses?: FeedbackButtonsCssClasses;\n}\n\nconst builtInCssClasses: MessageBubbleCssClasses =\n withStylelessCssClasses<MessageBubbleCssClasses>(\"MessageBubble\", {\n topContainer: \"w-full animate-fade-in @container\",\n subContainer:\n \"flex flex-col @lg:flex-row @lg:items-center @lg:gap-x-2 @lg:m-1\",\n subContainer__bot: \"\",\n subContainer__user: \"@lg:flex-row-reverse\",\n bubble: \"relative group peer w-fit max-w-[80%] rounded-2xl p-4\",\n bubble__bot: \"bg-gradient-to-tr from-slate-50 to-slate-100\",\n bubble__user:\n \"ml-auto @lg:ml-0 bg-gradient-to-tr from-blue-600 to-blue-700 text-white\",\n text: \"text-[13px] @[480px]:text-base prose overflow-x-auto\",\n text__bot: \"text-slate-900\",\n text__user: \"text-white break-words\",\n timestamp:\n \"w-fit ml-4 @lg:ml-0 text-slate-400 text-[10px] @[480px]:text-[13px] opacity-0 peer-hover:opacity-100 duration-200 whitespace-pre-wrap\",\n timestamp__bot: \"\",\n timestamp__user: \"ml-auto\",\n feedbackButtonsCssClasses: {},\n });\n\n/**\n * The props for the {@link MessageBubble} component.\n *\n * @public\n */\nexport interface MessageBubbleProps {\n /** The message to display. */\n message: Message;\n /**\n * Whether to show the feedback buttons on the message bubble.\n * Defaults to true.\n */\n showFeedbackButtons?: boolean;\n /**\n * Whether to show the timestamp of the message with the message bubble.\n * Defaults to true.\n */\n showTimestamp?: boolean;\n /**\n * A function which is called to format the message's timestamp given in\n * ISO format (e.g. \"2023-05-18T19:33:34.553Z\").\n * Defaults to \"HH:MM A\" (e.g. \"7:33 PM\").\n */\n formatTimestamp?: (timestamp: string) => string;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageBubbleCssClasses;\n /** Link target open behavior on click. */\n linkTarget?: string;\n /** A callback which is called when user clicks a link. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * A component that displays the provided message.\n *\n * @public\n *\n * @param props - {@link MessageBubbleProps}\n */\nexport function MessageBubble({\n message,\n showFeedbackButtons = true,\n showTimestamp = true,\n customCssClasses,\n formatTimestamp = defaultFormatTimestamp,\n linkTarget,\n onLinkClick,\n}: MessageBubbleProps) {\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const bubbleCssClasses = twMerge(\n cssClasses.bubble,\n message.source === MessageSource.USER\n ? cssClasses.bubble__user\n : cssClasses.bubble__bot\n );\n const textCssClasses = twMerge(\n cssClasses.text,\n message.source === MessageSource.USER\n ? cssClasses.text__user\n : cssClasses.text__bot\n );\n const subContainerCssClasses = twMerge(\n cssClasses.subContainer,\n message.source === MessageSource.USER\n ? cssClasses.subContainer__user\n : cssClasses.subContainer__bot\n );\n const timestampCssClasses = twMerge(\n cssClasses.timestamp,\n message.source === MessageSource.USER\n ? cssClasses.timestamp__user\n : cssClasses.timestamp__bot\n );\n\n const markdownCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: textCssClasses,\n }),\n [textCssClasses]\n );\n\n return (\n <div className={cssClasses.topContainer}>\n <div className={subContainerCssClasses}>\n <div className={bubbleCssClasses}>\n {showFeedbackButtons && message.source === MessageSource.BOT && (\n <FeedbackButtons\n customCssClasses={cssClasses.feedbackButtonsCssClasses}\n responseId={message.responseId}\n />\n )}\n <Markdown\n content={message.text}\n responseId={message.responseId}\n customCssClasses={markdownCssClasses}\n linkTarget={linkTarget}\n onLinkClick={onLinkClick}\n />\n </div>\n {/* fallback on empty space here to perserve the height for timestamp div */}\n {showTimestamp && (\n <div className={timestampCssClasses}>\n {message.timestamp ? formatTimestamp(message.timestamp) : \" \"}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Formats message's timestamp from \"2023-05-18T19:33:34.553Z\" to \"7:33 PM\"\n *\n * @param timestamp - the timestamp to convert from\n * @returns formatted timestamp\n */\nfunction defaultFormatTimestamp(timestamp: string): string {\n return new Date(timestamp).toLocaleString(undefined, {\n hour: \"numeric\",\n minute: \"numeric\",\n hour12: true,\n });\n}\n"],"names":[],"mappings":";;;;;;;;AA8BA,MAAM,iBAAiB,GACrB,uBAAuB,CAA0B,eAAe,EAAE;AAChE,IAAA,YAAY,EAAE,mCAAmC;AACjD,IAAA,YAAY,EACV,iEAAiE;AACnE,IAAA,iBAAiB,EAAE,EAAE;AACrB,IAAA,kBAAkB,EAAE,sBAAsB;AAC1C,IAAA,MAAM,EAAE,uDAAuD;AAC/D,IAAA,WAAW,EAAE,8CAA8C;AAC3D,IAAA,YAAY,EACV,yEAAyE;AAC3E,IAAA,IAAI,EAAE,sDAAsD;AAC5D,IAAA,SAAS,EAAE,gBAAgB;AAC3B,IAAA,UAAU,EAAE,wBAAwB;AACpC,IAAA,SAAS,EACP,uIAAuI;AACzI,IAAA,cAAc,EAAE,EAAE;AAClB,IAAA,eAAe,EAAE,SAAS;AAC1B,IAAA,yBAAyB,EAAE,EAAE;AAC9B,CAAA,CAAC,CAAC;AAkCL;;;;;;AAMG;AACG,SAAU,aAAa,CAAC,EAC5B,OAAO,EACP,mBAAmB,GAAG,IAAI,EAC1B,aAAa,GAAG,IAAI,EACpB,gBAAgB,EAChB,eAAe,GAAG,sBAAsB,EACxC,UAAU,EACV,WAAW,GACQ,EAAA;IACnB,MAAM,UAAU,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAC9E,IAAA,MAAM,gBAAgB,GAAG,OAAO,CAC9B,UAAU,CAAC,MAAM,EACjB,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,IAAI;UACjC,UAAU,CAAC,YAAY;AACzB,UAAE,UAAU,CAAC,WAAW,CAC3B,CAAC;AACF,IAAA,MAAM,cAAc,GAAG,OAAO,CAC5B,UAAU,CAAC,IAAI,EACf,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,IAAI;UACjC,UAAU,CAAC,UAAU;AACvB,UAAE,UAAU,CAAC,SAAS,CACzB,CAAC;AACF,IAAA,MAAM,sBAAsB,GAAG,OAAO,CACpC,UAAU,CAAC,YAAY,EACvB,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,IAAI;UACjC,UAAU,CAAC,kBAAkB;AAC/B,UAAE,UAAU,CAAC,iBAAiB,CACjC,CAAC;AACF,IAAA,MAAM,mBAAmB,GAAG,OAAO,CACjC,UAAU,CAAC,SAAS,EACpB,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,IAAI;UACjC,UAAU,CAAC,eAAe;AAC5B,UAAE,UAAU,CAAC,cAAc,CAC9B,CAAC;AAEF,IAAA,MAAM,kBAAkB,GAAuB,OAAO,CACpD,OAAO;AACL,QAAA,SAAS,EAAE,cAAc;AAC1B,KAAA,CAAC,EACF,CAAC,cAAc,CAAC,CACjB,CAAC;AAEF,IAAA,QACE,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,UAAU,CAAC,YAAY,EAAA;QACrC,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,sBAAsB,EAAA;YACpC,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,gBAAgB,EAAA;gBAC7B,mBAAmB,IAAI,OAAO,CAAC,MAAM,KAAK,aAAa,CAAC,GAAG,KAC1D,KAAC,CAAA,aAAA,CAAA,eAAe,IACd,gBAAgB,EAAE,UAAU,CAAC,yBAAyB,EACtD,UAAU,EAAE,OAAO,CAAC,UAAU,EAAA,CAC9B,CACH;gBACD,KAAC,CAAA,aAAA,CAAA,QAAQ,EACP,EAAA,OAAO,EAAE,OAAO,CAAC,IAAI,EACrB,UAAU,EAAE,OAAO,CAAC,UAAU,EAC9B,gBAAgB,EAAE,kBAAkB,EACpC,UAAU,EAAE,UAAU,EACtB,WAAW,EAAE,WAAW,EAAA,CACxB,CACE;AAEL,YAAA,aAAa,KACZ,KAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,SAAS,EAAE,mBAAmB,EAChC,EAAA,OAAO,CAAC,SAAS,GAAG,eAAe,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,GAAG,CACzD,CACP,CACG,CACF,EACN;AACJ,CAAC;AAED;;;;;AAKG;AACH,SAAS,sBAAsB,CAAC,SAAiB,EAAA;IAC/C,OAAO,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE;AACnD,QAAA,IAAI,EAAE,SAAS;AACf,QAAA,MAAM,EAAE,SAAS;AACjB,QAAA,MAAM,EAAE,IAAI;AACb,KAAA,CAAC,CAAC;AACL;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAW3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;IAC/C,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAW3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;IAC/C,0CAA0C;IAC1C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,2CAA2C;IAC3C,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;CAChC;AAWD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAmDhE,CAAC"}
|
|
@@ -31,7 +31,14 @@ const MessageSuggestions = ({ handleError, suggestions, customCssClasses, stream
|
|
|
31
31
|
.finally(() => {
|
|
32
32
|
onSend?.(msg);
|
|
33
33
|
});
|
|
34
|
-
}, [
|
|
34
|
+
}, [
|
|
35
|
+
notes,
|
|
36
|
+
actions,
|
|
37
|
+
sendMessageWithRetries,
|
|
38
|
+
handleError,
|
|
39
|
+
defaultHandleApiError,
|
|
40
|
+
onSend,
|
|
41
|
+
]);
|
|
35
42
|
const classes = useComposedCssClasses(defaultClassnames, customCssClasses);
|
|
36
43
|
return (React.createElement("div", { className: classes.container }, suggestions.map((suggestion, index) => (React.createElement("button", { key: index, className: classes.suggestion,
|
|
37
44
|
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.mjs","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { useSendMessageWithRetries } from \"../hooks/useSendMessageWithRetries\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n /** List of clickable message suggestions to render. */\n suggestions: string[];\n /** {@inheritdoc ChatInputProps.handleError} */\n handleError?: (e: unknown) => void;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageSuggestionCssClasses;\n /** {@inheritdoc ChatInputProps.stream} */\n stream?: boolean;\n /** {@inheritdoc ChatInputProps.onSend} */\n onSend?: (message: string) => void;\n /** {@inheritdoc ChatInputProps.onRetry} */\n onRetry?: (e: unknown) => void
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.mjs","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { useSendMessageWithRetries } from \"../hooks/useSendMessageWithRetries\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n /** List of clickable message suggestions to render. */\n suggestions: string[];\n /** {@inheritdoc ChatInputProps.handleError} */\n handleError?: (e: unknown) => void;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageSuggestionCssClasses;\n /** {@inheritdoc ChatInputProps.stream} */\n stream?: boolean;\n /** {@inheritdoc ChatInputProps.onSend} */\n onSend?: (message: string) => void;\n /** {@inheritdoc ChatInputProps.onRetry} */\n onRetry?: (e: unknown) => void;\n}\n\nconst defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(\n \"Suggestions\",\n {\n container: \"flex gap-2 mb-4 w-full overflow-x-auto flex-wrap\",\n suggestion:\n \"hover:cursor-pointer px-2 py-1 bg-white hover:bg-slate-300 rounded-full text-sm text-blue-700 border border-blue-700 hover:underline\",\n }\n);\n\n/**\n * A component that displays a list of suggested messages\n * to the user, which they can click to send the message to the bot.\n *\n * @internal\n */\nexport const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({\n handleError,\n suggestions,\n customCssClasses,\n stream = false,\n onSend,\n onRetry,\n}) => {\n const actions = useChatActions();\n const notes = useChatState((state) => state.conversation.notes);\n const defaultHandleApiError = useDefaultHandleApiError();\n const sendMessageWithRetries = useSendMessageWithRetries(stream, 1, onRetry);\n const sendMsg = useCallback(\n (msg: string) => {\n const newNotes = {\n ...(notes || {}),\n suggestedReplies: undefined,\n } satisfies MessageNotes;\n actions.setMessageNotes(newNotes);\n sendMessageWithRetries(msg)\n .catch(handleError ?? defaultHandleApiError)\n .finally(() => {\n onSend?.(msg);\n });\n },\n [\n notes,\n actions,\n sendMessageWithRetries,\n handleError,\n defaultHandleApiError,\n onSend,\n ]\n );\n\n const classes = useComposedCssClasses(defaultClassnames, customCssClasses);\n\n return (\n <div className={classes.container}>\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n className={classes.suggestion}\n // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop\n onClick={() => sendMsg(suggestion)}\n >\n {suggestion}\n </button>\n ))}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAyCA,MAAM,iBAAiB,GAAgC,uBAAuB,CAC5E,aAAa,EACb;AACE,IAAA,SAAS,EAAE,kDAAkD;AAC7D,IAAA,UAAU,EACR,sIAAsI;AACzI,CAAA,CACF,CAAC;AAEF;;;;;AAKG;MACU,kBAAkB,GAAsC,CAAC,EACpE,WAAW,EACX,WAAW,EACX,gBAAgB,EAChB,MAAM,GAAG,KAAK,EACd,MAAM,EACN,OAAO,GACR,KAAI;AACH,IAAA,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;AACjC,IAAA,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AAChE,IAAA,MAAM,qBAAqB,GAAG,wBAAwB,EAAE,CAAC;IACzD,MAAM,sBAAsB,GAAG,yBAAyB,CAAC,MAAM,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;AAC7E,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,GAAW,KAAI;AACd,QAAA,MAAM,QAAQ,GAAG;AACf,YAAA,IAAI,KAAK,IAAI,EAAE,CAAC;AAChB,YAAA,gBAAgB,EAAE,SAAS;SACL,CAAC;AACzB,QAAA,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClC,sBAAsB,CAAC,GAAG,CAAC;AACxB,aAAA,KAAK,CAAC,WAAW,IAAI,qBAAqB,CAAC;aAC3C,OAAO,CAAC,MAAK;AACZ,YAAA,MAAM,GAAG,GAAG,CAAC,CAAC;AAChB,SAAC,CAAC,CAAC;AACP,KAAC,EACD;QACE,KAAK;QACL,OAAO;QACP,sBAAsB;QACtB,WAAW;QACX,qBAAqB;QACrB,MAAM;AACP,KAAA,CACF,CAAC;IAEF,MAAM,OAAO,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE3E,IAAA,QACE,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,OAAO,CAAC,SAAS,EAAA,EAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,MACjC,KACE,CAAA,aAAA,CAAA,QAAA,EAAA,EAAA,GAAG,EAAE,KAAK,EACV,SAAS,EAAE,OAAO,CAAC,UAAU;;AAE7B,QAAA,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAAA,EAEjC,UAAU,CACJ,CACV,CAAC,CACE,EACN;AACJ;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFetchInitialMessage.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useFetchInitialMessage.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EAClC,MAAM,UAAQ,EACd,eAAe,UAAO,
|
|
1
|
+
{"version":3,"file":"useFetchInitialMessage.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useFetchInitialMessage.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,EAClC,MAAM,UAAQ,EACd,eAAe,UAAO,QAwBvB"}
|
|
@@ -24,7 +24,15 @@ function useFetchInitialMessage(handleError, stream = false, customCondition = t
|
|
|
24
24
|
}
|
|
25
25
|
const res = stream ? chat.streamNextMessage() : chat.getNextMessage();
|
|
26
26
|
res.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
|
|
27
|
-
}, [
|
|
27
|
+
}, [
|
|
28
|
+
chat,
|
|
29
|
+
stream,
|
|
30
|
+
handleError,
|
|
31
|
+
defaultHandleApiError,
|
|
32
|
+
canSendMessage,
|
|
33
|
+
customCondition,
|
|
34
|
+
messages.length,
|
|
35
|
+
]);
|
|
28
36
|
}
|
|
29
37
|
|
|
30
38
|
export { useFetchInitialMessage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useFetchInitialMessage.mjs","sources":["../../../../src/hooks/useFetchInitialMessage.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { useChatState, useChatActions } from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\n\n/**\n * Sends a request to Chat API to fetch the initial message when the\n * conversation first start or when the message history is reset.\n *\n * @internal\n *\n * @param handleError - A function which is called when an error occurs while fetching for initial message.\n * By default, the error is logged to the console and an error message is added to state.\n * @param stream - Enable streaming behavior by making a request to Chat Streaming API. Defaults to false.\n * @param customCondition - additional condition for when to fetch initial message\n */\nexport function useFetchInitialMessage(\n handleError?: (e: unknown) => void,\n stream = false,\n customCondition = true
|
|
1
|
+
{"version":3,"file":"useFetchInitialMessage.mjs","sources":["../../../../src/hooks/useFetchInitialMessage.ts"],"sourcesContent":["import { useEffect } from \"react\";\nimport { useChatState, useChatActions } from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\n\n/**\n * Sends a request to Chat API to fetch the initial message when the\n * conversation first start or when the message history is reset.\n *\n * @internal\n *\n * @param handleError - A function which is called when an error occurs while fetching for initial message.\n * By default, the error is logged to the console and an error message is added to state.\n * @param stream - Enable streaming behavior by making a request to Chat Streaming API. Defaults to false.\n * @param customCondition - additional condition for when to fetch initial message\n */\nexport function useFetchInitialMessage(\n handleError?: (e: unknown) => void,\n stream = false,\n customCondition = true\n) {\n const chat = useChatActions();\n const defaultHandleApiError = useDefaultHandleApiError();\n const messages = useChatState((state) => state.conversation.messages);\n const canSendMessage = useChatState(\n (state) => state.conversation.canSendMessage\n );\n\n useEffect(() => {\n if (messages.length !== 0 || !canSendMessage || !customCondition) {\n return;\n }\n const res = stream ? chat.streamNextMessage() : chat.getNextMessage();\n res.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));\n }, [\n chat,\n stream,\n handleError,\n defaultHandleApiError,\n canSendMessage,\n customCondition,\n messages.length,\n ]);\n}\n"],"names":[],"mappings":";;;;AAIA;;;;;;;;;;AAUG;AACG,SAAU,sBAAsB,CACpC,WAAkC,EAClC,MAAM,GAAG,KAAK,EACd,eAAe,GAAG,IAAI,EAAA;AAEtB,IAAA,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;AAC9B,IAAA,MAAM,qBAAqB,GAAG,wBAAwB,EAAE,CAAC;AACzD,IAAA,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;AACtE,IAAA,MAAM,cAAc,GAAG,YAAY,CACjC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,cAAc,CAC7C,CAAC;IAEF,SAAS,CAAC,MAAK;QACb,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,CAAC,cAAc,IAAI,CAAC,eAAe,EAAE;YAChE,OAAO;AACR,SAAA;AACD,QAAA,MAAM,GAAG,GAAG,MAAM,GAAG,IAAI,CAAC,iBAAiB,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;QACtE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9E,KAAC,EAAE;QACD,IAAI;QACJ,MAAM;QACN,WAAW;QACX,qBAAqB;QACrB,cAAc;QACd,eAAe;AACf,QAAA,QAAQ,CAAC,MAAM;AAChB,KAAA,CAAC,CAAC;AACL;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSendMessageWithRetries.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useSendMessageWithRetries.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,UAAQ,EACd,UAAU,SAAI,EACd,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,GAC7B,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"useSendMessageWithRetries.d.ts","sourceRoot":"","sources":["../../../../src/hooks/useSendMessageWithRetries.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,UAAQ,EACd,UAAU,SAAI,EACd,OAAO,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,GAC7B,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAiClC"}
|
|
@@ -23,7 +23,9 @@ function useSendMessageWithRetries(stream = false, maxRetries = 0, onRetry) {
|
|
|
23
23
|
let text = input;
|
|
24
24
|
for (let numRetries = 0; numRetries <= maxRetries; numRetries++) {
|
|
25
25
|
if (numRetries > 0 && !!err) {
|
|
26
|
-
if (err instanceof ApiError &&
|
|
26
|
+
if (err instanceof ApiError &&
|
|
27
|
+
!!err.statusCode &&
|
|
28
|
+
err.statusCode >= 500) {
|
|
27
29
|
onRetry?.(err);
|
|
28
30
|
// avoid re-adding user message to conversation history on retry
|
|
29
31
|
text = "";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSendMessageWithRetries.mjs","sources":["../../../../src/hooks/useSendMessageWithRetries.ts"],"sourcesContent":["import { ApiError, useChatActions } from \"@yext/chat-headless-react\";\nimport { useCallback } from \"react\";\n\n/**\n * Returns a function that sends a message to the chat API with retries\n * if the API returns a 5xx status code.\n
|
|
1
|
+
{"version":3,"file":"useSendMessageWithRetries.mjs","sources":["../../../../src/hooks/useSendMessageWithRetries.ts"],"sourcesContent":["import { ApiError, useChatActions } from \"@yext/chat-headless-react\";\nimport { useCallback } from \"react\";\n\n/**\n * Returns a function that sends a message to the chat API with retries\n * if the API returns a 5xx status code.\n *\n * @remarks\n * The function will throw the error if the maximum number of retries is reached\n * or if the error is not a 5xx status code.\n *\n * @internal\n *\n * @param stream - If true, use streaming API\n * @param maxRetries - Maximum number of retries\n * @param onRetry - Callback to handle error on each retry\n *\n */\nexport function useSendMessageWithRetries(\n stream = false,\n maxRetries = 0,\n onRetry?: (e: unknown) => void\n): (input: string) => Promise<void> {\n const chat = useChatActions();\n return useCallback(\n async (input: string) => {\n let err: unknown;\n let text = input;\n for (let numRetries = 0; numRetries <= maxRetries; numRetries++) {\n if (numRetries > 0 && !!err) {\n if (\n err instanceof ApiError &&\n !!err.statusCode &&\n err.statusCode >= 500\n ) {\n onRetry?.(err);\n // avoid re-adding user message to conversation history on retry\n text = \"\";\n } else {\n throw err;\n }\n }\n try {\n await (stream\n ? chat.streamNextMessage(text)\n : chat.getNextMessage(text));\n return;\n } catch (e) {\n err = e;\n }\n }\n throw err;\n },\n [chat, maxRetries, onRetry, stream]\n );\n}\n"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;AAcG;AACG,SAAU,yBAAyB,CACvC,MAAM,GAAG,KAAK,EACd,UAAU,GAAG,CAAC,EACd,OAA8B,EAAA;AAE9B,IAAA,MAAM,IAAI,GAAG,cAAc,EAAE,CAAC;AAC9B,IAAA,OAAO,WAAW,CAChB,OAAO,KAAa,KAAI;AACtB,QAAA,IAAI,GAAY,CAAC;QACjB,IAAI,IAAI,GAAG,KAAK,CAAC;QACjB,KAAK,IAAI,UAAU,GAAG,CAAC,EAAE,UAAU,IAAI,UAAU,EAAE,UAAU,EAAE,EAAE;AAC/D,YAAA,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE;gBAC3B,IACE,GAAG,YAAY,QAAQ;oBACvB,CAAC,CAAC,GAAG,CAAC,UAAU;AAChB,oBAAA,GAAG,CAAC,UAAU,IAAI,GAAG,EACrB;AACA,oBAAA,OAAO,GAAG,GAAG,CAAC,CAAC;;oBAEf,IAAI,GAAG,EAAE,CAAC;AACX,iBAAA;AAAM,qBAAA;AACL,oBAAA,MAAM,GAAG,CAAC;AACX,iBAAA;AACF,aAAA;YACD,IAAI;AACF,gBAAA,OAAO,MAAM;AACX,sBAAE,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC;sBAC5B,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/B,OAAO;AACR,aAAA;AAAC,YAAA,OAAO,CAAC,EAAE;gBACV,GAAG,GAAG,CAAC,CAAC;AACT,aAAA;AACF,SAAA;AACD,QAAA,MAAM,GAAG,CAAC;KACX,EACD,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,CAAC,CACpC,CAAC;AACJ;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yext/chat-ui-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "A library of React Components for powering Yext Chat integrations.",
|
|
5
5
|
"author": "clippy@yext.com",
|
|
6
6
|
"main": "./lib/commonjs/src/index.js",
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@testing-library/user-event": "^14.4.3",
|
|
70
70
|
"@types/jest": "^29.5.1",
|
|
71
71
|
"@types/react": "^18.2.7",
|
|
72
|
-
"@yext/chat-headless-react": "^0.
|
|
72
|
+
"@yext/chat-headless-react": "^0.9.0",
|
|
73
73
|
"@yext/eslint-config": "^1.0.0",
|
|
74
74
|
"babel-jest": "^29.5.0",
|
|
75
75
|
"eslint": "^8.39.0",
|
|
@@ -91,7 +91,7 @@
|
|
|
91
91
|
"typescript": "^5.0.4"
|
|
92
92
|
},
|
|
93
93
|
"peerDependencies": {
|
|
94
|
-
"@yext/chat-headless-react": "^0.
|
|
94
|
+
"@yext/chat-headless-react": "^0.9.0",
|
|
95
95
|
"react": "^16.14 || ^17 || ^18",
|
|
96
96
|
"react-dom": "^16.14 || ^17 || || ^18"
|
|
97
97
|
},
|
|
@@ -63,7 +63,7 @@ export interface ChatInputProps {
|
|
|
63
63
|
* A function which is called when a retryable error occurs from
|
|
64
64
|
* Chat API while processing the user's message.
|
|
65
65
|
*/
|
|
66
|
-
onRetry?: (e: unknown) => void
|
|
66
|
+
onRetry?: (e: unknown) => void;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
/**
|
|
@@ -92,7 +92,7 @@ export function ChatInput({
|
|
|
92
92
|
(state) => state.conversation.canSendMessage
|
|
93
93
|
);
|
|
94
94
|
const defaultHandleApiError = useDefaultHandleApiError();
|
|
95
|
-
const sendMessageWithRetries =
|
|
95
|
+
const sendMessageWithRetries = useSendMessageWithRetries(stream, 1, onRetry);
|
|
96
96
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
97
97
|
|
|
98
98
|
const sendMessage = useCallback(async () => {
|
|
@@ -100,18 +100,27 @@ export function ChatInput({
|
|
|
100
100
|
sendMessageWithRetries(input)
|
|
101
101
|
.catch(handleError ?? defaultHandleApiError)
|
|
102
102
|
.finally(() => {
|
|
103
|
-
onSend?.(input)
|
|
104
|
-
})
|
|
105
|
-
}, [
|
|
103
|
+
onSend?.(input);
|
|
104
|
+
});
|
|
105
|
+
}, [
|
|
106
|
+
sendMessageWithRetries,
|
|
107
|
+
input,
|
|
108
|
+
handleError,
|
|
109
|
+
defaultHandleApiError,
|
|
110
|
+
onSend,
|
|
111
|
+
]);
|
|
106
112
|
|
|
107
113
|
const handleKeyDown = useCallback(
|
|
108
114
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
109
|
-
if (
|
|
115
|
+
if (
|
|
116
|
+
!e.shiftKey &&
|
|
117
|
+
e.key === "Enter" &&
|
|
110
118
|
// The Japanese Keyboard uses "Enter" key to convert from Hiragana to Kanji.
|
|
111
119
|
// "isComposing" is a flag that indicates whether the event is part of an ongoing composition session.
|
|
112
120
|
// Safari does not support `isComposing` with the Japanese IME event,
|
|
113
121
|
// so we have to additionally check for the keyCode to handle that edge case.
|
|
114
|
-
!(e.nativeEvent.isComposing || e.keyCode === 229)
|
|
122
|
+
!(e.nativeEvent.isComposing || e.keyCode === 229)
|
|
123
|
+
) {
|
|
115
124
|
e.preventDefault();
|
|
116
125
|
if (canSendMessage && input.trim().length !== 0) {
|
|
117
126
|
sendMessage();
|
|
@@ -45,10 +45,10 @@ const builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(
|
|
|
45
45
|
{
|
|
46
46
|
container: "h-full w-full flex flex-col relative shadow-2xl bg-white",
|
|
47
47
|
messagesScrollContainer: "flex flex-col mt-auto overflow-hidden",
|
|
48
|
-
messagesContainer: "flex flex-col gap-y-1 px-4 overflow-auto",
|
|
48
|
+
messagesContainer: "flex flex-col gap-y-1 px-4 overflow-auto [&>*:first-child]:mt-3",
|
|
49
49
|
inputContainer: "w-full p-4",
|
|
50
50
|
messageBubbleCssClasses: {
|
|
51
|
-
topContainer: "
|
|
51
|
+
topContainer: "mt-1",
|
|
52
52
|
},
|
|
53
53
|
footer: "text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]",
|
|
54
54
|
}
|
|
@@ -75,6 +75,10 @@ export interface ChatPanelProps
|
|
|
75
75
|
* can click on instead of typing their own.
|
|
76
76
|
*/
|
|
77
77
|
messageSuggestions?: string[];
|
|
78
|
+
/** Link target open behavior on click.
|
|
79
|
+
* Defaults to "_blank".
|
|
80
|
+
*/
|
|
81
|
+
linkTarget?: string;
|
|
78
82
|
/** A callback which is called when user clicks a link. */
|
|
79
83
|
onLinkClick?: (href?: string) => void;
|
|
80
84
|
/**
|
|
@@ -101,9 +105,10 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
101
105
|
stream,
|
|
102
106
|
handleError,
|
|
103
107
|
messageSuggestions,
|
|
108
|
+
linkTarget = "_blank",
|
|
104
109
|
onLinkClick,
|
|
105
|
-
onSend:onSendProp,
|
|
106
|
-
onRetry:onRetryProp,
|
|
110
|
+
onSend: onSendProp,
|
|
111
|
+
onRetry: onRetryProp,
|
|
107
112
|
retryText = "Error occurred. Retrying",
|
|
108
113
|
} = props;
|
|
109
114
|
const messages = useChatState((state) => state.conversation.messages);
|
|
@@ -116,15 +121,21 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
116
121
|
useFetchInitialMessage(handleError, stream);
|
|
117
122
|
|
|
118
123
|
const [retry, setRetry] = useState(false);
|
|
119
|
-
const onSend = useCallback(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
124
|
+
const onSend = useCallback(
|
|
125
|
+
(message: string) => {
|
|
126
|
+
onSendProp?.(message);
|
|
127
|
+
setRetry(false);
|
|
128
|
+
},
|
|
129
|
+
[onSendProp]
|
|
130
|
+
);
|
|
123
131
|
|
|
124
|
-
const onRetry = useCallback(
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
const onRetry = useCallback(
|
|
133
|
+
(e: unknown) => {
|
|
134
|
+
onRetryProp?.(e);
|
|
135
|
+
setRetry(true);
|
|
136
|
+
},
|
|
137
|
+
[onRetryProp]
|
|
138
|
+
);
|
|
128
139
|
|
|
129
140
|
useEffect(() => {
|
|
130
141
|
reportAnalyticsEvent({
|
|
@@ -190,14 +201,21 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
190
201
|
{...props}
|
|
191
202
|
customCssClasses={cssClasses.messageBubbleCssClasses}
|
|
192
203
|
message={message}
|
|
204
|
+
linkTarget={linkTarget}
|
|
193
205
|
onLinkClick={onLinkClick}
|
|
194
206
|
/>
|
|
195
207
|
</div>
|
|
196
208
|
))}
|
|
197
|
-
{loading &&
|
|
198
|
-
<
|
|
199
|
-
|
|
200
|
-
|
|
209
|
+
{loading && (
|
|
210
|
+
<div className="flex">
|
|
211
|
+
<LoadingDots />
|
|
212
|
+
{retry && (
|
|
213
|
+
<p className="text-slate-500 text-[13px] font-bold">
|
|
214
|
+
{retryText}
|
|
215
|
+
</p>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
)}
|
|
201
219
|
</div>
|
|
202
220
|
</div>
|
|
203
221
|
<div className={cssClasses.inputContainer}>
|
|
@@ -222,6 +240,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
222
240
|
<Markdown
|
|
223
241
|
content={footer}
|
|
224
242
|
linkClickEvent="WEBSITE"
|
|
243
|
+
linkTarget={linkTarget}
|
|
225
244
|
onLinkClick={onLinkClick}
|
|
226
245
|
customCssClasses={footerCssClasses}
|
|
227
246
|
/>
|
|
@@ -70,10 +70,10 @@ const builtInCssClasses: ChatPopUpCssClasses = withStylelessCssClasses(
|
|
|
70
70
|
panelCssClasses: {
|
|
71
71
|
container: "max-[480px]:rounded-none rounded-3xl",
|
|
72
72
|
inputContainer: "max-[480px]:rounded-none rounded-b-3xl",
|
|
73
|
-
messagesScrollContainer: "rounded-b-3xl",
|
|
74
73
|
},
|
|
75
74
|
}
|
|
76
75
|
);
|
|
76
|
+
const popupLocalStorageKey = "YEXT_CHAT_OPEN_ON_LOAD";
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* The props for the {@link ChatPopUp} component.
|
|
@@ -151,6 +151,7 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
151
151
|
//only show initial message popup (if specified) when CTA label is not provided
|
|
152
152
|
!ctaLabel && showInitialMessagePopUp
|
|
153
153
|
);
|
|
154
|
+
|
|
154
155
|
const onCloseInitialMessage = useCallback(() => {
|
|
155
156
|
setshowInitialMessage(false);
|
|
156
157
|
}, []);
|
|
@@ -162,27 +163,44 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
162
163
|
// to avoid message requests immediately on load while the popup is still "hidden"
|
|
163
164
|
const [renderChat, setRenderChat] = useState(false);
|
|
164
165
|
|
|
166
|
+
// Set the initial value of the local storage flag for opening on load only if it doesn't already exist
|
|
167
|
+
|
|
168
|
+
if (window.localStorage.getItem(popupLocalStorageKey) === null) {
|
|
169
|
+
window.localStorage.setItem(
|
|
170
|
+
popupLocalStorageKey,
|
|
171
|
+
openOnLoad ? "true" : "false"
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const openOnLoadLocalStorage =
|
|
175
|
+
window.localStorage.getItem(popupLocalStorageKey);
|
|
176
|
+
|
|
177
|
+
/* Open panel on load if:
|
|
178
|
+
- openOnLoad prop is true or there are messages in state (from browser storage), and local storage flag is true */
|
|
179
|
+
const isOpenOnLoad =
|
|
180
|
+
(messages.length > 1 && openOnLoadLocalStorage === "true") || openOnLoad;
|
|
165
181
|
|
|
166
182
|
// only fetch initial message when ChatPanel is closed on load (otherwise, it will be fetched in ChatPanel)
|
|
167
183
|
useFetchInitialMessage(
|
|
168
184
|
showInitialMessagePopUp ? console.error : handleError,
|
|
169
185
|
false,
|
|
170
|
-
(showUnreadNotification || showInitialMessagePopUp) &&
|
|
186
|
+
(showUnreadNotification || showInitialMessagePopUp) &&
|
|
187
|
+
!renderChat &&
|
|
188
|
+
!isOpenOnLoad
|
|
171
189
|
);
|
|
172
190
|
|
|
173
191
|
useEffect(() => {
|
|
174
|
-
|
|
175
|
-
if (!renderChat && (openOnLoad || messages.length > 1)) {
|
|
192
|
+
if (!renderChat && isOpenOnLoad) {
|
|
176
193
|
setShowChat(true);
|
|
177
194
|
setRenderChat(true);
|
|
178
195
|
setshowInitialMessage(false);
|
|
179
196
|
}
|
|
180
|
-
}, [messages.length,
|
|
197
|
+
}, [renderChat, messages.length, isOpenOnLoad]);
|
|
181
198
|
|
|
182
199
|
const onClick = useCallback(() => {
|
|
183
|
-
setShowChat(prev => !prev);
|
|
200
|
+
setShowChat((prev) => !prev);
|
|
184
201
|
setRenderChat(true);
|
|
185
202
|
setshowInitialMessage(false);
|
|
203
|
+
window.localStorage.setItem(popupLocalStorageKey, "true");
|
|
186
204
|
}, []);
|
|
187
205
|
|
|
188
206
|
const onClose = useCallback(() => {
|
|
@@ -190,12 +208,13 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
190
208
|
customOnClose?.();
|
|
191
209
|
// consider all the messages are read while the panel was open
|
|
192
210
|
setNumReadMessagesLength(messages.length);
|
|
193
|
-
|
|
211
|
+
window.localStorage.setItem(popupLocalStorageKey, "false");
|
|
212
|
+
}, [customOnClose, messages.length]);
|
|
194
213
|
|
|
195
214
|
useEffect(() => {
|
|
196
215
|
// update number of unread messages if there are new messages added while the panel is closed
|
|
197
216
|
setNumUnreadMessagesLength(messages.length - numReadMessages);
|
|
198
|
-
}, [messages, numReadMessages]);
|
|
217
|
+
}, [messages.length, numReadMessages]);
|
|
199
218
|
|
|
200
219
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
201
220
|
const panelCssClasses = twMerge(
|
|
@@ -46,6 +46,8 @@ interface MarkdownProps {
|
|
|
46
46
|
* Defaults to 'CHAT_LINK_CLICK'.
|
|
47
47
|
*/
|
|
48
48
|
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
49
|
+
/** Link target open behavior on click. */
|
|
50
|
+
linkTarget?: string;
|
|
49
51
|
/** A callback which is called when a link is clicked. */
|
|
50
52
|
onLinkClick?: (href?: string) => void;
|
|
51
53
|
}
|
|
@@ -64,6 +66,7 @@ export function Markdown({
|
|
|
64
66
|
responseId,
|
|
65
67
|
customCssClasses,
|
|
66
68
|
linkClickEvent = "CHAT_LINK_CLICK",
|
|
69
|
+
linkTarget,
|
|
67
70
|
onLinkClick,
|
|
68
71
|
}: MarkdownProps) {
|
|
69
72
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
@@ -86,7 +89,7 @@ export function Markdown({
|
|
|
86
89
|
<a
|
|
87
90
|
{...props}
|
|
88
91
|
onClick={createClickHandlerFn(props.href)}
|
|
89
|
-
target=
|
|
92
|
+
target={linkTarget}
|
|
90
93
|
rel="noopener noreferrer"
|
|
91
94
|
className={cssClasses.link}
|
|
92
95
|
>
|
|
@@ -100,6 +103,7 @@ export function Markdown({
|
|
|
100
103
|
linkClickEvent,
|
|
101
104
|
responseId,
|
|
102
105
|
cssClasses,
|
|
106
|
+
linkTarget,
|
|
103
107
|
onLinkClick,
|
|
104
108
|
]);
|
|
105
109
|
|