@yext/chat-ui-react 0.8.5 → 0.8.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/bundle.css +1 -1
- package/lib/commonjs/package.json.js +1 -1
- package/lib/commonjs/src/components/ChatInput.d.ts +3 -1
- package/lib/commonjs/src/components/ChatInput.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatInput.js +5 -3
- package/lib/commonjs/src/components/ChatInput.js.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.d.ts +5 -0
- package/lib/commonjs/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPanel.js +10 -3
- package/lib/commonjs/src/components/ChatPanel.js.map +1 -1
- package/lib/commonjs/src/components/ChatPopUp.d.ts.map +1 -1
- package/lib/commonjs/src/components/ChatPopUp.js +5 -3
- package/lib/commonjs/src/components/ChatPopUp.js.map +1 -1
- package/lib/commonjs/src/components/Markdown.d.ts +19 -3
- package/lib/commonjs/src/components/Markdown.d.ts.map +1 -1
- package/lib/commonjs/src/components/Markdown.js +11 -5
- 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 +5 -2
- package/lib/commonjs/src/components/MessageBubble.js.map +1 -1
- package/lib/esm/index.d.ts +11 -2
- package/lib/esm/package.json.mjs +1 -1
- package/lib/esm/src/components/ChatInput.d.ts +3 -1
- package/lib/esm/src/components/ChatInput.d.ts.map +1 -1
- package/lib/esm/src/components/ChatInput.mjs +5 -3
- package/lib/esm/src/components/ChatInput.mjs.map +1 -1
- package/lib/esm/src/components/ChatPanel.d.ts +5 -0
- package/lib/esm/src/components/ChatPanel.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPanel.mjs +10 -3
- package/lib/esm/src/components/ChatPanel.mjs.map +1 -1
- package/lib/esm/src/components/ChatPopUp.d.ts.map +1 -1
- package/lib/esm/src/components/ChatPopUp.mjs +5 -3
- package/lib/esm/src/components/ChatPopUp.mjs.map +1 -1
- package/lib/esm/src/components/Markdown.d.ts +19 -3
- package/lib/esm/src/components/Markdown.d.ts.map +1 -1
- package/lib/esm/src/components/Markdown.mjs +11 -5
- 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 +6 -3
- package/lib/esm/src/components/MessageBubble.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/ChatInput.tsx +7 -2
- package/src/components/ChatPanel.tsx +33 -2
- package/src/components/ChatPopUp.tsx +5 -1
- package/src/components/Markdown.tsx +37 -7
- package/src/components/MessageBubble.tsx +14 -3
|
@@ -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\";\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\ninterface MarkdownProps {\n /** Stringified markdown. */\n content: string;\n /** The response ID correlates to the current message. */\n responseId?: string;\n /**
|
|
1
|
+
{"version":3,"file":"Markdown.mjs","sources":["../../../../src/components/Markdown.tsx"],"sourcesContent":["import ReactMarkdown, {\n PluggableList,\n ReactMarkdownOptions,\n} from \"react-markdown\";\nimport remarkGfm from \"remark-gfm\";\nimport rehypeRaw from \"rehype-raw\";\nimport rehypeSanitize from \"rehype-sanitize\";\nimport React, { useMemo } from \"react\";\nimport { useReportAnalyticsEvent } from \"../hooks/useReportAnalyticsEvent\";\nimport { useComposedCssClasses } from \"../hooks/useComposedCssClasses\";\n\n// The Remark and Rehype plugins to use in conjunction with ReactMarkdown.\nconst unifiedPlugins: { remark?: PluggableList; rehype: PluggableList } = {\n remark: [\n remarkGfm, //renders Github-Flavored Markdown\n ],\n rehype: [\n rehypeRaw, //to support HTML embedded in markdown\n rehypeSanitize, //to sanitize HTML content\n ],\n};\n\n/**\n * The CSS class interface for the Markdown component.\n *\n * @internal\n */\nexport interface MarkdownCssClasses {\n container?: string;\n link?: string;\n}\n\nconst builtInCssClasses: MarkdownCssClasses = {\n link: \"cursor-pointer\",\n};\n\ninterface MarkdownProps {\n /** Stringified markdown. */\n content: string;\n /** The response ID correlates to the current message. */\n responseId?: string;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MarkdownCssClasses;\n /**\n * Action to report for analytics event when a link is clicked.\n * Defaults to 'CHAT_LINK_CLICK'.\n */\n linkClickEvent?: \"WEBSITE\" | \"CHAT_LINK_CLICK\";\n /** A callback which is called when a link is clicked. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * Renders Github-Flavored Markdown from the Knowledge Graph. This Markdown can include\n * arbitrary HTML. Any HTML will be sanitized according to Rehype's default Schema.\n *\n * @remarks\n * A link click will send a CHAT_LINK_CLICK analytics event\n *\n * @internal\n */\nexport function Markdown({\n content,\n responseId,\n customCssClasses,\n linkClickEvent = \"CHAT_LINK_CLICK\",\n onLinkClick,\n}: MarkdownProps) {\n const reportAnalyticsEvent = useReportAnalyticsEvent();\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n\n const components: ReactMarkdownOptions[\"components\"] = useMemo(() => {\n const createClickHandlerFn = (href?: string) => () => {\n reportAnalyticsEvent({\n action: linkClickEvent,\n destinationUrl: href,\n chat: {\n responseId,\n },\n });\n onLinkClick?.(href)\n };\n return {\n a: ({ node: _, children, ...props }) => {\n return (\n <a\n {...props}\n onClick={createClickHandlerFn(props.href)}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className={cssClasses.link}\n >\n {children}\n </a>\n );\n },\n };\n }, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);\n\n return (\n <ReactMarkdown\n className={cssClasses.container}\n children={content}\n remarkPlugins={unifiedPlugins.remark}\n rehypePlugins={unifiedPlugins.rehype}\n components={components}\n />\n );\n}\n"],"names":[],"mappings":";;;;;;;;AAWA;AACA,MAAM,cAAc,GAAsD;AACxE,IAAA,MAAM,EAAE;AACN,QAAA,SAAS;AACV,KAAA;AACD,IAAA,MAAM,EAAE;QACN,SAAS;AACT,QAAA,cAAc;AACf,KAAA;CACF,CAAC;AAYF,MAAM,iBAAiB,GAAuB;AAC5C,IAAA,IAAI,EAAE,gBAAgB;CACvB,CAAC;AAkBF;;;;;;;;AAQG;AACa,SAAA,QAAQ,CAAC,EACvB,OAAO,EACP,UAAU,EACV,gBAAgB,EAChB,cAAc,GAAG,iBAAiB,EAClC,WAAW,GACG,EAAA;AACd,IAAA,MAAM,oBAAoB,GAAG,uBAAuB,EAAE,CAAC;IACvD,MAAM,UAAU,GAAG,qBAAqB,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;AAE9E,IAAA,MAAM,UAAU,GAAuC,OAAO,CAAC,MAAK;QAClE,MAAM,oBAAoB,GAAG,CAAC,IAAa,KAAK,MAAK;AACnD,YAAA,oBAAoB,CAAC;AACnB,gBAAA,MAAM,EAAE,cAAc;AACtB,gBAAA,cAAc,EAAE,IAAI;AACpB,gBAAA,IAAI,EAAE;oBACJ,UAAU;AACX,iBAAA;AACF,aAAA,CAAC,CAAC;AACH,YAAA,WAAW,GAAG,IAAI,CAAC,CAAA;AACrB,SAAC,CAAC;QACF,OAAO;AACL,YAAA,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,KAAK,EAAE,KAAI;AACrC,gBAAA,QACE,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAA,GACM,KAAK,EACT,OAAO,EAAE,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,EACzC,MAAM,EAAC,QAAQ,EACf,GAAG,EAAC,qBAAqB,EACzB,SAAS,EAAE,UAAU,CAAC,IAAI,EAAA,EAEzB,QAAQ,CACP,EACJ;aACH;SACF,CAAC;AACJ,KAAC,EAAE,CAAC,oBAAoB,EAAE,cAAc,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,IAAA,QACE,KAAA,CAAA,aAAA,CAAC,aAAa,EAAA,EACZ,SAAS,EAAE,UAAU,CAAC,SAAS,EAC/B,QAAQ,EAAE,OAAO,EACjB,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,aAAa,EAAE,cAAc,CAAC,MAAM,EACpC,UAAU,EAAE,UAAU,EAAA,CACtB,EACF;AACJ;;;;"}
|
|
@@ -48,6 +48,8 @@ export interface MessageBubbleProps {
|
|
|
48
48
|
formatTimestamp?: (timestamp: string) => string;
|
|
49
49
|
/** CSS classes for customizing the component styling. */
|
|
50
50
|
customCssClasses?: MessageBubbleCssClasses;
|
|
51
|
+
/** A callback which is called when user clicks a link. */
|
|
52
|
+
onLinkClick?: (href?: string) => void;
|
|
51
53
|
}
|
|
52
54
|
/**
|
|
53
55
|
* A component that displays the provided message.
|
|
@@ -56,5 +58,5 @@ export interface MessageBubbleProps {
|
|
|
56
58
|
*
|
|
57
59
|
* @param props - {@link MessageBubbleProps}
|
|
58
60
|
*/
|
|
59
|
-
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, }: MessageBubbleProps): React.JSX.Element;
|
|
61
|
+
export declare function MessageBubble({ message, showFeedbackButtons, showTimestamp, customCssClasses, formatTimestamp, onLinkClick, }: MessageBubbleProps): React.JSX.Element;
|
|
60
62
|
//# sourceMappingURL=MessageBubble.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.d.ts","sourceRoot":"","sources":["../../../../src/components/MessageBubble.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useMemo } from 'react';
|
|
2
2
|
import { MessageSource } from '@yext/chat-headless-react';
|
|
3
3
|
import { useComposedCssClasses } from '../hooks/useComposedCssClasses.mjs';
|
|
4
4
|
import { twMerge } from 'tailwind-merge';
|
|
@@ -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
|
|
@@ -43,11 +43,14 @@ function MessageBubble({ message, showFeedbackButtons = true, showTimestamp = tr
|
|
|
43
43
|
const timestampCssClasses = twMerge(cssClasses.timestamp, message.source === MessageSource.USER
|
|
44
44
|
? cssClasses.timestamp__user
|
|
45
45
|
: cssClasses.timestamp__bot);
|
|
46
|
+
const markdownCssClasses = useMemo(() => ({
|
|
47
|
+
container: textCssClasses,
|
|
48
|
+
}), [textCssClasses]);
|
|
46
49
|
return (React.createElement("div", { className: cssClasses.topContainer },
|
|
47
50
|
React.createElement("div", { className: subContainerCssClasses },
|
|
48
51
|
React.createElement("div", { className: bubbleCssClasses },
|
|
49
52
|
showFeedbackButtons && message.source === MessageSource.BOT && (React.createElement(FeedbackButtons, { customCssClasses: cssClasses.feedbackButtonsCssClasses, responseId: message.responseId })),
|
|
50
|
-
React.createElement(Markdown, { content: message.text, responseId: message.responseId,
|
|
53
|
+
React.createElement(Markdown, { content: message.text, responseId: message.responseId, customCssClasses: markdownCssClasses, onLinkClick: onLinkClick })),
|
|
51
54
|
showTimestamp && (React.createElement("div", { className: timestampCssClasses }, message.timestamp ? formatTimestamp(message.timestamp) : " ")))));
|
|
52
55
|
}
|
|
53
56
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MessageBubble.mjs","sources":["../../../../src/components/MessageBubble.tsx"],"sourcesContent":["import React from \"react\";\nimport { Message, MessageSource } from \"@yext/chat-headless-react\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Markdown } 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 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
|
|
1
|
+
{"version":3,"file":"MessageBubble.mjs","sources":["../../../../src/components/MessageBubble.tsx"],"sourcesContent":["import React, { useMemo } from \"react\";\nimport { Message, MessageSource } from \"@yext/chat-headless-react\";\nimport { useComposedCssClasses } from \"../hooks\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Markdown, MarkdownCssClasses } from \"./Markdown\";\nimport { withStylelessCssClasses } from \"../utils/withStylelessCssClasses\";\nimport { FeedbackButtons, FeedbackButtonsCssClasses } from \"./FeedbackButtons\";\n\n/**\n * The CSS class interface for the {@link MessageBubble} component.\n *\n * @public\n */\nexport interface MessageBubbleCssClasses {\n topContainer?: string;\n subContainer?: string;\n subContainer__bot?: string;\n subContainer__user?: string;\n bubble?: string;\n bubble__bot?: string;\n bubble__user?: string;\n text?: string;\n text__bot?: string;\n text__user?: string;\n timestamp?: string;\n timestamp__bot?: string;\n timestamp__user?: string;\n feedbackButtonsCssClasses?: FeedbackButtonsCssClasses;\n}\n\nconst builtInCssClasses: MessageBubbleCssClasses =\n withStylelessCssClasses<MessageBubbleCssClasses>(\"MessageBubble\", {\n topContainer: \"w-full animate-fade-in @container\",\n subContainer:\n \"flex flex-col @lg:flex-row @lg:items-center @lg:gap-x-2 @lg:m-1\",\n subContainer__bot: \"\",\n subContainer__user: \"@lg:flex-row-reverse\",\n bubble: \"relative group peer w-fit max-w-[80%] rounded-2xl p-4\",\n bubble__bot: \"bg-gradient-to-tr from-slate-50 to-slate-100\",\n bubble__user:\n \"ml-auto @lg:ml-0 bg-gradient-to-tr from-blue-600 to-blue-700 text-white\",\n text: \"text-[13px] @[480px]:text-base prose overflow-x-auto\",\n text__bot: \"text-slate-900\",\n text__user: \"text-white break-words\",\n timestamp:\n \"w-fit my-0.5 ml-4 @lg:ml-0 text-slate-400 text-[10px] @[480px]:text-[13px] opacity-0 peer-hover:opacity-100 duration-200 whitespace-pre-wrap\",\n timestamp__bot: \"\",\n timestamp__user: \"ml-auto\",\n feedbackButtonsCssClasses: {},\n });\n\n/**\n * The props for the {@link MessageBubble} component.\n *\n * @public\n */\nexport interface MessageBubbleProps {\n /** The message to display. */\n message: Message;\n /**\n * Whether to show the feedback buttons on the message bubble.\n * Defaults to true.\n */\n showFeedbackButtons?: boolean;\n /**\n * Whether to show the timestamp of the message with the message bubble.\n * Defaults to true.\n */\n showTimestamp?: boolean;\n /**\n * A function which is called to format the message's timestamp given in\n * ISO format (e.g. \"2023-05-18T19:33:34.553Z\").\n * Defaults to \"HH:MM A\" (e.g. \"7:33 PM\").\n */\n formatTimestamp?: (timestamp: string) => string;\n /** CSS classes for customizing the component styling. */\n customCssClasses?: MessageBubbleCssClasses;\n /** A callback which is called when user clicks a link. */\n onLinkClick?: (href?: string) => void;\n}\n\n/**\n * A component that displays the provided message.\n *\n * @public\n *\n * @param props - {@link MessageBubbleProps}\n */\nexport function MessageBubble({\n message,\n showFeedbackButtons = true,\n showTimestamp = true,\n customCssClasses,\n formatTimestamp = defaultFormatTimestamp,\n onLinkClick,\n}: MessageBubbleProps) {\n const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);\n const bubbleCssClasses = twMerge(\n cssClasses.bubble,\n message.source === MessageSource.USER\n ? cssClasses.bubble__user\n : cssClasses.bubble__bot\n );\n const textCssClasses = twMerge(\n cssClasses.text,\n message.source === MessageSource.USER\n ? cssClasses.text__user\n : cssClasses.text__bot\n );\n const subContainerCssClasses = twMerge(\n cssClasses.subContainer,\n message.source === MessageSource.USER\n ? cssClasses.subContainer__user\n : cssClasses.subContainer__bot\n );\n const timestampCssClasses = twMerge(\n cssClasses.timestamp,\n message.source === MessageSource.USER\n ? cssClasses.timestamp__user\n : cssClasses.timestamp__bot\n );\n\n const markdownCssClasses: MarkdownCssClasses = useMemo(\n () => ({\n container: textCssClasses,\n }),\n [textCssClasses]\n );\n\n return (\n <div className={cssClasses.topContainer}>\n <div className={subContainerCssClasses}>\n <div className={bubbleCssClasses}>\n {showFeedbackButtons && message.source === MessageSource.BOT && (\n <FeedbackButtons\n customCssClasses={cssClasses.feedbackButtonsCssClasses}\n responseId={message.responseId}\n />\n )}\n <Markdown\n content={message.text}\n responseId={message.responseId}\n customCssClasses={markdownCssClasses}\n onLinkClick={onLinkClick}\n />\n </div>\n {/* fallback on empty space here to perserve the height for timestamp div */}\n {showTimestamp && (\n <div className={timestampCssClasses}>\n {message.timestamp ? formatTimestamp(message.timestamp) : \" \"}\n </div>\n )}\n </div>\n </div>\n );\n}\n\n/**\n * Formats message's timestamp from \"2023-05-18T19:33:34.553Z\" to \"7:33 PM\"\n *\n * @param timestamp - the timestamp to convert from\n * @returns formatted timestamp\n */\nfunction defaultFormatTimestamp(timestamp: string): string {\n return new Date(timestamp).toLocaleString(undefined, {\n hour: \"numeric\",\n minute: \"numeric\",\n hour12: true,\n });\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -56,6 +56,8 @@ export interface ChatInputProps {
|
|
|
56
56
|
sendButtonIcon?: JSX.Element;
|
|
57
57
|
/** CSS classes for customizing the component styling. */
|
|
58
58
|
customCssClasses?: ChatInputCssClasses;
|
|
59
|
+
/** A callback which is called when user sends a message. */
|
|
60
|
+
onSend?: (message: string) => void;
|
|
59
61
|
}
|
|
60
62
|
|
|
61
63
|
/**
|
|
@@ -76,6 +78,7 @@ export function ChatInput({
|
|
|
76
78
|
handleError,
|
|
77
79
|
sendButtonIcon = <ArrowIcon />,
|
|
78
80
|
customCssClasses,
|
|
81
|
+
onSend,
|
|
79
82
|
}: ChatInputProps) {
|
|
80
83
|
const chat = useChatActions();
|
|
81
84
|
const [input, setInput] = useState("");
|
|
@@ -91,8 +94,10 @@ export function ChatInput({
|
|
|
91
94
|
? chat.streamNextMessage(input)
|
|
92
95
|
: chat.getNextMessage(input);
|
|
93
96
|
setInput("");
|
|
94
|
-
res.
|
|
95
|
-
|
|
97
|
+
res.then(() => {
|
|
98
|
+
onSend?.(input)
|
|
99
|
+
}).catch((e) => (handleError ? handleError(e) : defaultHandleApiError(e)));
|
|
100
|
+
}, [chat, input, handleError, defaultHandleApiError, stream, onSend]);
|
|
96
101
|
|
|
97
102
|
const handleKeyDown = useCallback(
|
|
98
103
|
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
MessageSuggestionCssClasses,
|
|
22
22
|
MessageSuggestions,
|
|
23
23
|
} from "./MessageSuggestions";
|
|
24
|
+
import { Markdown, MarkdownCssClasses } from "./Markdown";
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* The CSS class interface for the {@link ChatPanel} component.
|
|
@@ -35,6 +36,7 @@ export interface ChatPanelCssClasses {
|
|
|
35
36
|
inputCssClasses?: ChatInputCssClasses;
|
|
36
37
|
messageBubbleCssClasses?: MessageBubbleCssClasses;
|
|
37
38
|
messageSuggestionClasses?: MessageSuggestionCssClasses;
|
|
39
|
+
footer?: string;
|
|
38
40
|
}
|
|
39
41
|
|
|
40
42
|
const builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(
|
|
@@ -47,6 +49,7 @@ const builtInCssClasses: ChatPanelCssClasses = withStylelessCssClasses(
|
|
|
47
49
|
messageBubbleCssClasses: {
|
|
48
50
|
topContainer: "first:mt-4",
|
|
49
51
|
},
|
|
52
|
+
footer: "text-center text-slate-400 rounded-b-3xl px-4 pb-4 text-[12px]",
|
|
50
53
|
}
|
|
51
54
|
);
|
|
52
55
|
|
|
@@ -60,6 +63,8 @@ export interface ChatPanelProps
|
|
|
60
63
|
Omit<ChatInputProps, "customCssClasses"> {
|
|
61
64
|
/** A header to render at the top of the panel. */
|
|
62
65
|
header?: ReactNode;
|
|
66
|
+
/** A footer markdown string to render at the bottom of the panel. */
|
|
67
|
+
footer?: string;
|
|
63
68
|
/**
|
|
64
69
|
* CSS classes for customizing the component styling.
|
|
65
70
|
*/
|
|
@@ -69,6 +74,8 @@ export interface ChatPanelProps
|
|
|
69
74
|
* can click on instead of typing their own.
|
|
70
75
|
*/
|
|
71
76
|
messageSuggestions?: string[];
|
|
77
|
+
/** A callback which is called when user clicks a link. */
|
|
78
|
+
onLinkClick?: (href?: string) => void;
|
|
72
79
|
}
|
|
73
80
|
|
|
74
81
|
/**
|
|
@@ -81,8 +88,15 @@ export interface ChatPanelProps
|
|
|
81
88
|
* @param props - {@link ChatPanelProps}
|
|
82
89
|
*/
|
|
83
90
|
export function ChatPanel(props: ChatPanelProps) {
|
|
84
|
-
const {
|
|
85
|
-
|
|
91
|
+
const {
|
|
92
|
+
header,
|
|
93
|
+
footer,
|
|
94
|
+
customCssClasses,
|
|
95
|
+
stream,
|
|
96
|
+
handleError,
|
|
97
|
+
messageSuggestions,
|
|
98
|
+
onLinkClick,
|
|
99
|
+
} = props;
|
|
86
100
|
const messages = useChatState((state) => state.conversation.messages);
|
|
87
101
|
const loading = useChatState((state) => state.conversation.isLoading);
|
|
88
102
|
const suggestedReplies = useChatState(
|
|
@@ -136,6 +150,14 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
136
150
|
return (message) => (messagesRef.current[index] = message);
|
|
137
151
|
}, []);
|
|
138
152
|
|
|
153
|
+
const footerCssClasses: MarkdownCssClasses = useMemo(
|
|
154
|
+
() => ({
|
|
155
|
+
container: cssClasses.footer,
|
|
156
|
+
link: "cursor-pointer hover:underline text-blue-600",
|
|
157
|
+
}),
|
|
158
|
+
[cssClasses]
|
|
159
|
+
);
|
|
160
|
+
|
|
139
161
|
return (
|
|
140
162
|
<div className="yext-chat w-full h-full">
|
|
141
163
|
<div className={cssClasses.container}>
|
|
@@ -148,6 +170,7 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
148
170
|
{...props}
|
|
149
171
|
customCssClasses={cssClasses.messageBubbleCssClasses}
|
|
150
172
|
message={message}
|
|
173
|
+
onLinkClick={onLinkClick}
|
|
151
174
|
/>
|
|
152
175
|
</div>
|
|
153
176
|
))}
|
|
@@ -163,6 +186,14 @@ export function ChatPanel(props: ChatPanelProps) {
|
|
|
163
186
|
)}
|
|
164
187
|
<ChatInput {...props} customCssClasses={cssClasses.inputCssClasses} />
|
|
165
188
|
</div>
|
|
189
|
+
{footer && (
|
|
190
|
+
<Markdown
|
|
191
|
+
content={footer}
|
|
192
|
+
linkClickEvent="WEBSITE"
|
|
193
|
+
onLinkClick={onLinkClick}
|
|
194
|
+
customCssClasses={footerCssClasses}
|
|
195
|
+
/>
|
|
196
|
+
)}
|
|
166
197
|
</div>
|
|
167
198
|
</div>
|
|
168
199
|
);
|
|
@@ -133,6 +133,7 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
133
133
|
showUnreadNotification = false,
|
|
134
134
|
ctaLabel,
|
|
135
135
|
title,
|
|
136
|
+
footer,
|
|
136
137
|
} = props;
|
|
137
138
|
|
|
138
139
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
@@ -224,6 +225,7 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
224
225
|
customCssClasses={cssClasses.headerCssClasses}
|
|
225
226
|
/>
|
|
226
227
|
}
|
|
228
|
+
footer={footer}
|
|
227
229
|
/>
|
|
228
230
|
)}
|
|
229
231
|
</div>
|
|
@@ -254,7 +256,9 @@ export function ChatPopUp(props: ChatPopUpProps) {
|
|
|
254
256
|
onClick={onClick}
|
|
255
257
|
className={
|
|
256
258
|
cssClasses.button +
|
|
257
|
-
(showHeartBeatAnimation && !!numUnreadMessages
|
|
259
|
+
(showHeartBeatAnimation && !!numUnreadMessages
|
|
260
|
+
? " animate-heartbeat"
|
|
261
|
+
: "")
|
|
258
262
|
}
|
|
259
263
|
>
|
|
260
264
|
{openPanelButtonIcon ?? (
|
|
@@ -7,6 +7,7 @@ import rehypeRaw from "rehype-raw";
|
|
|
7
7
|
import rehypeSanitize from "rehype-sanitize";
|
|
8
8
|
import React, { useMemo } from "react";
|
|
9
9
|
import { useReportAnalyticsEvent } from "../hooks/useReportAnalyticsEvent";
|
|
10
|
+
import { useComposedCssClasses } from "../hooks/useComposedCssClasses";
|
|
10
11
|
|
|
11
12
|
// The Remark and Rehype plugins to use in conjunction with ReactMarkdown.
|
|
12
13
|
const unifiedPlugins: { remark?: PluggableList; rehype: PluggableList } = {
|
|
@@ -19,13 +20,34 @@ const unifiedPlugins: { remark?: PluggableList; rehype: PluggableList } = {
|
|
|
19
20
|
],
|
|
20
21
|
};
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The CSS class interface for the Markdown component.
|
|
25
|
+
*
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
export interface MarkdownCssClasses {
|
|
29
|
+
container?: string;
|
|
30
|
+
link?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const builtInCssClasses: MarkdownCssClasses = {
|
|
34
|
+
link: "cursor-pointer",
|
|
35
|
+
};
|
|
36
|
+
|
|
22
37
|
interface MarkdownProps {
|
|
23
38
|
/** Stringified markdown. */
|
|
24
39
|
content: string;
|
|
25
40
|
/** The response ID correlates to the current message. */
|
|
26
41
|
responseId?: string;
|
|
27
|
-
/**
|
|
28
|
-
|
|
42
|
+
/** CSS classes for customizing the component styling. */
|
|
43
|
+
customCssClasses?: MarkdownCssClasses;
|
|
44
|
+
/**
|
|
45
|
+
* Action to report for analytics event when a link is clicked.
|
|
46
|
+
* Defaults to 'CHAT_LINK_CLICK'.
|
|
47
|
+
*/
|
|
48
|
+
linkClickEvent?: "WEBSITE" | "CHAT_LINK_CLICK";
|
|
49
|
+
/** A callback which is called when a link is clicked. */
|
|
50
|
+
onLinkClick?: (href?: string) => void;
|
|
29
51
|
}
|
|
30
52
|
|
|
31
53
|
/**
|
|
@@ -37,18 +59,26 @@ interface MarkdownProps {
|
|
|
37
59
|
*
|
|
38
60
|
* @internal
|
|
39
61
|
*/
|
|
40
|
-
export function Markdown({
|
|
62
|
+
export function Markdown({
|
|
63
|
+
content,
|
|
64
|
+
responseId,
|
|
65
|
+
customCssClasses,
|
|
66
|
+
linkClickEvent = "CHAT_LINK_CLICK",
|
|
67
|
+
onLinkClick,
|
|
68
|
+
}: MarkdownProps) {
|
|
41
69
|
const reportAnalyticsEvent = useReportAnalyticsEvent();
|
|
70
|
+
const cssClasses = useComposedCssClasses(builtInCssClasses, customCssClasses);
|
|
42
71
|
|
|
43
72
|
const components: ReactMarkdownOptions["components"] = useMemo(() => {
|
|
44
73
|
const createClickHandlerFn = (href?: string) => () => {
|
|
45
74
|
reportAnalyticsEvent({
|
|
46
|
-
action:
|
|
75
|
+
action: linkClickEvent,
|
|
47
76
|
destinationUrl: href,
|
|
48
77
|
chat: {
|
|
49
78
|
responseId,
|
|
50
79
|
},
|
|
51
80
|
});
|
|
81
|
+
onLinkClick?.(href)
|
|
52
82
|
};
|
|
53
83
|
return {
|
|
54
84
|
a: ({ node: _, children, ...props }) => {
|
|
@@ -58,18 +88,18 @@ export function Markdown({ content, responseId, className }: MarkdownProps) {
|
|
|
58
88
|
onClick={createClickHandlerFn(props.href)}
|
|
59
89
|
target="_blank"
|
|
60
90
|
rel="noopener noreferrer"
|
|
61
|
-
className=
|
|
91
|
+
className={cssClasses.link}
|
|
62
92
|
>
|
|
63
93
|
{children}
|
|
64
94
|
</a>
|
|
65
95
|
);
|
|
66
96
|
},
|
|
67
97
|
};
|
|
68
|
-
}, [reportAnalyticsEvent, responseId]);
|
|
98
|
+
}, [reportAnalyticsEvent, linkClickEvent, responseId, cssClasses, onLinkClick]);
|
|
69
99
|
|
|
70
100
|
return (
|
|
71
101
|
<ReactMarkdown
|
|
72
|
-
className={
|
|
102
|
+
className={cssClasses.container}
|
|
73
103
|
children={content}
|
|
74
104
|
remarkPlugins={unifiedPlugins.remark}
|
|
75
105
|
rehypePlugins={unifiedPlugins.rehype}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useMemo } from "react";
|
|
2
2
|
import { Message, MessageSource } from "@yext/chat-headless-react";
|
|
3
3
|
import { useComposedCssClasses } from "../hooks";
|
|
4
4
|
import { twMerge } from "tailwind-merge";
|
|
5
|
-
import { Markdown } from "./Markdown";
|
|
5
|
+
import { Markdown, MarkdownCssClasses } from "./Markdown";
|
|
6
6
|
import { withStylelessCssClasses } from "../utils/withStylelessCssClasses";
|
|
7
7
|
import { FeedbackButtons, FeedbackButtonsCssClasses } from "./FeedbackButtons";
|
|
8
8
|
|
|
@@ -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(
|
|
@@ -117,6 +120,13 @@ export function MessageBubble({
|
|
|
117
120
|
: cssClasses.timestamp__bot
|
|
118
121
|
);
|
|
119
122
|
|
|
123
|
+
const markdownCssClasses: MarkdownCssClasses = useMemo(
|
|
124
|
+
() => ({
|
|
125
|
+
container: textCssClasses,
|
|
126
|
+
}),
|
|
127
|
+
[textCssClasses]
|
|
128
|
+
);
|
|
129
|
+
|
|
120
130
|
return (
|
|
121
131
|
<div className={cssClasses.topContainer}>
|
|
122
132
|
<div className={subContainerCssClasses}>
|
|
@@ -130,7 +140,8 @@ export function MessageBubble({
|
|
|
130
140
|
<Markdown
|
|
131
141
|
content={message.text}
|
|
132
142
|
responseId={message.responseId}
|
|
133
|
-
|
|
143
|
+
customCssClasses={markdownCssClasses}
|
|
144
|
+
onLinkClick={onLinkClick}
|
|
134
145
|
/>
|
|
135
146
|
</div>
|
|
136
147
|
{/* fallback on empty space here to perserve the height for timestamp div */}
|