UNPKG

@progress/kendo-react-conversational-ui

Version:

React Chat component allows the user to participate in chat sessions with users or chat bots. KendoReact Conversational UI components

370 lines (369 loc) 10.4 kB
/** * @license *------------------------------------------------------------------------------------------- * Copyright © 2025 Progress Software Corporation. All rights reserved. * Licensed under commercial license. See LICENSE.md in the package root for more information *------------------------------------------------------------------------------------------- */ import { validatePackage as je, getLicenseMessage as Ke, Keys as g, classNames as ze, WatermarkOverlay as He } from "@progress/kendo-react-common"; import o from "prop-types"; import * as e from "react"; import { convertMsgsToViewItems as $e } from "../ViewItem.mjs"; import Je from "./ActionGroup.mjs"; import Qe from "./AttachmentGroup.mjs"; import Xe from "./DateMarker.mjs"; import Ye from "./MessageGroup.mjs"; import Ze from "./NewMessage.mjs"; import _e from "./Header.mjs"; import et from "./elements/PinnedMessage.mjs"; import tt from "./Notification.mjs"; import st from "./ChatContext.mjs"; import { ChatMessage as nt } from "./ChatMessage.mjs"; import { packageMetadata as ye } from "../../package-metadata.mjs"; import { ariaLabelMessageList as Ce, messages as ke, chatCopyNotification as Me } from "../../messages/index.mjs"; import { useLocalization as ot } from "@progress/kendo-react-intl"; import { isStandardMessageFormat as at, mapDataArrayToMessages as rt } from "../utils/fieldMapping.mjs"; import { useChatScroll as it } from "../utils/scroll.mjs"; const Te = e.forwardRef((Se, d) => { const { messages: u = [], messageTemplate: F, attachmentTemplate: h, width: xe, height: Ee, onSendMessage: p, onSuggestionClick: I, onInputValueChange: b, onActionExecute: y, dir: C, messageBox: Re, placeholder: Fe, className: v, message: w = nt, authorId: r, suggestions: A, suggestionTemplate: Ie, inputValue: be, headerTemplate: m, timestampTemplate: N, statusTemplate: L, allowMessageCollapse: D, messageToolbarActions: P, messageContextMenuActions: B, fileActions: V, onUnpin: ve, messageWidthMode: U, onToolbarAction: G, onContextMenuAction: q, onFileAction: O, onDownload: W, speechToTextConfig: j, uploadConfig: K, sendButtonConfig: we, textField: z, statusField: H, authorIdField: $, authorNameField: J, authorImageUrlField: Q, authorImageAltTextField: X, idField: Y, timestampField: Z, filesField: _, attachmentsField: ee, replyToIdField: te, isDeletedField: se, isPinnedField: ne, typingField: oe, suggestedActionsField: ae, ...Ae } = Se, [i, k] = e.useState(null), [M, Ne] = e.useState(!1), [Le, re] = e.useState(!1), [ie, le] = e.useState(null), [l, ce] = e.useState(null), T = e.useRef(null), ue = e.useRef(null), S = e.useRef(null), me = e.useRef(null), x = e.useRef(void 0), E = e.useRef(!1), R = e.useRef(null), pe = e.useRef(""), a = e.useMemo(() => { if (!u || u.length === 0) return []; const t = { textField: z, statusField: H, authorIdField: $, authorNameField: J, authorImageUrlField: Q, authorImageAltTextField: X, idField: Y, timestampField: Z, filesField: _, attachmentsField: ee, replyToIdField: te, isDeletedField: se, isPinnedField: ne, typingField: oe, suggestedActionsField: ae }; return !Object.values(t).some((n) => n !== void 0) && at(u[0]) ? u.map((n) => ({ ...n, timestamp: n.timestamp ? new Date(n.timestamp) : n.timestamp })) : rt(u, t); }, [ u, z, H, $, J, Q, X, Y, Z, _, ee, te, se, ne, oe, ae ]); e.useEffect(() => { if (r === pe.current && R.current) { ce(R.current); return; } if (r && a.length > 0) { const t = a.find( (n) => n.author && (n.author.id === r || n.author.id === String(r)) ), s = (t == null ? void 0 : t.author) || { id: r }; R.current = s, pe.current = r, ce(s); } }, [r, a]); const c = e.useMemo(() => a.length > 0 ? $e(a) : [], [a]), De = e.useMemo(() => !je(ye, { component: "Chat" }), []), Pe = Ke(ye), de = e.useMemo(() => a.find((t) => t.isPinned), [a]), fe = ot(); it({ viewItemsWrapperRef: S, isKeyboardNavigationActiveRef: E, processedMessages: a, suggestions: A }); const Be = e.useCallback(() => { clearTimeout(x.current); }, []), Ve = e.useCallback(() => { x.current = window.setTimeout(() => { k(null); }, 0); }, []), f = e.useCallback((t) => { k(t); }, []), Ue = e.useCallback( (t) => { let s = null; const n = i !== null ? i : c.lastSelectionIndex; t.keyCode === g.up ? n === null ? s = 0 : n > 0 && (s = n - 1) : t.keyCode === g.down ? n === null ? s = 0 : n < c.lastSelectionIndex && (s = n + 1) : t.keyCode === g.home ? s = 0 : t.keyCode === g.end && (s = c.lastSelectionIndex), s !== null && (k(s), E.current = !0, t.preventDefault()); }, [i, c] ), ge = e.useCallback( (t, s, n) => { var he; if (y && y({ action: { value: t.value, title: t.title, type: t.type }, syntheticEvent: s, nativeEvent: s.nativeEvent, target: s.currentTarget }), !s.isDefaultPrevented()) { switch (t.type) { case "reply": p && p({ message: { id: "", author: l, text: t.value, timestamp: /* @__PURE__ */ new Date() }, syntheticEvent: s, nativeEvent: s.nativeEvent, target: s.currentTarget }); break; case "call": window.open("tel:" + t.value); break; case "openUrl": window.open(t.value); break; } (he = me.current) == null || he.focusInput(); } }, [y, p, l] ), Ge = e.useCallback(() => ze("k-chat", v, { "k-rtl": M }), [v, M]), qe = e.useCallback(() => { const t = c.length - 1; return c.map((s, n) => s.type === "date-marker" ? /* @__PURE__ */ e.createElement(Xe, { item: s, key: s.trackBy, timestampTemplate: N }) : s.type === "message-group" ? /* @__PURE__ */ e.createElement( Ye, { group: s, itemTemplate: F, attachmentTemplate: h, user: l, selectedItemIndex: i, onRequestSelection: f, isLastGroup: n === t, key: s.messages[0].selectionIndex, message: w, allowMessageCollapse: D, messageToolbarActions: P, messageContextMenuActions: B, messageWidthMode: U } ) : s.type === "attachment-group" ? /* @__PURE__ */ e.createElement( Qe, { group: s, itemTemplate: h, onRequestSelection: f, selected: s.selectionIndex === i, isLastGroup: n === t, key: s.selectionIndex } ) : s.type === "action-group" ? /* @__PURE__ */ e.createElement( Je, { group: s, onActionExecute: ge, onRequestSelection: f, selected: s.selectionIndex === i, key: s.selectionIndex } ) : null); }, [ c, N, F, h, l, i, f, w, D, P, B, U, ge ]), Oe = e.useCallback(() => m ? typeof m == "function" ? m() : m : null, [m]); e.useEffect(() => { const t = C !== void 0 ? C === "rtl" : T.current && getComputedStyle(T.current).direction === "rtl"; Ne(!!t); }, [C]), e.useEffect(() => () => { clearTimeout(x.current); }, []); const We = e.useMemo( () => ({ replyToMessage: ie, setReplyToMessage: le, messages: a, user: l, internalScrollContainerRef: ue, messageListScrollContainerRef: S, onToolbarAction: G, onContextMenuAction: q, onFileAction: O, setShowCopyNotification: re, onDownload: W, fileActions: V, speechToTextConfig: j, uploadConfig: K, statusTemplate: L }), [ ie, le, a, l, G, q, O, re, W, V, j, K, L ] ); return /* @__PURE__ */ e.createElement(st.Provider, { value: We }, /* @__PURE__ */ e.createElement( "div", { style: { width: xe, height: Ee, position: "relative", overflow: "hidden" }, onKeyDown: Ue, className: Ge(), ref: (t) => { T.current = t, ue.current = t, typeof d == "function" ? d(t) : d && (d.current = t); }, ...Ae }, m !== void 0 && /* @__PURE__ */ e.createElement(_e, null, Oe()), de && /* @__PURE__ */ e.createElement(et, { message: de, onUnpin: ve, user: l }), /* @__PURE__ */ e.createElement( "div", { className: "k-message-list k-avatars", onBlur: Ve, onFocus: Be, role: "log", "aria-label": fe.toLanguageString( Ce, ke[Ce] ), "aria-live": "polite", ref: S }, /* @__PURE__ */ e.createElement("div", { className: "k-message-list-content" }, qe()) ), /* @__PURE__ */ e.createElement( Ze, { onSendMessage: (t, s) => { p && p({ message: t, syntheticEvent: s, nativeEvent: s.nativeEvent, target: s.currentTarget }); }, onSuggestionClick: (t) => { I && I(t); }, isDirectionRightToLeft: M, ref: me, placeholder: Fe, MessageBox: Re, suggestions: A, suggestionTemplate: Ie, inputValue: be, onInputValueChange: (t) => { b && b(t); }, onInputClick: () => { E.current = !1; }, sendButtonConfig: we } ), /* @__PURE__ */ e.createElement( tt, { show: Le, text: fe.toLanguageString( Me, ke[Me] ) } ), De && /* @__PURE__ */ e.createElement(He, { message: Pe }) )); }); Te.displayName = "Chat"; Te.propTypes = { messages: o.arrayOf(o.object), user: o.object, messageTemplate: o.any, attachmentTemplate: o.any, width: o.oneOfType([o.string, o.number]), onSendMessage: o.func, onActionExecute: o.func, dir: o.string, messageBox: o.any }; export { Te as Chat };