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