@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
JavaScript
/**
* @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
};