@yext/chat-ui-react 0.8.6 → 0.8.8
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/ChatInput.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatInput.js +4 -2
- package/lib/commonjs/src/components/ChatInput.js.map +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 +4 -4
- 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 +9 -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/commonjs/src/components/MessageSuggestions.d.ts +4 -0
- package/lib/commonjs/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/commonjs/src/components/MessageSuggestions.js +3 -3
- package/lib/commonjs/src/components/MessageSuggestions.js.map +1 -1
- package/lib/esm/index.d.ts +9 -1
- package/lib/esm/package.json.mjs +1 -1
- package/lib/esm/src/components/ChatInput.d.ts.map +1 -1
- package/lib/esm/src/components/ChatInput.mjs +4 -2
- package/lib/esm/src/components/ChatInput.mjs.map +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 +4 -4
- 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 +9 -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/lib/esm/src/components/MessageSuggestions.d.ts +4 -0
- package/lib/esm/src/components/MessageSuggestions.d.ts.map +1 -1
- package/lib/esm/src/components/MessageSuggestions.mjs +3 -3
- package/lib/esm/src/components/MessageSuggestions.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ChatInput.tsx +5 -3
- package/src/components/ChatPanel.tsx +6 -0
- package/src/components/Markdown.tsx +11 -1
- package/src/components/MessageBubble.tsx +4 -0
- package/src/components/MessageSuggestions.tsx +7 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAQrD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,uCAAuC;IACvC,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAiC,EACjC,MAAc,EACd,cAAsB,EACtB,WAAW,EACX,cAA8B,EAC9B,gBAAgB,EAChB,MAAM,GACP,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAQrD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,uCAAuC;IACvC,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAiC,EACjC,MAAc,EACd,cAAsB,EACtB,WAAW,EACX,cAA8B,EAC9B,gBAAgB,EAChB,MAAM,GACP,EAAE,cAAc,qBA6DhB"}
|
|
@@ -40,9 +40,11 @@ function ChatInput({ placeholder = "Type a message...", stream = false, inputAut
|
|
|
40
40
|
? chat.streamNextMessage(input)
|
|
41
41
|
: chat.getNextMessage(input);
|
|
42
42
|
setInput("");
|
|
43
|
-
res
|
|
43
|
+
res
|
|
44
|
+
.then(() => {
|
|
44
45
|
onSend?.(input);
|
|
45
|
-
})
|
|
46
|
+
})
|
|
47
|
+
.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
|
|
46
48
|
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);
|
|
47
49
|
const handleKeyDown = React.useCallback((e) => {
|
|
48
50
|
if (!e.shiftKey && e.key === "Enter") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.js","sources":["../../../../src/components/ChatInput.tsx"],"sourcesContent":["import React, { useCallback, useState } from \"react\";\nimport { useChatActions, useChatState } from \"@yext/chat-headless-react\";\nimport { ArrowIcon } from \"../icons/Arrow\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport TextareaAutosize from \"react-textarea-autosize\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\n\n/**\n * The CSS class interface for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputCssClasses {\n container?: string;\n textArea?: string;\n sendButton?: string;\n}\n\nconst builtInCssClasses: ChatInputCssClasses = withStylelessCssClasses(\n \"Input\",\n {\n container: \"w-full h-fit flex flex-row relative @container\",\n textArea:\n \"w-full p-4 pr-12 border border-slate-300 rounded-3xl resize-none text-[13px] @[480px]:text-base placeholder:text-[13px] placeholder:@[480px]:text-base text-slate-900\",\n sendButton:\n \"rounded-full p-1.5 w-8 h-8 stroke-2 text-white bg-blue-600 disabled:bg-slate-200 hover:bg-blue-800 active:scale-90 transition-all absolute right-4 bottom-2.5 @[480px]:bottom-3.5\",\n }\n);\n\n/**\n * The props for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputProps {\n /**\n * The input's placeholder text when no text has been entered by the user.\n * Defaults to \"Type a message...\".\n */\n placeholder?: string;\n /**\n * Enable streaming behavior by making a request to Chat Streaming API.\n * This feature is experimental, and is subject to change.\n * Defaults to false.\n */\n stream?: boolean;\n /** Enable auto focus for the input box. Defaults to false. */\n inputAutoFocus?: boolean;\n /**\n * A function which is called when an error occurs from Chat API while processing the user's message.\n * By default, the error is logged to the console and an error message is added to state.\n */\n handleError?: (e: unknown) => void;\n /** Custom icon for the send button. */\n sendButtonIcon?: JSX.Element;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: ChatInputCssClasses;\n /** A callback which is called when user sends a message. */\n onSend?: (message: string) => void;\n}\n\n/**\n * A component that allows user to input message and send to Chat API.\n *\n * @remarks\n * Pressing \"Enter\" key will send the current message.\n * To add a newline, press \"Shift\" and \"Enter\".\n *\n * @public\n *\n * @param props - {@link ChatInputProps}\n */\nexport function ChatInput({\n placeholder = \"Type a message...\",\n stream = false,\n inputAutoFocus = false,\n handleError,\n sendButtonIcon = <ArrowIcon />,\n customCssClasses,\n onSend,\n}: ChatInputProps) {\n const chat = useChatActions();\n const [input, setInput] = useState(\"\");\n const canSendMessage = useChatState(\n (state) => state.conversation.canSendMessage\n );\n const defaultHandleApiError = useDefaultHandleApiError();\n\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const sendMessage = useCallback(async () => {\n const res = stream\n ? chat.streamNextMessage(input)\n : chat.getNextMessage(input);\n setInput(\"\");\n res.then(() => {\n
|
|
1
|
+
{"version":3,"file":"ChatInput.js","sources":["../../../../src/components/ChatInput.tsx"],"sourcesContent":["import React, { useCallback, useState } from \"react\";\nimport { useChatActions, useChatState } from \"@yext/chat-headless-react\";\nimport { ArrowIcon } from \"../icons/Arrow\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport TextareaAutosize from \"react-textarea-autosize\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\n\n/**\n * The CSS class interface for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputCssClasses {\n container?: string;\n textArea?: string;\n sendButton?: string;\n}\n\nconst builtInCssClasses: ChatInputCssClasses = withStylelessCssClasses(\n \"Input\",\n {\n container: \"w-full h-fit flex flex-row relative @container\",\n textArea:\n \"w-full p-4 pr-12 border border-slate-300 rounded-3xl resize-none text-[13px] @[480px]:text-base placeholder:text-[13px] placeholder:@[480px]:text-base text-slate-900\",\n sendButton:\n \"rounded-full p-1.5 w-8 h-8 stroke-2 text-white bg-blue-600 disabled:bg-slate-200 hover:bg-blue-800 active:scale-90 transition-all absolute right-4 bottom-2.5 @[480px]:bottom-3.5\",\n }\n);\n\n/**\n * The props for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputProps {\n /**\n * The input's placeholder text when no text has been entered by the user.\n * Defaults to \"Type a message...\".\n */\n placeholder?: string;\n /**\n * Enable streaming behavior by making a request to Chat Streaming API.\n * This feature is experimental, and is subject to change.\n * Defaults to false.\n */\n stream?: boolean;\n /** Enable auto focus for the input box. Defaults to false. */\n inputAutoFocus?: boolean;\n /**\n * A function which is called when an error occurs from Chat API while processing the user's message.\n * By default, the error is logged to the console and an error message is added to state.\n */\n handleError?: (e: unknown) => void;\n /** Custom icon for the send button. */\n sendButtonIcon?: JSX.Element;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: ChatInputCssClasses;\n /** A callback which is called when user sends a message. */\n onSend?: (message: string) => void;\n}\n\n/**\n * A component that allows user to input message and send to Chat API.\n *\n * @remarks\n * Pressing \"Enter\" key will send the current message.\n * To add a newline, press \"Shift\" and \"Enter\".\n *\n * @public\n *\n * @param props - {@link ChatInputProps}\n */\nexport function ChatInput({\n placeholder = \"Type a message...\",\n stream = false,\n inputAutoFocus = false,\n handleError,\n sendButtonIcon = <ArrowIcon />,\n customCssClasses,\n onSend,\n}: ChatInputProps) {\n const chat = useChatActions();\n const [input, setInput] = useState(\"\");\n const canSendMessage = useChatState(\n (state) => state.conversation.canSendMessage\n );\n const defaultHandleApiError = useDefaultHandleApiError();\n\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const sendMessage = useCallback(async () => {\n const res = stream\n ? chat.streamNextMessage(input)\n : chat.getNextMessage(input);\n setInput(\"\");\n res\n .then(() => {\n onSend?.(input);\n })\n .catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));\n }, [chat, input, handleError, defaultHandleApiError, stream, onSend]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (!e.shiftKey && e.key === \"Enter\") {\n e.preventDefault();\n if (canSendMessage && input.trim().length !== 0) {\n sendMessage();\n }\n }\n },\n [sendMessage, canSendMessage, input]\n );\n\n const onInputChange = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n },\n []\n );\n\n return (\n <div className={cssClasses.container}>\n <TextareaAutosize\n autoFocus={inputAutoFocus}\n onKeyDown={handleKeyDown}\n value={input}\n onChange={onInputChange}\n className={cssClasses.textArea}\n placeholder={placeholder}\n />\n <button\n aria-label=\"Send Message\"\n disabled={!canSendMessage || input.trim().length === 0}\n onClick={sendMessage}\n className={cssClasses.sendButton}\n >\n {sendButtonIcon}\n </button>\n </div>\n );\n}\n"],"names":["withStylelessCssClasses","React","ArrowIcon","useChatActions","useState","useChatState","useDefaultHandleApiError","useComposedCssClasses","useCallback","TextareaAutosize"],"mappings":"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"}
|
|
@@ -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,qBA+G9C"}
|
|
@@ -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
|
-
suggestions && (React__default.default.createElement(MessageSuggestions.MessageSuggestions, { suggestions: suggestions, customCssClasses: cssClasses.messageSuggestionClasses })),
|
|
95
|
+
suggestions && (React__default.default.createElement(MessageSuggestions.MessageSuggestions, { handleError: handleError, 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 handleError={handleError}\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":";;;;;;;;;;;;;;;;;;AAyCA,MAAM,iBAAiB,GAAwBA,+CAAuB,CACpE,OAAO,EACP;AACE,IAAA,SAAS,EAAE,0DAA0D;AACrE,IAAA,uBAAuB,EAAE,uCAAuC;AAChE,IAAA,iBAAiB,EAAE,0CAA0C;AAC7D,IAAA,cAAc,EAAE,YAAY;AAC5B,IAAA,uBAAuB,EAAE;AACvB,QAAA,YAAY,EAAE,YAAY;AAC3B,KAAA;AACD,IAAA,MAAM,EAAE,gEAAgE;AACzE,CAAA,CACF,CAAC;AA2BF;;;;;;;;AAQG;AACG,SAAU,SAAS,CAAC,KAAqB,EAAA;AAC7C,IAAA,MAAM,EACJ,MAAM,EACN,MAAM,EACN,gBAAgB,EAChB,MAAM,EACN,WAAW,EACX,kBAAkB,EAClB,WAAW,GACZ,GAAG,KAAK,CAAC;AACV,IAAA,MAAM,QAAQ,GAAGC,8BAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;AACtE,IAAA,MAAM,OAAO,GAAGA,8BAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,CAAC;AACtE,IAAA,MAAM,gBAAgB,GAAGA,8BAAY,CACnC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,EAAE,gBAAgB,CACtD,CAAC;IACF,MAAM,UAAU,GAAGC,2CAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAC9E,IAAA,MAAM,oBAAoB,GAAGC,+CAAuB,EAAE,CAAC;AACvD,IAAAC,6CAAsB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAE5CC,eAAS,CAAC,MAAK;AACb,QAAA,oBAAoB,CAAC;AACnB,YAAA,MAAM,EAAE,iBAAiB;AAC1B,SAAA,CAAC,CAAC;AACL,KAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,CAAC;AAE3B,IAAA,MAAM,WAAW,GAAGC,aAAO,CAAC,MAAK;AAC/B,QAAA,IACE,QAAQ,CAAC,MAAM,KAAK,CAAC;AACrB,aAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,KAAK,CAAC,EACvD;AACA,YAAA,OAAO,kBAAkB,CAAC;AAC3B,SAAA;AACD,QAAA,OAAO,gBAAgB,CAAC;KACzB,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,kBAAkB,CAAC,CAAC,CAAC;AAErD,IAAA,MAAM,WAAW,GAAGC,YAAM,CAA+B,EAAE,CAAC,CAAC;AAC7D,IAAA,MAAM,iBAAiB,GAAGA,YAAM,CAAiB,IAAI,CAAC,CAAC;;IAGvDF,eAAS,CAAC,MAAK;QACb,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,QAAA,WAAW,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;;AAGpE,QAAA,IAAI,WAAW,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE;YACnC,SAAS,GAAG,WAAW,CAAC,OAAO;AAC5B,iBAAA,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACZ,iBAAA,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,EAAE,YAAY,IAAI,CAAC,CAAC;AACzC,iBAAA,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,MAAM,CAAC,CAAC;AAC9C,SAAA;;AAGD,QAAA,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC;AAChC,YAAA,GAAG,EAAE,SAAS;AACd,YAAA,QAAQ,EAAE,QAAQ;AACnB,SAAA,CAAC,CAAC;AACL,KAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;AAEf,IAAA,MAAM,cAAc,GAAGG,iBAAW,CAAC,CAAC,KAAK,KAAI;QAC3C,IAAI,CAAC,WAAW,EAAE,OAAO;AAAE,YAAA,OAAO,IAAI,CAAC;AACvC,QAAA,OAAO,CAAC,OAAO,MAAM,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC;KAC5D,EAAE,EAAE,CAAC,CAAC;AAEP,IAAA,MAAM,gBAAgB,GAAuBF,aAAO,CAClD,OAAO;QACL,SAAS,EAAE,UAAU,CAAC,MAAM;AAC5B,QAAA,IAAI,EAAE,8CAA8C;AACrD,KAAA,CAAC,EACF,CAAC,UAAU,CAAC,CACb,CAAC;AAEF,IAAA,QACEG,sBAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAC,yBAAyB,EAAA;AACtC,QAAAA,sBAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,UAAU,CAAC,SAAS,EAAA;YACjC,MAAM;AACP,YAAAA,sBAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,UAAU,CAAC,uBAAuB,EAAA;gBAChDA,sBAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,GAAG,EAAE,iBAAiB,EAAE,SAAS,EAAE,UAAU,CAAC,iBAAiB,EAAA;oBACjE,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,KAAK,MAC3BA,sBAAK,CAAA,aAAA,CAAA,KAAA,EAAA,EAAA,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,cAAc,CAAC,KAAK,CAAC,EAAA;wBACzCA,sBAAC,CAAA,aAAA,CAAAC,2BAAa,OACR,KAAK,EACT,gBAAgB,EAAE,UAAU,CAAC,uBAAuB,EACpD,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,WAAW,EACxB,CAAA,CACE,CACP,CAAC;AACD,oBAAA,OAAO,IAAID,sBAAA,CAAA,aAAA,CAACE,uBAAW,EAAA,IAAA,CAAG,CACvB,CACF;AACN,YAAAF,sBAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,UAAU,CAAC,cAAc,EAAA;AACtC,gBAAA,WAAW,KACVA,sBAAA,CAAA,aAAA,CAACG,qCAAkB,EACjB,EAAA,WAAW,EAAE,WAAW,EACxB,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,UAAU,CAAC,wBAAwB,GACrD,CACH;gBACDH,sBAAC,CAAA,aAAA,CAAAI,mBAAS,EAAK,EAAA,GAAA,KAAK,EAAE,gBAAgB,EAAE,UAAU,CAAC,eAAe,EAAA,CAAI,CAClE;YACL,MAAM,KACLJ,sBAAA,CAAA,aAAA,CAACK,iBAAQ,EAAA,EACP,OAAO,EAAE,MAAM,EACf,cAAc,EAAC,SAAS,EACxB,WAAW,EAAE,WAAW,EACxB,gBAAgB,EAAE,gBAAgB,EAAA,CAClC,CACH,CACG,CACF,EACN;AACJ;;;;"}
|
|
@@ -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,qBA+Cf"}
|
|
@@ -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,20 @@ 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
|
-
}, [
|
|
60
|
+
}, [
|
|
61
|
+
reportAnalyticsEvent,
|
|
62
|
+
linkClickEvent,
|
|
63
|
+
responseId,
|
|
64
|
+
cssClasses,
|
|
65
|
+
onLinkClick,
|
|
66
|
+
]);
|
|
60
67
|
return (React__default.default.createElement(ReactMarkdown__default.default, { className: cssClasses.container, children: content, remarkPlugins: unifiedPlugins.remark, rehypePlugins: unifiedPlugins.rehype, components: components }));
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -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
|
|
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 }, [\n reportAnalyticsEvent,\n linkClickEvent,\n responseId,\n cssClasses,\n onLinkClick,\n ]);\n\n return (\n <ReactMarkdown\n className={cssClasses.container}\n children={content}\n remarkPlugins={unifiedPlugins.remark}\n rehypePlugins={unifiedPlugins.rehype}\n components={components}\n />\n );\n}\n"],"names":["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,CAAC;AACtB,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;QACD,oBAAoB;QACpB,cAAc;QACd,UAAU;QACV,UAAU;QACV,WAAW;AACZ,KAAA,CAAC,CAAC;AAEH,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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"}
|
|
@@ -14,7 +14,11 @@ export interface MessageSuggestionCssClasses {
|
|
|
14
14
|
* @public
|
|
15
15
|
*/
|
|
16
16
|
export interface MessageSuggestionsProps {
|
|
17
|
+
/** List of clickable message suggestions to render. */
|
|
17
18
|
suggestions: string[];
|
|
19
|
+
/** {@inheritdoc ChatInputProps.handleError} */
|
|
20
|
+
handleError?: (e: unknown) => void;
|
|
21
|
+
/** CSS classes for customizing the component styling. */
|
|
18
22
|
customCssClasses?: MessageSuggestionCssClasses;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAU3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;CAChD;AAWD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAU3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;CAChD;AAWD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqChE,CAAC"}
|
|
@@ -20,7 +20,7 @@ const defaultClassnames = withStylelessCssClasses.withStylelessCssClasses("Sugge
|
|
|
20
20
|
*
|
|
21
21
|
* @internal
|
|
22
22
|
*/
|
|
23
|
-
const MessageSuggestions = ({ suggestions, customCssClasses, }) => {
|
|
23
|
+
const MessageSuggestions = ({ handleError, suggestions, customCssClasses, }) => {
|
|
24
24
|
const actions = chatHeadlessReact.useChatActions();
|
|
25
25
|
const notes = chatHeadlessReact.useChatState((state) => state.conversation.notes);
|
|
26
26
|
const defaultHandleApiError = useDefaultHandleApiError.useDefaultHandleApiError();
|
|
@@ -31,8 +31,8 @@ const MessageSuggestions = ({ suggestions, customCssClasses, }) => {
|
|
|
31
31
|
};
|
|
32
32
|
actions.setMessageNotes(newNotes);
|
|
33
33
|
const res = actions.getNextMessage(msg);
|
|
34
|
-
res.catch(defaultHandleApiError);
|
|
35
|
-
}, [actions, notes, defaultHandleApiError]);
|
|
34
|
+
res.catch(handleError ?? defaultHandleApiError);
|
|
35
|
+
}, [actions, notes, handleError, defaultHandleApiError]);
|
|
36
36
|
const classes = useComposedCssClasses.useComposedCssClasses(defaultClassnames, customCssClasses);
|
|
37
37
|
return (React__default.default.createElement("div", { className: classes.container }, suggestions.map((suggestion, index) => (React__default.default.createElement("button", { key: index, className: classes.suggestion,
|
|
38
38
|
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.js","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n suggestions: string[];\n customCssClasses?: MessageSuggestionCssClasses;\n}\n\nconst defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(\n \"Suggestions\",\n {\n container: \"flex gap-2 mb-4 w-full overflow-x-auto flex-wrap\",\n suggestion:\n \"hover:cursor-pointer px-2 py-1 bg-white hover:bg-slate-300 rounded-full text-sm text-blue-700 border border-blue-700 hover:underline\",\n }\n);\n\n/**\n * A component that displays a list of suggested messages\n * to the user, which they can click to send the message to the bot.\n *\n * @internal\n */\nexport const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({\n suggestions,\n customCssClasses,\n}) => {\n const actions = useChatActions();\n const notes = useChatState((state) => state.conversation.notes);\n const defaultHandleApiError = useDefaultHandleApiError();\n const sendMsg = useCallback(\n (msg: string) => {\n const newNotes = {\n ...(notes || {}),\n suggestedReplies: undefined,\n } satisfies MessageNotes;\n actions.setMessageNotes(newNotes);\n const res = actions.getNextMessage(msg);\n res.catch(defaultHandleApiError);\n },\n [actions, notes, defaultHandleApiError]\n );\n\n const classes = useComposedCssClasses(defaultClassnames, customCssClasses);\n\n return (\n <div className={classes.container}>\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n className={classes.suggestion}\n // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop\n onClick={() => sendMsg(suggestion)}\n >\n {suggestion}\n </button>\n ))}\n </div>\n );\n};\n"],"names":["withStylelessCssClasses","useChatActions","useChatState","useDefaultHandleApiError","useCallback","useComposedCssClasses","React"],"mappings":";;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.js","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n /** List of clickable message suggestions to render. */\n suggestions: string[];\n /** {@inheritdoc ChatInputProps.handleError} */\n handleError?: (e: unknown) => void;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageSuggestionCssClasses;\n}\n\nconst defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(\n \"Suggestions\",\n {\n container: \"flex gap-2 mb-4 w-full overflow-x-auto flex-wrap\",\n suggestion:\n \"hover:cursor-pointer px-2 py-1 bg-white hover:bg-slate-300 rounded-full text-sm text-blue-700 border border-blue-700 hover:underline\",\n }\n);\n\n/**\n * A component that displays a list of suggested messages\n * to the user, which they can click to send the message to the bot.\n *\n * @internal\n */\nexport const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({\n handleError,\n suggestions,\n customCssClasses,\n}) => {\n const actions = useChatActions();\n const notes = useChatState((state) => state.conversation.notes);\n const defaultHandleApiError = useDefaultHandleApiError();\n const sendMsg = useCallback(\n (msg: string) => {\n const newNotes = {\n ...(notes || {}),\n suggestedReplies: undefined,\n } satisfies MessageNotes;\n actions.setMessageNotes(newNotes);\n const res = actions.getNextMessage(msg);\n res.catch(handleError ?? defaultHandleApiError);\n },\n [actions, notes, handleError, defaultHandleApiError]\n );\n\n const classes = useComposedCssClasses(defaultClassnames, customCssClasses);\n\n return (\n <div className={classes.container}>\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n className={classes.suggestion}\n // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop\n onClick={() => sendMsg(suggestion)}\n >\n {suggestion}\n </button>\n ))}\n </div>\n );\n};\n"],"names":["withStylelessCssClasses","useChatActions","useChatState","useDefaultHandleApiError","useCallback","useComposedCssClasses","React"],"mappings":";;;;;;;;;;;;AAkCA,MAAM,iBAAiB,GAAgCA,+CAAuB,CAC5E,aAAa,EACb;AACE,IAAA,SAAS,EAAE,kDAAkD;AAC7D,IAAA,UAAU,EACR,sIAAsI;AACzI,CAAA,CACF,CAAC;AAEF;;;;;AAKG;AACI,MAAM,kBAAkB,GAAsC,CAAC,EACpE,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,KAAI;AACH,IAAA,MAAM,OAAO,GAAGC,gCAAc,EAAE,CAAC;AACjC,IAAA,MAAM,KAAK,GAAGC,8BAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AAChE,IAAA,MAAM,qBAAqB,GAAGC,iDAAwB,EAAE,CAAC;AACzD,IAAA,MAAM,OAAO,GAAGC,iBAAW,CACzB,CAAC,GAAW,KAAI;AACd,QAAA,MAAM,QAAQ,GAAG;AACf,YAAA,IAAI,KAAK,IAAI,EAAE,CAAC;AAChB,YAAA,gBAAgB,EAAE,SAAS;SACL,CAAC;AACzB,QAAA,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AACxC,QAAA,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,qBAAqB,CAAC,CAAC;KACjD,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,CAAC,CACrD,CAAC;IAEF,MAAM,OAAO,GAAGC,2CAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE3E,IAAA,QACEC,sBAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,OAAO,CAAC,SAAS,EAAA,EAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,MACjCA,sBACE,CAAA,aAAA,CAAA,QAAA,EAAA,EAAA,GAAG,EAAE,KAAK,EACV,SAAS,EAAE,OAAO,CAAC,UAAU;;AAE7B,QAAA,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAAA,EAEjC,UAAU,CACJ,CACV,CAAC,CACE,EACN;AACJ;;;;"}
|
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
|
/**
|
|
@@ -331,7 +335,11 @@ export declare interface MessageSuggestionCssClasses {
|
|
|
331
335
|
* @public
|
|
332
336
|
*/
|
|
333
337
|
export declare interface MessageSuggestionsProps {
|
|
338
|
+
/** List of clickable message suggestions to render. */
|
|
334
339
|
suggestions: string[];
|
|
340
|
+
/** {@inheritdoc ChatInputProps.handleError} */
|
|
341
|
+
handleError?: (e: unknown) => void;
|
|
342
|
+
/** CSS classes for customizing the component styling. */
|
|
335
343
|
customCssClasses?: MessageSuggestionCssClasses;
|
|
336
344
|
}
|
|
337
345
|
|
package/lib/esm/package.json.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAQrD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,uCAAuC;IACvC,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAiC,EACjC,MAAc,EACd,cAAsB,EACtB,WAAW,EACX,cAA8B,EAC9B,gBAAgB,EAChB,MAAM,GACP,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"ChatInput.d.ts","sourceRoot":"","sources":["../../../../src/components/ChatInput.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AAQrD;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAaD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;OAGG;IACH,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,uCAAuC;IACvC,cAAc,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC;IAC7B,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,mBAAmB,CAAC;IACvC,4DAA4D;IAC5D,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,EACxB,WAAiC,EACjC,MAAc,EACd,cAAsB,EACtB,WAAW,EACX,cAA8B,EAC9B,gBAAgB,EAChB,MAAM,GACP,EAAE,cAAc,qBA6DhB"}
|
|
@@ -33,9 +33,11 @@ function ChatInput({ placeholder = "Type a message...", stream = false, inputAut
|
|
|
33
33
|
? chat.streamNextMessage(input)
|
|
34
34
|
: chat.getNextMessage(input);
|
|
35
35
|
setInput("");
|
|
36
|
-
res
|
|
36
|
+
res
|
|
37
|
+
.then(() => {
|
|
37
38
|
onSend?.(input);
|
|
38
|
-
})
|
|
39
|
+
})
|
|
40
|
+
.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
|
|
39
41
|
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);
|
|
40
42
|
const handleKeyDown = useCallback((e) => {
|
|
41
43
|
if (!e.shiftKey && e.key === "Enter") {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChatInput.mjs","sources":["../../../../src/components/ChatInput.tsx"],"sourcesContent":["import React, { useCallback, useState } from \"react\";\nimport { useChatActions, useChatState } from \"@yext/chat-headless-react\";\nimport { ArrowIcon } from \"../icons/Arrow\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport TextareaAutosize from \"react-textarea-autosize\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\n\n/**\n * The CSS class interface for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputCssClasses {\n container?: string;\n textArea?: string;\n sendButton?: string;\n}\n\nconst builtInCssClasses: ChatInputCssClasses = withStylelessCssClasses(\n \"Input\",\n {\n container: \"w-full h-fit flex flex-row relative @container\",\n textArea:\n \"w-full p-4 pr-12 border border-slate-300 rounded-3xl resize-none text-[13px] @[480px]:text-base placeholder:text-[13px] placeholder:@[480px]:text-base text-slate-900\",\n sendButton:\n \"rounded-full p-1.5 w-8 h-8 stroke-2 text-white bg-blue-600 disabled:bg-slate-200 hover:bg-blue-800 active:scale-90 transition-all absolute right-4 bottom-2.5 @[480px]:bottom-3.5\",\n }\n);\n\n/**\n * The props for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputProps {\n /**\n * The input's placeholder text when no text has been entered by the user.\n * Defaults to \"Type a message...\".\n */\n placeholder?: string;\n /**\n * Enable streaming behavior by making a request to Chat Streaming API.\n * This feature is experimental, and is subject to change.\n * Defaults to false.\n */\n stream?: boolean;\n /** Enable auto focus for the input box. Defaults to false. */\n inputAutoFocus?: boolean;\n /**\n * A function which is called when an error occurs from Chat API while processing the user's message.\n * By default, the error is logged to the console and an error message is added to state.\n */\n handleError?: (e: unknown) => void;\n /** Custom icon for the send button. */\n sendButtonIcon?: JSX.Element;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: ChatInputCssClasses;\n /** A callback which is called when user sends a message. */\n onSend?: (message: string) => void;\n}\n\n/**\n * A component that allows user to input message and send to Chat API.\n *\n * @remarks\n * Pressing \"Enter\" key will send the current message.\n * To add a newline, press \"Shift\" and \"Enter\".\n *\n * @public\n *\n * @param props - {@link ChatInputProps}\n */\nexport function ChatInput({\n placeholder = \"Type a message...\",\n stream = false,\n inputAutoFocus = false,\n handleError,\n sendButtonIcon = <ArrowIcon />,\n customCssClasses,\n onSend,\n}: ChatInputProps) {\n const chat = useChatActions();\n const [input, setInput] = useState(\"\");\n const canSendMessage = useChatState(\n (state) => state.conversation.canSendMessage\n );\n const defaultHandleApiError = useDefaultHandleApiError();\n\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const sendMessage = useCallback(async () => {\n const res = stream\n ? chat.streamNextMessage(input)\n : chat.getNextMessage(input);\n setInput(\"\");\n res.then(() => {\n
|
|
1
|
+
{"version":3,"file":"ChatInput.mjs","sources":["../../../../src/components/ChatInput.tsx"],"sourcesContent":["import React, { useCallback, useState } from \"react\";\nimport { useChatActions, useChatState } from \"@yext/chat-headless-react\";\nimport { ArrowIcon } from \"../icons/Arrow\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport TextareaAutosize from \"react-textarea-autosize\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\n\n/**\n * The CSS class interface for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputCssClasses {\n container?: string;\n textArea?: string;\n sendButton?: string;\n}\n\nconst builtInCssClasses: ChatInputCssClasses = withStylelessCssClasses(\n \"Input\",\n {\n container: \"w-full h-fit flex flex-row relative @container\",\n textArea:\n \"w-full p-4 pr-12 border border-slate-300 rounded-3xl resize-none text-[13px] @[480px]:text-base placeholder:text-[13px] placeholder:@[480px]:text-base text-slate-900\",\n sendButton:\n \"rounded-full p-1.5 w-8 h-8 stroke-2 text-white bg-blue-600 disabled:bg-slate-200 hover:bg-blue-800 active:scale-90 transition-all absolute right-4 bottom-2.5 @[480px]:bottom-3.5\",\n }\n);\n\n/**\n * The props for the {@link ChatInput} component.\n *\n * @public\n */\nexport interface ChatInputProps {\n /**\n * The input's placeholder text when no text has been entered by the user.\n * Defaults to \"Type a message...\".\n */\n placeholder?: string;\n /**\n * Enable streaming behavior by making a request to Chat Streaming API.\n * This feature is experimental, and is subject to change.\n * Defaults to false.\n */\n stream?: boolean;\n /** Enable auto focus for the input box. Defaults to false. */\n inputAutoFocus?: boolean;\n /**\n * A function which is called when an error occurs from Chat API while processing the user's message.\n * By default, the error is logged to the console and an error message is added to state.\n */\n handleError?: (e: unknown) => void;\n /** Custom icon for the send button. */\n sendButtonIcon?: JSX.Element;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: ChatInputCssClasses;\n /** A callback which is called when user sends a message. */\n onSend?: (message: string) => void;\n}\n\n/**\n * A component that allows user to input message and send to Chat API.\n *\n * @remarks\n * Pressing \"Enter\" key will send the current message.\n * To add a newline, press \"Shift\" and \"Enter\".\n *\n * @public\n *\n * @param props - {@link ChatInputProps}\n */\nexport function ChatInput({\n placeholder = \"Type a message...\",\n stream = false,\n inputAutoFocus = false,\n handleError,\n sendButtonIcon = <ArrowIcon />,\n customCssClasses,\n onSend,\n}: ChatInputProps) {\n const chat = useChatActions();\n const [input, setInput] = useState(\"\");\n const canSendMessage = useChatState(\n (state) => state.conversation.canSendMessage\n );\n const defaultHandleApiError = useDefaultHandleApiError();\n\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const sendMessage = useCallback(async () => {\n const res = stream\n ? chat.streamNextMessage(input)\n : chat.getNextMessage(input);\n setInput(\"\");\n res\n .then(() => {\n onSend?.(input);\n })\n .catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));\n }, [chat, input, handleError, defaultHandleApiError, stream, onSend]);\n\n const handleKeyDown = useCallback(\n (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n if (!e.shiftKey && e.key === \"Enter\") {\n e.preventDefault();\n if (canSendMessage && input.trim().length !== 0) {\n sendMessage();\n }\n }\n },\n [sendMessage, canSendMessage, input]\n );\n\n const onInputChange = useCallback(\n (e: React.ChangeEvent<HTMLTextAreaElement>) => {\n setInput(e.target.value);\n },\n []\n );\n\n return (\n <div className={cssClasses.container}>\n <TextareaAutosize\n autoFocus={inputAutoFocus}\n onKeyDown={handleKeyDown}\n value={input}\n onChange={onInputChange}\n className={cssClasses.textArea}\n placeholder={placeholder}\n />\n <button\n aria-label=\"Send Message\"\n disabled={!canSendMessage || input.trim().length === 0}\n onClick={sendMessage}\n className={cssClasses.sendButton}\n >\n {sendButtonIcon}\n </button>\n </div>\n );\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -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,qBA+G9C"}
|
|
@@ -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
|
-
suggestions && (React.createElement(MessageSuggestions, { suggestions: suggestions, customCssClasses: cssClasses.messageSuggestionClasses })),
|
|
89
|
+
suggestions && (React.createElement(MessageSuggestions, { handleError: handleError, 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 handleError={handleError}\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,qBA+Cf"}
|
|
@@ -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,20 @@ 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
|
-
}, [
|
|
50
|
+
}, [
|
|
51
|
+
reportAnalyticsEvent,
|
|
52
|
+
linkClickEvent,
|
|
53
|
+
responseId,
|
|
54
|
+
cssClasses,
|
|
55
|
+
onLinkClick,
|
|
56
|
+
]);
|
|
50
57
|
return (React.createElement(ReactMarkdown, { className: cssClasses.container, children: content, remarkPlugins: unifiedPlugins.remark, rehypePlugins: unifiedPlugins.rehype, components: components }));
|
|
51
58
|
}
|
|
52
59
|
|
|
@@ -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
|
|
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 }, [\n reportAnalyticsEvent,\n linkClickEvent,\n responseId,\n cssClasses,\n onLinkClick,\n ]);\n\n return (\n <ReactMarkdown\n className={cssClasses.container}\n children={content}\n remarkPlugins={unifiedPlugins.remark}\n rehypePlugins={unifiedPlugins.rehype}\n components={components}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAWA;AACA,MAAM,cAAc,GAAsD;AACxE,IAAA,MAAM,EAAE;AACN,QAAA,SAAS;AACV,KAAA;AACD,IAAA,MAAM,EAAE;QACN,SAAS;AACT,QAAA,cAAc;AACf,KAAA;CACF,CAAC;AAYF,MAAM,iBAAiB,GAAuB;AAC5C,IAAA,IAAI,EAAE,gBAAgB;CACvB,CAAC;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,CAAC;AACtB,SAAC,CAAC;QACF,OAAO;AACL,YAAA,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,KAAI;AACrC,gBAAA,QACE,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAA,GACM,KAAK,EACT,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,EACzC,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAA,EAEzB,QAAQ,CACP,EACJ;aACH;SACF,CAAC;AACJ,KAAC,EAAE;QACD,oBAAoB;QACpB,cAAc;QACd,UAAU;QACV,UAAU;QACV,WAAW;AACZ,KAAA,CAAC,CAAC;AAEH,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,aAAa,EAAA,EACZ,SAAS,EAAE,UAAU,CAAC,SAAS,EAC/B,QAAQ,EAAE,OAAO,EACjB,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,UAAU,EAAE,UAAU,EAAA,CACtB,EACF;AACJ;;;;"}
|
|
@@ -48,6 +48,8 @@ export interface MessageBubbleProps {
|
|
|
48
48
|
formatTimestamp?: (timestamp: string) => string;
|
|
49
49
|
/** CSS classes for customizing the component styling. */
|
|
50
50
|
customCssClasses?: MessageBubbleCssClasses;
|
|
51
|
+
/** 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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"}
|
|
@@ -14,7 +14,11 @@ export interface MessageSuggestionCssClasses {
|
|
|
14
14
|
* @public
|
|
15
15
|
*/
|
|
16
16
|
export interface MessageSuggestionsProps {
|
|
17
|
+
/** List of clickable message suggestions to render. */
|
|
17
18
|
suggestions: string[];
|
|
19
|
+
/** {@inheritdoc ChatInputProps.handleError} */
|
|
20
|
+
handleError?: (e: unknown) => void;
|
|
21
|
+
/** CSS classes for customizing the component styling. */
|
|
18
22
|
customCssClasses?: MessageSuggestionCssClasses;
|
|
19
23
|
}
|
|
20
24
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAU3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;CAChD;AAWD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageSuggestions.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsB,MAAM,OAAO,CAAC;AAU3C;;;;GAIG;AACH,MAAM,WAAW,2BAA2B;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,+CAA+C;IAC/C,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,yDAAyD;IACzD,gBAAgB,CAAC,EAAE,2BAA2B,CAAC;CAChD;AAWD;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAqChE,CAAC"}
|
|
@@ -14,7 +14,7 @@ const defaultClassnames = withStylelessCssClasses("Suggestions", {
|
|
|
14
14
|
*
|
|
15
15
|
* @internal
|
|
16
16
|
*/
|
|
17
|
-
const MessageSuggestions = ({ suggestions, customCssClasses, }) => {
|
|
17
|
+
const MessageSuggestions = ({ handleError, suggestions, customCssClasses, }) => {
|
|
18
18
|
const actions = useChatActions();
|
|
19
19
|
const notes = useChatState((state) => state.conversation.notes);
|
|
20
20
|
const defaultHandleApiError = useDefaultHandleApiError();
|
|
@@ -25,8 +25,8 @@ const MessageSuggestions = ({ suggestions, customCssClasses, }) => {
|
|
|
25
25
|
};
|
|
26
26
|
actions.setMessageNotes(newNotes);
|
|
27
27
|
const res = actions.getNextMessage(msg);
|
|
28
|
-
res.catch(defaultHandleApiError);
|
|
29
|
-
}, [actions, notes, defaultHandleApiError]);
|
|
28
|
+
res.catch(handleError ?? defaultHandleApiError);
|
|
29
|
+
}, [actions, notes, handleError, defaultHandleApiError]);
|
|
30
30
|
const classes = useComposedCssClasses(defaultClassnames, customCssClasses);
|
|
31
31
|
return (React.createElement("div", { className: classes.container }, suggestions.map((suggestion, index) => (React.createElement("button", { key: index, className: classes.suggestion,
|
|
32
32
|
// eslint-disable-next-line react-perf/jsx-no-new-function-as-prop
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageSuggestions.mjs","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n suggestions: string[];\n customCssClasses?: MessageSuggestionCssClasses;\n}\n\nconst defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(\n \"Suggestions\",\n {\n container: \"flex gap-2 mb-4 w-full overflow-x-auto flex-wrap\",\n suggestion:\n \"hover:cursor-pointer px-2 py-1 bg-white hover:bg-slate-300 rounded-full text-sm text-blue-700 border border-blue-700 hover:underline\",\n }\n);\n\n/**\n * A component that displays a list of suggested messages\n * to the user, which they can click to send the message to the bot.\n *\n * @internal\n */\nexport const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({\n suggestions,\n customCssClasses,\n}) => {\n const actions = useChatActions();\n const notes = useChatState((state) => state.conversation.notes);\n const defaultHandleApiError = useDefaultHandleApiError();\n const sendMsg = useCallback(\n (msg: string) => {\n const newNotes = {\n ...(notes || {}),\n suggestedReplies: undefined,\n } satisfies MessageNotes;\n actions.setMessageNotes(newNotes);\n const res = actions.getNextMessage(msg);\n res.catch(defaultHandleApiError);\n },\n [actions, notes, defaultHandleApiError]\n );\n\n const classes = useComposedCssClasses(defaultClassnames, customCssClasses);\n\n return (\n <div className={classes.container}>\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n className={classes.suggestion}\n // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop\n onClick={() => sendMsg(suggestion)}\n >\n {suggestion}\n </button>\n ))}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"MessageSuggestions.mjs","sources":["../../../../src/components/MessageSuggestions.tsx"],"sourcesContent":["import React, { useCallback } from \"react\";\nimport {\n MessageNotes,\n useChatActions,\n useChatState,\n} from \"@yext/chat-headless-react\";\nimport { useDefaultHandleApiError } from \"../hooks/useDefaultHandleApiError\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { useComposedCssClasses } from \"../hooks\";\n\n/**\n * The CSS class interface for the MessageSuggestion component.\n *\n * @public\n */\nexport interface MessageSuggestionCssClasses {\n container?: string;\n suggestion?: string;\n}\n\n/**\n * The props for the MessageSuggestions component.\n *\n * @public\n */\nexport interface MessageSuggestionsProps {\n /** List of clickable message suggestions to render. */\n suggestions: string[];\n /** {@inheritdoc ChatInputProps.handleError} */\n handleError?: (e: unknown) => void;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageSuggestionCssClasses;\n}\n\nconst defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(\n \"Suggestions\",\n {\n container: \"flex gap-2 mb-4 w-full overflow-x-auto flex-wrap\",\n suggestion:\n \"hover:cursor-pointer px-2 py-1 bg-white hover:bg-slate-300 rounded-full text-sm text-blue-700 border border-blue-700 hover:underline\",\n }\n);\n\n/**\n * A component that displays a list of suggested messages\n * to the user, which they can click to send the message to the bot.\n *\n * @internal\n */\nexport const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({\n handleError,\n suggestions,\n customCssClasses,\n}) => {\n const actions = useChatActions();\n const notes = useChatState((state) => state.conversation.notes);\n const defaultHandleApiError = useDefaultHandleApiError();\n const sendMsg = useCallback(\n (msg: string) => {\n const newNotes = {\n ...(notes || {}),\n suggestedReplies: undefined,\n } satisfies MessageNotes;\n actions.setMessageNotes(newNotes);\n const res = actions.getNextMessage(msg);\n res.catch(handleError ?? defaultHandleApiError);\n },\n [actions, notes, handleError, defaultHandleApiError]\n );\n\n const classes = useComposedCssClasses(defaultClassnames, customCssClasses);\n\n return (\n <div className={classes.container}>\n {suggestions.map((suggestion, index) => (\n <button\n key={index}\n className={classes.suggestion}\n // eslint-disable-next-line react-perf/jsx-no-new-function-as-prop\n onClick={() => sendMsg(suggestion)}\n >\n {suggestion}\n </button>\n ))}\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;AAkCA,MAAM,iBAAiB,GAAgC,uBAAuB,CAC5E,aAAa,EACb;AACE,IAAA,SAAS,EAAE,kDAAkD;AAC7D,IAAA,UAAU,EACR,sIAAsI;AACzI,CAAA,CACF,CAAC;AAEF;;;;;AAKG;AACI,MAAM,kBAAkB,GAAsC,CAAC,EACpE,WAAW,EACX,WAAW,EACX,gBAAgB,GACjB,KAAI;AACH,IAAA,MAAM,OAAO,GAAG,cAAc,EAAE,CAAC;AACjC,IAAA,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;AAChE,IAAA,MAAM,qBAAqB,GAAG,wBAAwB,EAAE,CAAC;AACzD,IAAA,MAAM,OAAO,GAAG,WAAW,CACzB,CAAC,GAAW,KAAI;AACd,QAAA,MAAM,QAAQ,GAAG;AACf,YAAA,IAAI,KAAK,IAAI,EAAE,CAAC;AAChB,YAAA,gBAAgB,EAAE,SAAS;SACL,CAAC;AACzB,QAAA,OAAO,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;QAClC,MAAM,GAAG,GAAG,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC;AACxC,QAAA,GAAG,CAAC,KAAK,CAAC,WAAW,IAAI,qBAAqB,CAAC,CAAC;KACjD,EACD,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,qBAAqB,CAAC,CACrD,CAAC;IAEF,MAAM,OAAO,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE3E,IAAA,QACE,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAK,SAAS,EAAE,OAAO,CAAC,SAAS,EAAA,EAC9B,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,KAAK,MACjC,KACE,CAAA,aAAA,CAAA,QAAA,EAAA,EAAA,GAAG,EAAE,KAAK,EACV,SAAS,EAAE,OAAO,CAAC,UAAU;;AAE7B,QAAA,OAAO,EAAE,MAAM,OAAO,CAAC,UAAU,CAAC,EAAA,EAEjC,UAAU,CACJ,CACV,CAAC,CACE,EACN;AACJ;;;;"}
|
package/package.json
CHANGED
|
@@ -94,9 +94,11 @@ export function ChatInput({
|
|
|
94
94
|
? chat.streamNextMessage(input)
|
|
95
95
|
: chat.getNextMessage(input);
|
|
96
96
|
setInput("");
|
|
97
|
-
res
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
res
|
|
98
|
+
.then(() => {
|
|
99
|
+
onSend?.(input);
|
|
100
|
+
})
|
|
101
|
+
.catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
|
|
100
102
|
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);
|
|
101
103
|
|
|
102
104
|
const handleKeyDown = useCallback(
|
|
@@ -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
|
))}
|
|
@@ -176,6 +180,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
176
180
|
<div className={cssClasses.inputContainer}>
|
|
177
181
|
{suggestions && (
|
|
178
182
|
<MessageSuggestions
|
|
183
|
+
handleError={handleError}
|
|
179
184
|
suggestions={suggestions}
|
|
180
185
|
customCssClasses={cssClasses.messageSuggestionClasses}
|
|
181
186
|
/>
|
|
@@ -186,6 +191,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
186
191
|
<Markdown
|
|
187
192
|
content={footer}
|
|
188
193
|
linkClickEvent="WEBSITE"
|
|
194
|
+
onLinkClick={onLinkClick}
|
|
189
195
|
customCssClasses={footerCssClasses}
|
|
190
196
|
/>
|
|
191
197
|
)}
|
|
@@ -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,13 @@ export function Markdown({
|
|
|
91
95
|
);
|
|
92
96
|
},
|
|
93
97
|
};
|
|
94
|
-
}, [
|
|
98
|
+
}, [
|
|
99
|
+
reportAnalyticsEvent,
|
|
100
|
+
linkClickEvent,
|
|
101
|
+
responseId,
|
|
102
|
+
cssClasses,
|
|
103
|
+
onLinkClick,
|
|
104
|
+
]);
|
|
95
105
|
|
|
96
106
|
return (
|
|
97
107
|
<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 */}
|
|
@@ -24,7 +24,11 @@ export interface MessageSuggestionCssClasses {
|
|
|
24
24
|
* @public
|
|
25
25
|
*/
|
|
26
26
|
export interface MessageSuggestionsProps {
|
|
27
|
+
/** List of clickable message suggestions to render. */
|
|
27
28
|
suggestions: string[];
|
|
29
|
+
/** {@inheritdoc ChatInputProps.handleError} */
|
|
30
|
+
handleError?: (e: unknown) => void;
|
|
31
|
+
/** CSS classes for customizing the component styling. */
|
|
28
32
|
customCssClasses?: MessageSuggestionCssClasses;
|
|
29
33
|
}
|
|
30
34
|
|
|
@@ -44,6 +48,7 @@ const defaultClassnames: MessageSuggestionCssClasses = withStylelessCssClasses(
|
|
|
44
48
|
* @internal
|
|
45
49
|
*/
|
|
46
50
|
export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
|
|
51
|
+
handleError,
|
|
47
52
|
suggestions,
|
|
48
53
|
customCssClasses,
|
|
49
54
|
}) => {
|
|
@@ -58,9 +63,9 @@ export const MessageSuggestions: React.FC<MessageSuggestionsProps> = ({
|
|
|
58
63
|
} satisfies MessageNotes;
|
|
59
64
|
actions.setMessageNotes(newNotes);
|
|
60
65
|
const res = actions.getNextMessage(msg);
|
|
61
|
-
res.catch(defaultHandleApiError);
|
|
66
|
+
res.catch(handleError ?? defaultHandleApiError);
|
|
62
67
|
},
|
|
63
|
-
[actions, notes, defaultHandleApiError]
|
|
68
|
+
[actions, notes, handleError, defaultHandleApiError]
|
|
64
69
|
);
|
|
65
70
|
|
|
66
71
|
const classes = useComposedCssClasses(defaultClassnames, customCssClasses);
|