@yext/chat-ui-react 0.8.6 → 0.8.7
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/lib/commonjs/package.json.js +1 -1
- package/lib/commonjs/src/components/ChatPanel.d.ts +2 -0
- package/lib/commonjs/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.js +3 -3
- package/lib/commonjs/src/components/ChatPanel.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 +2 -2
- package/lib/commonjs/src/components/MessageBubble.js.map +1 -1
- package/lib/esm/index.d.ts +5 -1
- package/lib/esm/package.json.mjs +1 -1
- package/lib/esm/src/components/ChatPanel.d.ts +2 -0
- package/lib/esm/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPanel.mjs +3 -3
- package/lib/esm/src/components/ChatPanel.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 +2 -2
- package/lib/esm/src/components/MessageBubble.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ChatPanel.tsx +5 -0
- package/src/components/Markdown.tsx +5 -1
- package/src/components/MessageBubble.tsx +4 -0
|
@@ -36,6 +36,8 @@ export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClass
|
|
|
36
36
|
* can click on instead of typing their own.
|
|
37
37
|
*/
|
|
38
38
|
messageSuggestions?: string[];
|
|
39
|
+
/** A callback which is called when user clicks a link. */
|
|
40
|
+
onLinkClick?: (href?: string) => void;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
43
|
* A component that renders a full panel for chat bot interactions. This includes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EACL,2BAA2B,EAE5B,MAAM,sBAAsB,CAAC;AAG9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;GAIG;AACH,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,CAAC,EAC9D,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC1C,kDAAkD;IAClD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EACL,2BAA2B,EAE5B,MAAM,sBAAsB,CAAC;AAG9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;GAIG;AACH,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,CAAC,EAC9D,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC1C,kDAAkD;IAClD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,qBA8G9C"}
|
|
@@ -36,7 +36,7 @@ const builtInCssClasses = withStylelessCssClasses.withStylelessCssClasses("Panel
|
|
|
36
36
|
* @param props - {@link ChatPanelProps}
|
|
37
37
|
*/
|
|
38
38
|
function ChatPanel(props) {
|
|
39
|
-
const { header, footer, customCssClasses, stream, handleError, messageSuggestions, } = props;
|
|
39
|
+
const { header, footer, customCssClasses, stream, handleError, messageSuggestions, onLinkClick, } = props;
|
|
40
40
|
const messages = chatHeadlessReact.useChatState((state) => state.conversation.messages);
|
|
41
41
|
const loading = chatHeadlessReact.useChatState((state) => state.conversation.isLoading);
|
|
42
42
|
const suggestedReplies = chatHeadlessReact.useChatState((state) => state.conversation.notes?.suggestedReplies);
|
|
@@ -89,12 +89,12 @@ function ChatPanel(props) {
|
|
|
89
89
|
React__default.default.createElement("div", { className: cssClasses.messagesScrollContainer },
|
|
90
90
|
React__default.default.createElement("div", { ref: messagesContainer, className: cssClasses.messagesContainer },
|
|
91
91
|
messages.map((message, index) => (React__default.default.createElement("div", { key: index, ref: setMessagesRef(index) },
|
|
92
|
-
React__default.default.createElement(MessageBubble.MessageBubble, { ...props, customCssClasses: cssClasses.messageBubbleCssClasses, message: message })))),
|
|
92
|
+
React__default.default.createElement(MessageBubble.MessageBubble, { ...props, customCssClasses: cssClasses.messageBubbleCssClasses, message: message, onLinkClick: onLinkClick })))),
|
|
93
93
|
loading && React__default.default.createElement(LoadingDots.LoadingDots, null))),
|
|
94
94
|
React__default.default.createElement("div", { className: cssClasses.inputContainer },
|
|
95
95
|
suggestions && (React__default.default.createElement(MessageSuggestions.MessageSuggestions, { suggestions: suggestions, customCssClasses: cssClasses.messageSuggestionClasses })),
|
|
96
96
|
React__default.default.createElement(ChatInput.ChatInput, { ...props, customCssClasses: cssClasses.inputCssClasses })),
|
|
97
|
-
footer && (React__default.default.createElement(Markdown.Markdown, { content: footer, linkClickEvent: "WEBSITE", customCssClasses: footerCssClasses })))));
|
|
97
|
+
footer && (React__default.default.createElement(Markdown.Markdown, { content: footer, linkClickEvent: "WEBSITE", onLinkClick: onLinkClick, customCssClasses: footerCssClasses })))));
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
exports.ChatPanel = ChatPanel;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.js","sources":["../../../../src/components/ChatPanel.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { useChatState } from \"@yext/chat-headless-react\";\nimport {\n MessageBubble,\n MessageBubbleCssClasses,\n MessageBubbleProps,\n} from \"./MessageBubble\";\nimport { ChatInput, ChatInputCssClasses, ChatInputProps } from \"./ChatInput\";\nimport { LoadingDots } from \"./LoadingDots\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useFetchInitialMessage } from \"../hooks/useFetchInitialMessage\";\nimport {\n MessageSuggestionCssClasses,\n MessageSuggestions,\n} from \"./MessageSuggestions\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\n\n/**\n * The CSS class interface for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelCssClasses {\n container?: string;\n messagesContainer?: string;\n messagesScrollContainer?: string;\n inputContainer?: string;\n inputCssClasses?: ChatInputCssClasses;\n messageBubbleCssClasses?: MessageBubbleCssClasses;\n messageSuggestionClasses?: MessageSuggestionCssClasses;\n footer?: string;\n}\n\nconst builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(\n \"Panel\",\n {\n container: \"h-full w-full flex flex-col relative shadow-2xl bg-white\",\n messagesScrollContainer: \"flex flex-col mt-auto overflow-hidden\",\n messagesContainer: \"flex flex-col gap-y-1 px-4 overflow-auto\",\n inputContainer: \"w-full p-4\",\n messageBubbleCssClasses: {\n topContainer: \"first:mt-4\",\n },\n footer: \"text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]\",\n }\n);\n\n/**\n * The props for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelProps\n extends Omit<MessageBubbleProps, \"customCssClasses\" | \"message\">,\n Omit<ChatInputProps, \"customCssClasses\"> {\n /** A header to render at the top of the panel. */\n header?: ReactNode;\n /** A footer markdown string to render at the bottom of the panel. */\n footer?: string;\n /**\n * CSS classes for customizing the component styling.\n */\n customCssClasses?: ChatPanelCssClasses;\n /**\n * A set of pre-written initial messages that the user\n * can click on instead of typing their own.\n */\n messageSuggestions?: string[];\n}\n\n/**\n * A component that renders a full panel for chat bot interactions. This includes\n * the message bubbles for the conversation, input box with send button, and header\n * (if provided).\n *\n * @public\n *\n * @param props - {@link ChatPanelProps}\n */\nexport function ChatPanel(props: ChatPanelProps) {\n const {\n header,\n footer,\n customCssClasses,\n stream,\n handleError,\n messageSuggestions,\n } = props;\n const messages = useChatState((state) => state.conversation.messages);\n const loading = useChatState((state) => state.conversation.isLoading);\n const suggestedReplies = useChatState(\n (state) => state.conversation.notes?.suggestedReplies\n );\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n useFetchInitialMessage(handleError, stream);\n\n useEffect(() => {\n reportAnalyticsEvent({\n action: \"CHAT_IMPRESSION\",\n });\n }, [reportAnalyticsEvent]);\n\n const suggestions = useMemo(() => {\n if (\n messages.length === 0 ||\n (messages.length === 1 && messages[0].source === \"BOT\")\n ) {\n return messageSuggestions;\n }\n return suggestedReplies;\n }, [messages, suggestedReplies, messageSuggestions]);\n\n const messagesRef = useRef<Array<HTMLDivElement | null>>([]);\n const messagesContainer = useRef<HTMLDivElement>(null);\n\n // Handle scrolling when messages change\n useEffect(() => {\n let scrollTop = 0;\n messagesRef.current = messagesRef.current.slice(0, messages.length);\n\n // Sums up scroll heights of all messages except the last one\n if (messagesRef?.current.length > 1) {\n scrollTop = messagesRef.current\n .slice(0, -1)\n .map((elem, _) => elem?.scrollHeight ?? 0)\n .reduce((total, height) => total + height);\n }\n\n // Scroll to the top of the last message\n messagesContainer.current?.scroll({\n top: scrollTop,\n behavior: \"smooth\",\n });\n }, [messages]);\n\n const setMessagesRef = useCallback((index) => {\n if (!messagesRef?.current) return null;\n return (message) => (messagesRef.current[index] = message);\n }, []);\n\n const footerCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: cssClasses.footer,\n link: \"cursor-pointer hover:underline text-blue-600\",\n }),\n [cssClasses]\n );\n\n return (\n <div className=\"yext-chat w-full h-full\">\n <div className={cssClasses.container}>\n {header}\n <div className={cssClasses.messagesScrollContainer}>\n <div ref={messagesContainer} className={cssClasses.messagesContainer}>\n {messages.map((message, index) => (\n <div key={index} ref={setMessagesRef(index)}>\n <MessageBubble\n {...props}\n customCssClasses={cssClasses.messageBubbleCssClasses}\n message={message}\n />\n </div>\n ))}\n {loading && <LoadingDots />}\n </div>\n </div>\n <div className={cssClasses.inputContainer}>\n {suggestions && (\n <MessageSuggestions\n suggestions={suggestions}\n customCssClasses={cssClasses.messageSuggestionClasses}\n />\n )}\n <ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />\n </div>\n {footer && (\n <Markdown\n content={footer}\n linkClickEvent=\"WEBSITE\"\n customCssClasses={footerCssClasses}\n />\n )}\n </div>\n </div>\n );\n}\n"],"names":["withStylelessCssClasses","useChatState","useComposedCssClasses","useReportAnalyticsEvent","useFetchInitialMessage","useEffect","useMemo","useRef","useCallback","React","MessageBubble","LoadingDots","MessageSuggestions","ChatInput","Markdown"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatPanel.js","sources":["../../../../src/components/ChatPanel.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { useChatState } from \"@yext/chat-headless-react\";\nimport {\n MessageBubble,\n MessageBubbleCssClasses,\n MessageBubbleProps,\n} from \"./MessageBubble\";\nimport { ChatInput, ChatInputCssClasses, ChatInputProps } from \"./ChatInput\";\nimport { LoadingDots } from \"./LoadingDots\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useFetchInitialMessage } from \"../hooks/useFetchInitialMessage\";\nimport {\n MessageSuggestionCssClasses,\n MessageSuggestions,\n} from \"./MessageSuggestions\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\n\n/**\n * The CSS class interface for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelCssClasses {\n container?: string;\n messagesContainer?: string;\n messagesScrollContainer?: string;\n inputContainer?: string;\n inputCssClasses?: ChatInputCssClasses;\n messageBubbleCssClasses?: MessageBubbleCssClasses;\n messageSuggestionClasses?: MessageSuggestionCssClasses;\n footer?: string;\n}\n\nconst builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(\n \"Panel\",\n {\n container: \"h-full w-full flex flex-col relative shadow-2xl bg-white\",\n messagesScrollContainer: \"flex flex-col mt-auto overflow-hidden\",\n messagesContainer: \"flex flex-col gap-y-1 px-4 overflow-auto\",\n inputContainer: \"w-full p-4\",\n messageBubbleCssClasses: {\n topContainer: \"first:mt-4\",\n },\n footer: \"text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]\",\n }\n);\n\n/**\n * The props for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelProps\n extends Omit<MessageBubbleProps, \"customCssClasses\" | \"message\">,\n Omit<ChatInputProps, \"customCssClasses\"> {\n /** A header to render at the top of the panel. */\n header?: ReactNode;\n /** A footer markdown string to render at the bottom of the panel. */\n footer?: string;\n /**\n * CSS classes for customizing the component styling.\n */\n customCssClasses?: ChatPanelCssClasses;\n /**\n * A set of pre-written initial messages that the user\n * can click on instead of typing their own.\n */\n messageSuggestions?: string[];\n /** A callback which is called when user clicks a link. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * A component that renders a full panel for chat bot interactions. This includes\n * the message bubbles for the conversation, input box with send button, and header\n * (if provided).\n *\n * @public\n *\n * @param props - {@link ChatPanelProps}\n */\nexport function ChatPanel(props: ChatPanelProps) {\n const {\n header,\n footer,\n customCssClasses,\n stream,\n handleError,\n messageSuggestions,\n onLinkClick,\n } = props;\n const messages = useChatState((state) => state.conversation.messages);\n const loading = useChatState((state) => state.conversation.isLoading);\n const suggestedReplies = useChatState(\n (state) => state.conversation.notes?.suggestedReplies\n );\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n useFetchInitialMessage(handleError, stream);\n\n useEffect(() => {\n reportAnalyticsEvent({\n action: \"CHAT_IMPRESSION\",\n });\n }, [reportAnalyticsEvent]);\n\n const suggestions = useMemo(() => {\n if (\n messages.length === 0 ||\n (messages.length === 1 && messages[0].source === \"BOT\")\n ) {\n return messageSuggestions;\n }\n return suggestedReplies;\n }, [messages, suggestedReplies, messageSuggestions]);\n\n const messagesRef = useRef<Array<HTMLDivElement | null>>([]);\n const messagesContainer = useRef<HTMLDivElement>(null);\n\n // Handle scrolling when messages change\n useEffect(() => {\n let scrollTop = 0;\n messagesRef.current = messagesRef.current.slice(0, messages.length);\n\n // Sums up scroll heights of all messages except the last one\n if (messagesRef?.current.length > 1) {\n scrollTop = messagesRef.current\n .slice(0, -1)\n .map((elem, _) => elem?.scrollHeight ?? 0)\n .reduce((total, height) => total + height);\n }\n\n // Scroll to the top of the last message\n messagesContainer.current?.scroll({\n top: scrollTop,\n behavior: \"smooth\",\n });\n }, [messages]);\n\n const setMessagesRef = useCallback((index) => {\n if (!messagesRef?.current) return null;\n return (message) => (messagesRef.current[index] = message);\n }, []);\n\n const footerCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: cssClasses.footer,\n link: \"cursor-pointer hover:underline text-blue-600\",\n }),\n [cssClasses]\n );\n\n return (\n <div className=\"yext-chat w-full h-full\">\n <div className={cssClasses.container}>\n {header}\n <div className={cssClasses.messagesScrollContainer}>\n <div ref={messagesContainer} className={cssClasses.messagesContainer}>\n {messages.map((message, index) => (\n <div key={index} ref={setMessagesRef(index)}>\n <MessageBubble\n {...props}\n customCssClasses={cssClasses.messageBubbleCssClasses}\n message={message}\n onLinkClick={onLinkClick}\n />\n </div>\n ))}\n {loading && <LoadingDots />}\n </div>\n </div>\n <div className={cssClasses.inputContainer}>\n {suggestions && (\n <MessageSuggestions\n suggestions={suggestions}\n customCssClasses={cssClasses.messageSuggestionClasses}\n />\n )}\n <ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />\n </div>\n {footer && (\n <Markdown\n content={footer}\n linkClickEvent=\"WEBSITE\"\n onLinkClick={onLinkClick}\n customCssClasses={footerCssClasses}\n />\n )}\n </div>\n </div>\n );\n}\n"],"names":["withStylelessCssClasses","useChatState","useComposedCssClasses","useReportAnalyticsEvent","useFetchInitialMessage","useEffect","useMemo","useRef","useCallback","React","MessageBubble","LoadingDots","MessageSuggestions","ChatInput","Markdown"],"mappings":"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"}
|
|
@@ -20,6 +20,8 @@ interface MarkdownProps {
|
|
|
20
20
|
* Defaults to 'CHAT_LINK_CLICK'.
|
|
21
21
|
*/
|
|
22
22
|
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
23
|
+
/** A callback which is called when a link is clicked. */
|
|
24
|
+
onLinkClick?: (href?: string) => void;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Renders Github-Flavored Markdown from the Knowledge Graph. This Markdown can include
|
|
@@ -30,6 +32,6 @@ interface MarkdownProps {
|
|
|
30
32
|
*
|
|
31
33
|
* @internal
|
|
32
34
|
*/
|
|
33
|
-
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, }: MarkdownProps): React.JSX.Element;
|
|
35
|
+
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, onLinkClick, }: MarkdownProps): React.JSX.Element;
|
|
34
36
|
export {};
|
|
35
37
|
//# 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;
|
|
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,qBAyCf"}
|
|
@@ -38,7 +38,7 @@ const builtInCssClasses = {
|
|
|
38
38
|
*
|
|
39
39
|
* @internal
|
|
40
40
|
*/
|
|
41
|
-
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", }) {
|
|
41
|
+
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", onLinkClick, }) {
|
|
42
42
|
const reportAnalyticsEvent = useReportAnalyticsEvent.useReportAnalyticsEvent();
|
|
43
43
|
const cssClasses = useComposedCssClasses.useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
44
44
|
const components = React.useMemo(() => {
|
|
@@ -50,13 +50,14 @@ function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHA
|
|
|
50
50
|
responseId,
|
|
51
51
|
},
|
|
52
52
|
});
|
|
53
|
+
onLinkClick?.(href);
|
|
53
54
|
};
|
|
54
55
|
return {
|
|
55
56
|
a: ({ node: _, children, ...props }) => {
|
|
56
57
|
return (React__default.default.createElement("a", { ...props, onClick: createClickHandlerFn(props.href), target: "_blank", rel: "noopener noreferrer", className: cssClasses.link }, children));
|
|
57
58
|
},
|
|
58
59
|
};
|
|
59
|
-
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses]);
|
|
60
|
+
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);
|
|
60
61
|
return (React__default.default.createElement(ReactMarkdown__default.default, { className: cssClasses.container, children: content, remarkPlugins: unifiedPlugins.remark, rehypePlugins: unifiedPlugins.rehype, components: components }));
|
|
61
62
|
}
|
|
62
63
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Markdown.js","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}\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}: 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 };\n return {\n a: ({ node: _, children, ...props }) => {\n return (\n <a\n {...props}\n onClick={createClickHandlerFn(props.href)}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses]);\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":["remarkGfm","rehypeRaw","rehypeSanitize","useReportAnalyticsEvent","useComposedCssClasses","useMemo","React","ReactMarkdown"],"mappings":";;;;;;;;;;;;;;;;;;AAWA;AACA,MAAM,cAAc,GAAsD;AACxE,IAAA,MAAM,EAAE;AACN,QAAAA,0BAAS;AACV,KAAA;AACD,IAAA,MAAM,EAAE;QACNC,0BAAS;AACT,QAAAC,+BAAc;AACf,KAAA;CACF,CAAC;AAYF,MAAM,iBAAiB,GAAuB;AAC5C,IAAA,IAAI,EAAE,gBAAgB;CACvB,CAAC;
|
|
1
|
+
{"version":3,"file":"Markdown.js","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=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);\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":["remarkGfm","rehypeRaw","rehypeSanitize","useReportAnalyticsEvent","useComposedCssClasses","useMemo","React","ReactMarkdown"],"mappings":";;;;;;;;;;;;;;;;;;AAWA;AACA,MAAM,cAAc,GAAsD;AACxE,IAAA,MAAM,EAAE;AACN,QAAAA,0BAAS;AACV,KAAA;AACD,IAAA,MAAM,EAAE;QACNC,0BAAS;AACT,QAAAC,+BAAc;AACf,KAAA;CACF,CAAC;AAYF,MAAM,iBAAiB,GAAuB;AAC5C,IAAA,IAAI,EAAE,gBAAgB;CACvB,CAAC;AAkBF;;;;;;;;AAQG;AACa,SAAA,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAc,GAAG,iBAAiB,EAClC,WAAW,GACG,EAAA;AACd,IAAA,MAAM,oBAAoB,GAAGC,+CAAuB,EAAE,CAAC;IACvD,MAAM,UAAU,GAAGC,2CAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE9E,IAAA,MAAM,UAAU,GAAuCC,aAAO,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,CAAA;AACrB,SAAC,CAAC;QACF,OAAO;AACL,YAAA,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,KAAI;AACrC,gBAAA,QACEC,sBAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAA,GACM,KAAK,EACT,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,EACzC,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAA,EAEzB,QAAQ,CACP,EACJ;aACH;SACF,CAAC;AACJ,KAAC,EAAE,CAAC,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,IAAA,QACEA,sBAAA,CAAA,aAAA,CAACC,8BAAa,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
|
+
/** A callback which is called when user clicks a link. */
|
|
52
|
+
onLinkClick?: (href?: string) => void;
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* A component that displays the provided message.
|
|
@@ -56,5 +58,5 @@ export interface MessageBubbleProps {
|
|
|
56
58
|
*
|
|
57
59
|
* @param props - {@link MessageBubbleProps}
|
|
58
60
|
*/
|
|
59
|
-
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, }: MessageBubbleProps): React.JSX.Element;
|
|
61
|
+
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, onLinkClick, }: MessageBubbleProps): React.JSX.Element;
|
|
60
62
|
//# 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;
|
|
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,qBA4DpB"}
|
|
@@ -35,7 +35,7 @@ const builtInCssClasses = withStylelessCssClasses.withStylelessCssClasses("Messa
|
|
|
35
35
|
*
|
|
36
36
|
* @param props - {@link MessageBubbleProps}
|
|
37
37
|
*/
|
|
38
|
-
function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = true, customCssClasses, formatTimestamp = defaultFormatTimestamp, }) {
|
|
38
|
+
function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = true, customCssClasses, formatTimestamp = defaultFormatTimestamp, onLinkClick, }) {
|
|
39
39
|
const cssClasses = useComposedCssClasses.useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
40
40
|
const bubbleCssClasses = tailwindMerge.twMerge(cssClasses.bubble, message.source === chatHeadlessReact.MessageSource.USER
|
|
41
41
|
? cssClasses.bubble__user
|
|
@@ -56,7 +56,7 @@ function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = tr
|
|
|
56
56
|
React__default.default.createElement("div", { className: subContainerCssClasses },
|
|
57
57
|
React__default.default.createElement("div", { className: bubbleCssClasses },
|
|
58
58
|
showFeedbackButtons && message.source === chatHeadlessReact.MessageSource.BOT && (React__default.default.createElement(FeedbackButtons.FeedbackButtons, { customCssClasses: cssClasses.feedbackButtonsCssClasses, responseId: message.responseId })),
|
|
59
|
-
React__default.default.createElement(Markdown.Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses })),
|
|
59
|
+
React__default.default.createElement(Markdown.Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses, onLinkClick: onLinkClick })),
|
|
60
60
|
showTimestamp && (React__default.default.createElement("div", { className: timestampCssClasses }, message.timestamp ? formatTimestamp(message.timestamp) : " ")))));
|
|
61
61
|
}
|
|
62
62
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.js","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 my-0.5 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}\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}: 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 />\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":["withStylelessCssClasses","useComposedCssClasses","twMerge","MessageSource","useMemo","React","FeedbackButtons","Markdown"],"mappings":";;;;;;;;;;;;;;AA8BA,MAAM,iBAAiB,GACrBA,+CAAuB,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,8IAA8I;AAChJ,IAAA,cAAc,EAAE,EAAE;AAClB,IAAA,eAAe,EAAE,SAAS;AAC1B,IAAA,yBAAyB,EAAE,EAAE;AAC9B,CAAA,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"MessageBubble.js","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 my-0.5 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 /** 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 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 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":["withStylelessCssClasses","useComposedCssClasses","twMerge","MessageSource","useMemo","React","FeedbackButtons","Markdown"],"mappings":"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"}
|
package/lib/esm/index.d.ts
CHANGED
|
@@ -159,6 +159,8 @@ export declare interface ChatPanelProps extends Omit<MessageBubbleProps, "custom
|
|
|
159
159
|
* can click on instead of typing their own.
|
|
160
160
|
*/
|
|
161
161
|
messageSuggestions?: string[];
|
|
162
|
+
/** A callback which is called when user clicks a link. */
|
|
163
|
+
onLinkClick?: (href?: string) => void;
|
|
162
164
|
}
|
|
163
165
|
|
|
164
166
|
/**
|
|
@@ -263,7 +265,7 @@ export declare interface InitialMessagePopUpCssClasses {
|
|
|
263
265
|
*
|
|
264
266
|
* @param props - {@link MessageBubbleProps}
|
|
265
267
|
*/
|
|
266
|
-
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, }: MessageBubbleProps): React_2.JSX.Element;
|
|
268
|
+
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, onLinkClick, }: MessageBubbleProps): React_2.JSX.Element;
|
|
267
269
|
|
|
268
270
|
/**
|
|
269
271
|
* The CSS class interface for the {@link MessageBubble} component.
|
|
@@ -313,6 +315,8 @@ export declare interface MessageBubbleProps {
|
|
|
313
315
|
formatTimestamp?: (timestamp: string) => string;
|
|
314
316
|
/** CSS classes for customizing the component styling. */
|
|
315
317
|
customCssClasses?: MessageBubbleCssClasses;
|
|
318
|
+
/** A callback which is called when user clicks a link. */
|
|
319
|
+
onLinkClick?: (href?: string) => void;
|
|
316
320
|
}
|
|
317
321
|
|
|
318
322
|
/**
|
package/lib/esm/package.json.mjs
CHANGED
|
@@ -36,6 +36,8 @@ export interface ChatPanelProps extends Omit<MessageBubbleProps, "customCssClass
|
|
|
36
36
|
* can click on instead of typing their own.
|
|
37
37
|
*/
|
|
38
38
|
messageSuggestions?: string[];
|
|
39
|
+
/** A callback which is called when user clicks a link. */
|
|
40
|
+
onLinkClick?: (href?: string) => void;
|
|
39
41
|
}
|
|
40
42
|
/**
|
|
41
43
|
* A component that renders a full panel for chat bot interactions. This includes
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EACL,2BAA2B,EAE5B,MAAM,sBAAsB,CAAC;AAG9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;GAIG;AACH,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,CAAC,EAC9D,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC1C,kDAAkD;IAClD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"ChatPanel.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatPanel.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EACZ,SAAS,EAKV,MAAM,OAAO,CAAC;AAEf,OAAO,EAEL,uBAAuB,EACvB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAa,mBAAmB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7E,OAAO,EACL,2BAA2B,EAE5B,MAAM,sBAAsB,CAAC;AAG9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,mBAAmB,CAAC;IACtC,uBAAuB,CAAC,EAAE,uBAAuB,CAAC;IAClD,wBAAwB,CAAC,EAAE,2BAA2B,CAAC;IACvD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAgBD;;;;GAIG;AACH,MAAM,WAAW,cACf,SAAQ,IAAI,CAAC,kBAAkB,EAAE,kBAAkB,GAAG,SAAS,CAAC,EAC9D,IAAI,CAAC,cAAc,EAAE,kBAAkB,CAAC;IAC1C,kDAAkD;IAClD,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,0DAA0D;IAC1D,WAAW,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,qBA8G9C"}
|
|
@@ -30,7 +30,7 @@ const builtInCssClasses = withStylelessCssClasses("Panel", {
|
|
|
30
30
|
* @param props - {@link ChatPanelProps}
|
|
31
31
|
*/
|
|
32
32
|
function ChatPanel(props) {
|
|
33
|
-
const { header, footer, customCssClasses, stream, handleError, messageSuggestions, } = props;
|
|
33
|
+
const { header, footer, customCssClasses, stream, handleError, messageSuggestions, onLinkClick, } = props;
|
|
34
34
|
const messages = useChatState((state) => state.conversation.messages);
|
|
35
35
|
const loading = useChatState((state) => state.conversation.isLoading);
|
|
36
36
|
const suggestedReplies = useChatState((state) => state.conversation.notes?.suggestedReplies);
|
|
@@ -83,12 +83,12 @@ function ChatPanel(props) {
|
|
|
83
83
|
React.createElement("div", { className: cssClasses.messagesScrollContainer },
|
|
84
84
|
React.createElement("div", { ref: messagesContainer, className: cssClasses.messagesContainer },
|
|
85
85
|
messages.map((message, index) => (React.createElement("div", { key: index, ref: setMessagesRef(index) },
|
|
86
|
-
React.createElement(MessageBubble, { ...props, customCssClasses: cssClasses.messageBubbleCssClasses, message: message })))),
|
|
86
|
+
React.createElement(MessageBubble, { ...props, customCssClasses: cssClasses.messageBubbleCssClasses, message: message, onLinkClick: onLinkClick })))),
|
|
87
87
|
loading && React.createElement(LoadingDots, null))),
|
|
88
88
|
React.createElement("div", { className: cssClasses.inputContainer },
|
|
89
89
|
suggestions && (React.createElement(MessageSuggestions, { suggestions: suggestions, customCssClasses: cssClasses.messageSuggestionClasses })),
|
|
90
90
|
React.createElement(ChatInput, { ...props, customCssClasses: cssClasses.inputCssClasses })),
|
|
91
|
-
footer && (React.createElement(Markdown, { content: footer, linkClickEvent: "WEBSITE", customCssClasses: footerCssClasses })))));
|
|
91
|
+
footer && (React.createElement(Markdown, { content: footer, linkClickEvent: "WEBSITE", onLinkClick: onLinkClick, customCssClasses: footerCssClasses })))));
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
export { ChatPanel };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatPanel.mjs","sources":["../../../../src/components/ChatPanel.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { useChatState } from \"@yext/chat-headless-react\";\nimport {\n MessageBubble,\n MessageBubbleCssClasses,\n MessageBubbleProps,\n} from \"./MessageBubble\";\nimport { ChatInput, ChatInputCssClasses, ChatInputProps } from \"./ChatInput\";\nimport { LoadingDots } from \"./LoadingDots\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useFetchInitialMessage } from \"../hooks/useFetchInitialMessage\";\nimport {\n MessageSuggestionCssClasses,\n MessageSuggestions,\n} from \"./MessageSuggestions\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\n\n/**\n * The CSS class interface for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelCssClasses {\n container?: string;\n messagesContainer?: string;\n messagesScrollContainer?: string;\n inputContainer?: string;\n inputCssClasses?: ChatInputCssClasses;\n messageBubbleCssClasses?: MessageBubbleCssClasses;\n messageSuggestionClasses?: MessageSuggestionCssClasses;\n footer?: string;\n}\n\nconst builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(\n \"Panel\",\n {\n container: \"h-full w-full flex flex-col relative shadow-2xl bg-white\",\n messagesScrollContainer: \"flex flex-col mt-auto overflow-hidden\",\n messagesContainer: \"flex flex-col gap-y-1 px-4 overflow-auto\",\n inputContainer: \"w-full p-4\",\n messageBubbleCssClasses: {\n topContainer: \"first:mt-4\",\n },\n footer: \"text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]\",\n }\n);\n\n/**\n * The props for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelProps\n extends Omit<MessageBubbleProps, \"customCssClasses\" | \"message\">,\n Omit<ChatInputProps, \"customCssClasses\"> {\n /** A header to render at the top of the panel. */\n header?: ReactNode;\n /** A footer markdown string to render at the bottom of the panel. */\n footer?: string;\n /**\n * CSS classes for customizing the component styling.\n */\n customCssClasses?: ChatPanelCssClasses;\n /**\n * A set of pre-written initial messages that the user\n * can click on instead of typing their own.\n */\n messageSuggestions?: string[];\n}\n\n/**\n * A component that renders a full panel for chat bot interactions. This includes\n * the message bubbles for the conversation, input box with send button, and header\n * (if provided).\n *\n * @public\n *\n * @param props - {@link ChatPanelProps}\n */\nexport function ChatPanel(props: ChatPanelProps) {\n const {\n header,\n footer,\n customCssClasses,\n stream,\n handleError,\n messageSuggestions,\n } = props;\n const messages = useChatState((state) => state.conversation.messages);\n const loading = useChatState((state) => state.conversation.isLoading);\n const suggestedReplies = useChatState(\n (state) => state.conversation.notes?.suggestedReplies\n );\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n useFetchInitialMessage(handleError, stream);\n\n useEffect(() => {\n reportAnalyticsEvent({\n action: \"CHAT_IMPRESSION\",\n });\n }, [reportAnalyticsEvent]);\n\n const suggestions = useMemo(() => {\n if (\n messages.length === 0 ||\n (messages.length === 1 && messages[0].source === \"BOT\")\n ) {\n return messageSuggestions;\n }\n return suggestedReplies;\n }, [messages, suggestedReplies, messageSuggestions]);\n\n const messagesRef = useRef<Array<HTMLDivElement | null>>([]);\n const messagesContainer = useRef<HTMLDivElement>(null);\n\n // Handle scrolling when messages change\n useEffect(() => {\n let scrollTop = 0;\n messagesRef.current = messagesRef.current.slice(0, messages.length);\n\n // Sums up scroll heights of all messages except the last one\n if (messagesRef?.current.length > 1) {\n scrollTop = messagesRef.current\n .slice(0, -1)\n .map((elem, _) => elem?.scrollHeight ?? 0)\n .reduce((total, height) => total + height);\n }\n\n // Scroll to the top of the last message\n messagesContainer.current?.scroll({\n top: scrollTop,\n behavior: \"smooth\",\n });\n }, [messages]);\n\n const setMessagesRef = useCallback((index) => {\n if (!messagesRef?.current) return null;\n return (message) => (messagesRef.current[index] = message);\n }, []);\n\n const footerCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: cssClasses.footer,\n link: \"cursor-pointer hover:underline text-blue-600\",\n }),\n [cssClasses]\n );\n\n return (\n <div className=\"yext-chat w-full h-full\">\n <div className={cssClasses.container}>\n {header}\n <div className={cssClasses.messagesScrollContainer}>\n <div ref={messagesContainer} className={cssClasses.messagesContainer}>\n {messages.map((message, index) => (\n <div key={index} ref={setMessagesRef(index)}>\n <MessageBubble\n {...props}\n customCssClasses={cssClasses.messageBubbleCssClasses}\n message={message}\n />\n </div>\n ))}\n {loading && <LoadingDots />}\n </div>\n </div>\n <div className={cssClasses.inputContainer}>\n {suggestions && (\n <MessageSuggestions\n suggestions={suggestions}\n customCssClasses={cssClasses.messageSuggestionClasses}\n />\n )}\n <ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />\n </div>\n {footer && (\n <Markdown\n content={footer}\n linkClickEvent=\"WEBSITE\"\n customCssClasses={footerCssClasses}\n />\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ChatPanel.mjs","sources":["../../../../src/components/ChatPanel.tsx"],"sourcesContent":["import React, {\n ReactNode,\n useCallback,\n useEffect,\n useMemo,\n useRef,\n} from \"react\";\nimport { useChatState } from \"@yext/chat-headless-react\";\nimport {\n MessageBubble,\n MessageBubbleCssClasses,\n MessageBubbleProps,\n} from \"./MessageBubble\";\nimport { ChatInput, ChatInputCssClasses, ChatInputProps } from \"./ChatInput\";\nimport { LoadingDots } from \"./LoadingDots\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useFetchInitialMessage } from \"../hooks/useFetchInitialMessage\";\nimport {\n MessageSuggestionCssClasses,\n MessageSuggestions,\n} from \"./MessageSuggestions\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\n\n/**\n * The CSS class interface for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelCssClasses {\n container?: string;\n messagesContainer?: string;\n messagesScrollContainer?: string;\n inputContainer?: string;\n inputCssClasses?: ChatInputCssClasses;\n messageBubbleCssClasses?: MessageBubbleCssClasses;\n messageSuggestionClasses?: MessageSuggestionCssClasses;\n footer?: string;\n}\n\nconst builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(\n \"Panel\",\n {\n container: \"h-full w-full flex flex-col relative shadow-2xl bg-white\",\n messagesScrollContainer: \"flex flex-col mt-auto overflow-hidden\",\n messagesContainer: \"flex flex-col gap-y-1 px-4 overflow-auto\",\n inputContainer: \"w-full p-4\",\n messageBubbleCssClasses: {\n topContainer: \"first:mt-4\",\n },\n footer: \"text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]\",\n }\n);\n\n/**\n * The props for the {@link ChatPanel} component.\n *\n * @public\n */\nexport interface ChatPanelProps\n extends Omit<MessageBubbleProps, \"customCssClasses\" | \"message\">,\n Omit<ChatInputProps, \"customCssClasses\"> {\n /** A header to render at the top of the panel. */\n header?: ReactNode;\n /** A footer markdown string to render at the bottom of the panel. */\n footer?: string;\n /**\n * CSS classes for customizing the component styling.\n */\n customCssClasses?: ChatPanelCssClasses;\n /**\n * A set of pre-written initial messages that the user\n * can click on instead of typing their own.\n */\n messageSuggestions?: string[];\n /** A callback which is called when user clicks a link. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * A component that renders a full panel for chat bot interactions. This includes\n * the message bubbles for the conversation, input box with send button, and header\n * (if provided).\n *\n * @public\n *\n * @param props - {@link ChatPanelProps}\n */\nexport function ChatPanel(props: ChatPanelProps) {\n const {\n header,\n footer,\n customCssClasses,\n stream,\n handleError,\n messageSuggestions,\n onLinkClick,\n } = props;\n const messages = useChatState((state) => state.conversation.messages);\n const loading = useChatState((state) => state.conversation.isLoading);\n const suggestedReplies = useChatState(\n (state) => state.conversation.notes?.suggestedReplies\n );\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n useFetchInitialMessage(handleError, stream);\n\n useEffect(() => {\n reportAnalyticsEvent({\n action: \"CHAT_IMPRESSION\",\n });\n }, [reportAnalyticsEvent]);\n\n const suggestions = useMemo(() => {\n if (\n messages.length === 0 ||\n (messages.length === 1 && messages[0].source === \"BOT\")\n ) {\n return messageSuggestions;\n }\n return suggestedReplies;\n }, [messages, suggestedReplies, messageSuggestions]);\n\n const messagesRef = useRef<Array<HTMLDivElement | null>>([]);\n const messagesContainer = useRef<HTMLDivElement>(null);\n\n // Handle scrolling when messages change\n useEffect(() => {\n let scrollTop = 0;\n messagesRef.current = messagesRef.current.slice(0, messages.length);\n\n // Sums up scroll heights of all messages except the last one\n if (messagesRef?.current.length > 1) {\n scrollTop = messagesRef.current\n .slice(0, -1)\n .map((elem, _) => elem?.scrollHeight ?? 0)\n .reduce((total, height) => total + height);\n }\n\n // Scroll to the top of the last message\n messagesContainer.current?.scroll({\n top: scrollTop,\n behavior: \"smooth\",\n });\n }, [messages]);\n\n const setMessagesRef = useCallback((index) => {\n if (!messagesRef?.current) return null;\n return (message) => (messagesRef.current[index] = message);\n }, []);\n\n const footerCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: cssClasses.footer,\n link: \"cursor-pointer hover:underline text-blue-600\",\n }),\n [cssClasses]\n );\n\n return (\n <div className=\"yext-chat w-full h-full\">\n <div className={cssClasses.container}>\n {header}\n <div className={cssClasses.messagesScrollContainer}>\n <div ref={messagesContainer} className={cssClasses.messagesContainer}>\n {messages.map((message, index) => (\n <div key={index} ref={setMessagesRef(index)}>\n <MessageBubble\n {...props}\n customCssClasses={cssClasses.messageBubbleCssClasses}\n message={message}\n onLinkClick={onLinkClick}\n />\n </div>\n ))}\n {loading && <LoadingDots />}\n </div>\n </div>\n <div className={cssClasses.inputContainer}>\n {suggestions && (\n <MessageSuggestions\n suggestions={suggestions}\n customCssClasses={cssClasses.messageSuggestionClasses}\n />\n )}\n <ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />\n </div>\n {footer && (\n <Markdown\n content={footer}\n linkClickEvent=\"WEBSITE\"\n onLinkClick={onLinkClick}\n customCssClasses={footerCssClasses}\n />\n )}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,8 @@ interface MarkdownProps {
|
|
|
20
20
|
* Defaults to 'CHAT_LINK_CLICK'.
|
|
21
21
|
*/
|
|
22
22
|
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
23
|
+
/** A callback which is called when a link is clicked. */
|
|
24
|
+
onLinkClick?: (href?: string) => void;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* Renders Github-Flavored Markdown from the Knowledge Graph. This Markdown can include
|
|
@@ -30,6 +32,6 @@ interface MarkdownProps {
|
|
|
30
32
|
*
|
|
31
33
|
* @internal
|
|
32
34
|
*/
|
|
33
|
-
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, }: MarkdownProps): React.JSX.Element;
|
|
35
|
+
export declare function Markdown({ content, responseId, customCssClasses, linkClickEvent, onLinkClick, }: MarkdownProps): React.JSX.Element;
|
|
34
36
|
export {};
|
|
35
37
|
//# 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;
|
|
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,qBAyCf"}
|
|
@@ -28,7 +28,7 @@ const builtInCssClasses = {
|
|
|
28
28
|
*
|
|
29
29
|
* @internal
|
|
30
30
|
*/
|
|
31
|
-
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", }) {
|
|
31
|
+
function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHAT_LINK_CLICK", onLinkClick, }) {
|
|
32
32
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
33
33
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
34
34
|
const components = useMemo(() => {
|
|
@@ -40,13 +40,14 @@ function Markdown({ content, responseId, customCssClasses, linkClickEvent = "CHA
|
|
|
40
40
|
responseId,
|
|
41
41
|
},
|
|
42
42
|
});
|
|
43
|
+
onLinkClick?.(href);
|
|
43
44
|
};
|
|
44
45
|
return {
|
|
45
46
|
a: ({ node: _, children, ...props }) => {
|
|
46
47
|
return (React.createElement("a", { ...props, onClick: createClickHandlerFn(props.href), target: "_blank", rel: "noopener noreferrer", className: cssClasses.link }, children));
|
|
47
48
|
},
|
|
48
49
|
};
|
|
49
|
-
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses]);
|
|
50
|
+
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);
|
|
50
51
|
return (React.createElement(ReactMarkdown, { className: cssClasses.container, children: content, remarkPlugins: unifiedPlugins.remark, rehypePlugins: unifiedPlugins.rehype, components: components }));
|
|
51
52
|
}
|
|
52
53
|
|
|
@@ -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}\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}: 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 };\n return {\n a: ({ node: _, children, ...props }) => {\n return (\n <a\n {...props}\n onClick={createClickHandlerFn(props.href)}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses]);\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;
|
|
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=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);\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;AAkBF;;;;;;;;AAQG;AACa,SAAA,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAc,GAAG,iBAAiB,EAClC,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,CAAA;AACrB,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,EAAC,QAAQ,EACf,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAA,EAEzB,QAAQ,CACP,EACJ;aACH;SACF,CAAC;AACJ,KAAC,EAAE,CAAC,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,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
|
+
/** A callback which is called when user clicks a link. */
|
|
52
|
+
onLinkClick?: (href?: string) => void;
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* A component that displays the provided message.
|
|
@@ -56,5 +58,5 @@ export interface MessageBubbleProps {
|
|
|
56
58
|
*
|
|
57
59
|
* @param props - {@link MessageBubbleProps}
|
|
58
60
|
*/
|
|
59
|
-
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, }: MessageBubbleProps): React.JSX.Element;
|
|
61
|
+
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, onLinkClick, }: MessageBubbleProps): React.JSX.Element;
|
|
60
62
|
//# 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;
|
|
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,qBA4DpB"}
|
|
@@ -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, }) {
|
|
32
|
+
function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = true, customCssClasses, formatTimestamp = defaultFormatTimestamp, 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 })),
|
|
53
|
+
React.createElement(Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses, 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 my-0.5 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}\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}: 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 />\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,8IAA8I;AAChJ,IAAA,cAAc,EAAE,EAAE;AAClB,IAAA,eAAe,EAAE,SAAS;AAC1B,IAAA,yBAAyB,EAAE,EAAE;AAC9B,CAAA,CAAC,CAAC;
|
|
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 my-0.5 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 /** 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 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 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":"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"}
|
package/package.json
CHANGED
|
@@ -74,6 +74,8 @@ export interface ChatPanelProps
|
|
|
74
74
|
* can click on instead of typing their own.
|
|
75
75
|
*/
|
|
76
76
|
messageSuggestions?: string[];
|
|
77
|
+
/** A callback which is called when user clicks a link. */
|
|
78
|
+
onLinkClick?: (href?: string) => void;
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
/**
|
|
@@ -93,6 +95,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
93
95
|
stream,
|
|
94
96
|
handleError,
|
|
95
97
|
messageSuggestions,
|
|
98
|
+
onLinkClick,
|
|
96
99
|
} = props;
|
|
97
100
|
const messages = useChatState((state) => state.conversation.messages);
|
|
98
101
|
const loading = useChatState((state) => state.conversation.isLoading);
|
|
@@ -167,6 +170,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
167
170
|
{...props}
|
|
168
171
|
customCssClasses={cssClasses.messageBubbleCssClasses}
|
|
169
172
|
message={message}
|
|
173
|
+
onLinkClick={onLinkClick}
|
|
170
174
|
/>
|
|
171
175
|
</div>
|
|
172
176
|
))}
|
|
@@ -186,6 +190,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
186
190
|
<Markdown
|
|
187
191
|
content={footer}
|
|
188
192
|
linkClickEvent="WEBSITE"
|
|
193
|
+
onLinkClick={onLinkClick}
|
|
189
194
|
customCssClasses={footerCssClasses}
|
|
190
195
|
/>
|
|
191
196
|
)}
|
|
@@ -46,6 +46,8 @@ interface MarkdownProps {
|
|
|
46
46
|
* Defaults to 'CHAT_LINK_CLICK'.
|
|
47
47
|
*/
|
|
48
48
|
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
49
|
+
/** A callback which is called when a link is clicked. */
|
|
50
|
+
onLinkClick?: (href?: string) => void;
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -62,6 +64,7 @@ export function Markdown({
|
|
|
62
64
|
responseId,
|
|
63
65
|
customCssClasses,
|
|
64
66
|
linkClickEvent = "CHAT_LINK_CLICK",
|
|
67
|
+
onLinkClick,
|
|
65
68
|
}: MarkdownProps) {
|
|
66
69
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
67
70
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
@@ -75,6 +78,7 @@ export function Markdown({
|
|
|
75
78
|
responseId,
|
|
76
79
|
},
|
|
77
80
|
});
|
|
81
|
+
onLinkClick?.(href)
|
|
78
82
|
};
|
|
79
83
|
return {
|
|
80
84
|
a: ({ node: _, children, ...props }) => {
|
|
@@ -91,7 +95,7 @@ export function Markdown({
|
|
|
91
95
|
);
|
|
92
96
|
},
|
|
93
97
|
};
|
|
94
|
-
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses]);
|
|
98
|
+
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);
|
|
95
99
|
|
|
96
100
|
return (
|
|
97
101
|
<ReactMarkdown
|
|
@@ -75,6 +75,8 @@ export interface MessageBubbleProps {
|
|
|
75
75
|
formatTimestamp?: (timestamp: string) => string;
|
|
76
76
|
/** CSS classes for customizing the component styling. */
|
|
77
77
|
customCssClasses?: MessageBubbleCssClasses;
|
|
78
|
+
/** A callback which is called when user clicks a link. */
|
|
79
|
+
onLinkClick?: (href?: string) => void;
|
|
78
80
|
}
|
|
79
81
|
|
|
80
82
|
/**
|
|
@@ -90,6 +92,7 @@ export function MessageBubble({
|
|
|
90
92
|
showTimestamp = true,
|
|
91
93
|
customCssClasses,
|
|
92
94
|
formatTimestamp = defaultFormatTimestamp,
|
|
95
|
+
onLinkClick,
|
|
93
96
|
}: MessageBubbleProps) {
|
|
94
97
|
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
95
98
|
const bubbleCssClasses = twMerge(
|
|
@@ -138,6 +141,7 @@ export function MessageBubble({
|
|
|
138
141
|
content={message.text}
|
|
139
142
|
responseId={message.responseId}
|
|
140
143
|
customCssClasses={markdownCssClasses}
|
|
144
|
+
onLinkClick={onLinkClick}
|
|
141
145
|
/>
|
|
142
146
|
</div>
|
|
143
147
|
{/* fallback on empty space here to perserve the height for timestamp div */}
|