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

277 lines (276 loc) 7.93 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 { classNames as v } from "@progress/kendo-react-common"; import * as e from "react"; import { Button as N, SpeechToTextButton as re } from "@progress/kendo-react-buttons"; import { Upload as ce } from "@progress/kendo-react-upload"; import { SuggestionGroup as ue } from "./SuggestionsGroup.mjs"; import { InputSuffix as ie, TextArea as me } from "@progress/kendo-react-inputs"; import { xIcon as fe, paperPlaneIcon as pe, paperclipIcon as ge } from "@progress/kendo-svg-icons"; import { useChatContext as de } from "./ChatContext.mjs"; import { useLocalization as ke } from "@progress/kendo-react-intl"; import { getDeletedMessageText as he, isAuthor as Ce } from "../utils.mjs"; import { closeReply as _, messages as E, newMessageSendButton as ee, attachFile as te, newMessageMessageInput as se } from "../../messages/index.mjs"; import be from "./elements/FileBox.mjs"; const xe = e.forwardRef((ne, ae) => { const { onSendMessage: S, isDirectionRightToLeft: B, placeholder: R, MessageBox: D, suggestions: y, onSuggestionClick: oe, inputValue: g, onInputValueChange: m, suggestionTemplate: le, sendButtonConfig: F, onInputClick: z } = ne, { replyToMessage: n, setReplyToMessage: d, user: k, messageListScrollContainerRef: i, speechToTextConfig: h, uploadConfig: M } = de(), [L] = e.useState(!1), [a, C] = e.useState(void 0), [o, w] = e.useState([]), [K, A] = e.useState(!1), f = e.useRef(null), T = e.useRef(null), l = ke(), u = e.useCallback(() => { f.current !== null && f.current.focus(); }, []); e.useEffect(() => { g !== void 0 && g !== a && (C(g), u()); }, [g, a, u]), e.useEffect(() => { n && u(); }, [n, u]), e.useLayoutEffect(() => { K && (i != null && i.current) && (i.current.scrollTop = i.current.scrollHeight - i.current.clientHeight, A(!1)); }, [K, i]); const P = e.useCallback( (t) => { const s = t.value; C(s), m && m(s); }, [m] ), p = e.useMemo( () => !!(a != null && a.trim() || o.length > 0), [a, o.length] ), b = e.useCallback( (t) => { if (t.preventDefault(), S) { const s = a; if (p) { const r = { id: "", author: k, text: s || "", timestamp: /* @__PURE__ */ new Date(), replyToId: n ? n.id : void 0, files: o.map((c) => ({ name: c.name, size: c.size, type: c.extension, rawFile: c.getRawFile ? c.getRawFile() : c })) }; d(null), S(r, t), w([]), C(""), m && m(""), A(!0); } } }, [ S, a, k, n, o, d, m, p ] ), H = e.useCallback( (t) => { if (!(t.key === "Enter" && t.shiftKey)) switch (t.key) { case "Enter": return t.preventDefault(), b(t); case "Escape": f.current && f.current.blur(); break; default: return; } }, [b] ); e.useImperativeHandle( ae, () => ({ focusInput: u }), [u] ); const U = e.useCallback((t) => { w((s) => s.filter((r) => r.name !== t)); }, []), V = e.useCallback(() => /* @__PURE__ */ e.createElement(be, { files: o, onFileRemove: U, renderInTextarea: !0 }), [o, U]), j = e.useCallback( (t, s) => t.isDeleted ? he(s, l) : t.text, [l] ), G = e.useCallback(() => { if (n) { const t = Ce(k, n); return /* @__PURE__ */ e.createElement( "div", { className: v( "k-message-reference", t ? "k-message-reference-sender" : "k-message-reference-receiver" ) }, /* @__PURE__ */ e.createElement("div", { className: "k-message-reference-content" }, j(n, t)), /* @__PURE__ */ e.createElement("span", { className: "k-spacer" }), /* @__PURE__ */ e.createElement( N, { fillMode: "flat", themeColor: "base", svgIcon: fe, onClick: () => d(null), "aria-label": l.toLanguageString(_, E[_]) } ) ); } return null; }, [n, k, j, d, l]), $ = e.useCallback( (t) => { try { const s = Array.from(t.newState); w((r) => [...r, ...s]), u(); } catch { } }, [u] ), q = e.useCallback(() => { T.current && T.current.actionElement.click(); }, []), J = e.useCallback((t) => { }, []), O = e.useCallback((t) => { const { isFinal: s, alternatives: r } = t; if (r.length > 0) { const c = r[0]; s && C((x) => (x ? `${x} ` : "") + c.transcript); } }, []), Q = e.useCallback(() => /* @__PURE__ */ e.createElement(e.Fragment, null, o.length > 0 && V(), n && /* @__PURE__ */ e.createElement("span", { className: "k-input-prefix k-input-prefix-horizontal" }, G())), [o.length, n, V, G]), W = e.useCallback((t) => { t.preventDefault(); }, []), X = e.useCallback((t) => { t.key === "Escape" && t.currentTarget.blur(); }, []), I = e.useMemo( () => /* @__PURE__ */ e.createElement( N, { rounded: "full", fillMode: "solid", themeColor: "primary", className: "k-chat-send", icon: "paper-plane", svgIcon: pe, onClick: b, onMouseDown: W, onKeyDown: X, "aria-label": l.toLanguageString(ee, E[ee]), dir: B ? "rtl" : void 0, disabled: !p, "aria-disabled": !p, ...F } ), [ b, W, X, l, B, p, F ] ), Y = e.useCallback(() => { const t = typeof h == "object" ? h : void 0, s = typeof M == "object" ? M : {}, { className: r, ...c } = s, x = v("k-hidden", r); return /* @__PURE__ */ e.createElement(ie, null, h !== !1 && /* @__PURE__ */ e.createElement( re, { fillMode: "clear", themeColor: "base", onError: J, onResult: O, ...t } ), /* @__PURE__ */ e.createElement( N, { key: "attach-file", fillMode: "clear", themeColor: "base", svgIcon: ge, onClick: q, "aria-label": l.toLanguageString(te, E[te]) } ), /* @__PURE__ */ e.createElement( ce, { ref: T, files: o, onAdd: $, multiple: !0, autoUpload: !1, ...c, className: x } ), /* @__PURE__ */ e.createElement("span", { className: "k-spacer" }), I); }, [ h, M, o, J, O, q, $, l, I ]), Z = e.useMemo( () => /* @__PURE__ */ e.createElement( me, { className: v("k-message-box", "!k-flex-col", { "k-focus": L }), resizable: "none", rows: 1, placeholder: R, value: a, onChange: P, onKeyDown: H, onClick: z, prefix: Q, suffix: Y, ref: f, "aria-label": l.toLanguageString( se, E[se] ) } ), [ L, R, a, P, H, z, Q, Y, l ] ); return /* @__PURE__ */ e.createElement("div", { className: "k-message-box-wrapper" }, y && y.length > 0 && /* @__PURE__ */ e.createElement( ue, { onSuggestionClick: oe, suggestions: y, suggestionTemplate: le } ), D ? /* @__PURE__ */ e.createElement(D, { messageInput: Z, sendButton: I }) : Z); }); xe.displayName = "NewMessage"; export { xe as default };