@banbox/chat 1.0.18 → 1.0.19
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/dist/index.cjs +134 -109
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +134 -109
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/ChatImagePreviewModal.tsx +3 -3
- package/src/chat/SinglePopup.tsx +76 -29
- package/src/ui/AttachmentPreviewStrip.tsx +17 -12
- package/src/ui/ChatComposerBar.tsx +21 -13
- package/src/ui/ChatFooter.tsx +5 -5
- package/src/ui/ChatInquiryBar.tsx +1 -1
- package/src/ui/ChatThreadItem.tsx +1 -1
- package/src/ui/MessageHoverActions.tsx +2 -2
- package/src/ui/ReplyCard.tsx +1 -1
- package/src/ui/Select.tsx +2 -1
- package/src/ui/drop-up/BusinessCardDropup.tsx +1 -1
- package/src/ui/drop-up/EmojiDropup.tsx +3 -3
- package/src/ui/message-items/ChatBubbleAudio.tsx +1 -1
- package/src/ui/message-items/ChatBubbleFiles.tsx +8 -2
package/package.json
CHANGED
|
@@ -83,7 +83,7 @@ const ChatImagePreviewModal: FC<ChatImagePreviewModalProps> = ({
|
|
|
83
83
|
<AnimatePresence>
|
|
84
84
|
{isOpen && total > 0 && (
|
|
85
85
|
<motion.div
|
|
86
|
-
className="fixed inset-0 z-
|
|
86
|
+
className="fixed inset-0 z-10010 flex items-center justify-center"
|
|
87
87
|
initial={{ opacity: 0, backgroundColor: "rgba(0,0,0,0)" }}
|
|
88
88
|
animate={{ opacity: 1, backgroundColor: "rgba(0,0,0,0.55)" }}
|
|
89
89
|
exit={{ opacity: 0, backgroundColor: "rgba(0,0,0,0)" }}
|
|
@@ -129,7 +129,7 @@ const ChatImagePreviewModal: FC<ChatImagePreviewModalProps> = ({
|
|
|
129
129
|
type="button"
|
|
130
130
|
onClick={goPrev}
|
|
131
131
|
disabled={!hasPrev}
|
|
132
|
-
className={`absolute left-0 top-1/2 -translate-y-1/2 flex h-[100px] items-center rounded-tr-[3px] rounded-br-[3px] p-[7px] backdrop-blur-[2px] shadow-[3px_0px_6px_0px_rgba(0,0,0,0.1)] transition-
|
|
132
|
+
className={`absolute left-0 top-1/2 -translate-y-1/2 flex h-[100px] items-center rounded-tr-[3px] rounded-br-[3px] p-[7px] backdrop-blur-[2px] shadow-[3px_0px_6px_0px_rgba(0,0,0,0.1)] transition-all duration-200 ${hasPrev ? "cursor-pointer opacity-100 hover:bg-white hover:shadow-[3px_0px_10px_0px_rgba(0,0,0,0.18)]" : "cursor-default opacity-30"}`}
|
|
133
133
|
style={{ backgroundColor: "rgba(255,255,255,0.7)" }}
|
|
134
134
|
aria-label="Previous image"
|
|
135
135
|
>
|
|
@@ -149,7 +149,7 @@ const ChatImagePreviewModal: FC<ChatImagePreviewModalProps> = ({
|
|
|
149
149
|
type="button"
|
|
150
150
|
onClick={goNext}
|
|
151
151
|
disabled={!hasNext}
|
|
152
|
-
className={`absolute right-0 top-1/2 -translate-y-1/2 flex h-[100px] items-center rounded-tl-[3px] rounded-bl-[3px] p-[7px] backdrop-blur-[2px] shadow-[-3px_0px_6px_0px_rgba(0,0,0,0.1)] transition-
|
|
152
|
+
className={`absolute right-0 top-1/2 -translate-y-1/2 flex h-[100px] items-center rounded-tl-[3px] rounded-bl-[3px] p-[7px] backdrop-blur-[2px] shadow-[-3px_0px_6px_0px_rgba(0,0,0,0.1)] transition-all duration-200 ${hasNext ? "cursor-pointer opacity-100 hover:bg-white hover:shadow-[-3px_0px_10px_0px_rgba(0,0,0,0.18)]" : "cursor-default opacity-30"}`}
|
|
153
153
|
style={{ backgroundColor: "rgba(255,255,255,0.7)" }}
|
|
154
154
|
aria-label="Next image"
|
|
155
155
|
>
|
package/src/chat/SinglePopup.tsx
CHANGED
|
@@ -22,9 +22,10 @@ import type { ChatAdapter, ChatUICallbacks } from "../adapter/types";
|
|
|
22
22
|
import type { Message, MessageRef, Reference, Thread } from "../types";
|
|
23
23
|
import type { ChatTheme } from "./InboxPopup";
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
function coalesceThreadId(
|
|
26
|
+
reference: Reference | undefined,
|
|
27
|
+
threads: Thread[],
|
|
28
|
+
): string {
|
|
28
29
|
if (!reference?.id) return threads[0]?.id ?? "";
|
|
29
30
|
const refId = reference.id;
|
|
30
31
|
// Priority: exact thread.id match → orderId match → inquiryId match → first thread
|
|
@@ -51,16 +52,34 @@ function toRef(m: Message): MessageRef {
|
|
|
51
52
|
|
|
52
53
|
/** Maps the first letter of a name to a deterministic background colour. */
|
|
53
54
|
const avatarBgByInitial: Record<string, string> = {
|
|
54
|
-
a: "#FFE4E4",
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
a: "#FFE4E4",
|
|
56
|
+
b: "#E4F0FF",
|
|
57
|
+
c: "#E4FFE9",
|
|
58
|
+
d: "#FFF4E4",
|
|
59
|
+
e: "#F4E4FF",
|
|
60
|
+
f: "#FFE4F4",
|
|
61
|
+
g: "#E4FFFF",
|
|
62
|
+
h: "#FFFFE4",
|
|
63
|
+
i: "#E4E4FF",
|
|
64
|
+
j: "#FFE9E4",
|
|
65
|
+
k: "#E4FFE4",
|
|
66
|
+
l: "#FFE4EA",
|
|
67
|
+
m: "#E8E4FF",
|
|
68
|
+
n: "#E4F8FF",
|
|
69
|
+
o: "#FFF0E4",
|
|
70
|
+
p: "#F0FFE4",
|
|
71
|
+
q: "#FFE4F8",
|
|
72
|
+
r: "#E4FFEC",
|
|
73
|
+
s: "#FFEEE4",
|
|
74
|
+
t: "#E4EAFF",
|
|
75
|
+
u: "#F8FFE4",
|
|
76
|
+
v: "#FFE4EE",
|
|
77
|
+
w: "#E4FFFA",
|
|
78
|
+
x: "#FFF8E4",
|
|
79
|
+
y: "#EAE4FF",
|
|
59
80
|
z: "#E4FFF0",
|
|
60
81
|
};
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
64
83
|
export type SinglePopupProps = {
|
|
65
84
|
adapter: ChatAdapter;
|
|
66
85
|
uiCallbacks?: ChatUICallbacks;
|
|
@@ -75,12 +94,19 @@ export type SinglePopupProps = {
|
|
|
75
94
|
/* ══════════════════════════════════════════════════
|
|
76
95
|
Component
|
|
77
96
|
══════════════════════════════════════════════════ */
|
|
78
|
-
const SinglePopup: React.FC<SinglePopupProps> = ({
|
|
97
|
+
const SinglePopup: React.FC<SinglePopupProps> = ({
|
|
98
|
+
adapter,
|
|
99
|
+
uiCallbacks,
|
|
100
|
+
theme,
|
|
101
|
+
footerActions,
|
|
102
|
+
}) => {
|
|
79
103
|
const { close, reference } = useChatUI();
|
|
80
104
|
const { isOpen: isGalleryOpen, closeGallery } = useGallery();
|
|
81
105
|
|
|
82
106
|
// ── Threads — subscribed so real-API updates (new msg, pin, delete) are reflected
|
|
83
|
-
const [threads, setThreads] = React.useState<Thread[]>(() =>
|
|
107
|
+
const [threads, setThreads] = React.useState<Thread[]>(() =>
|
|
108
|
+
adapter.threads.list(reference),
|
|
109
|
+
);
|
|
84
110
|
React.useEffect(() => {
|
|
85
111
|
// Refresh once on mount (covers any gap between render and subscribe)
|
|
86
112
|
setThreads(adapter.threads.list(reference));
|
|
@@ -97,7 +123,9 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
97
123
|
);
|
|
98
124
|
const [activeId] = React.useState<string>(initialThreadId);
|
|
99
125
|
|
|
100
|
-
const activeThread: Thread | undefined = threads.find(
|
|
126
|
+
const activeThread: Thread | undefined = threads.find(
|
|
127
|
+
(t) => t.id === activeId,
|
|
128
|
+
);
|
|
101
129
|
const isVerified = activeThread?.badge === true;
|
|
102
130
|
|
|
103
131
|
const meta = (reference?.meta ?? {}) as {
|
|
@@ -114,13 +142,11 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
114
142
|
const avatarSrc: string | undefined =
|
|
115
143
|
meta.avatarSrc ?? activeThread?.avatarSrc;
|
|
116
144
|
|
|
117
|
-
const initial =
|
|
145
|
+
const initial =
|
|
118
146
|
meta.initial ??
|
|
119
147
|
activeThread?.avatarText ??
|
|
120
|
-
(activeThread?.title ?? meta.title ?? "?").charAt(0).toUpperCase()
|
|
121
|
-
);
|
|
122
|
-
const avatarBg =
|
|
123
|
-
avatarBgByInitial[initial.toLowerCase()] ?? "#E4F0FF";
|
|
148
|
+
(activeThread?.title ?? meta.title ?? "?").charAt(0).toUpperCase();
|
|
149
|
+
const avatarBg = avatarBgByInitial[initial.toLowerCase()] ?? "#E4F0FF";
|
|
124
150
|
|
|
125
151
|
const title = meta.title ?? activeThread?.title ?? "Unknown";
|
|
126
152
|
const online = meta.online ?? activeThread?.online ?? false;
|
|
@@ -130,7 +156,9 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
130
156
|
activeId ? adapter.messages.list(activeId) : [],
|
|
131
157
|
);
|
|
132
158
|
const [scrollKey, setScrollKey] = React.useState<number>(Date.now());
|
|
133
|
-
const [replyTo, setReplyTo] = React.useState<MessageRef | undefined>(
|
|
159
|
+
const [replyTo, setReplyTo] = React.useState<MessageRef | undefined>(
|
|
160
|
+
undefined,
|
|
161
|
+
);
|
|
134
162
|
const [showDelete, setShowDelete] = React.useState(false);
|
|
135
163
|
|
|
136
164
|
React.useEffect(() => {
|
|
@@ -149,7 +177,10 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
149
177
|
}, [activeId, adapter]);
|
|
150
178
|
|
|
151
179
|
const handleConfirmDelete = React.useCallback(() => {
|
|
152
|
-
if (!activeId) {
|
|
180
|
+
if (!activeId) {
|
|
181
|
+
setShowDelete(false);
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
153
184
|
adapter.threads.delete(activeId);
|
|
154
185
|
uiCallbacks?.showToast?.({
|
|
155
186
|
type: "success",
|
|
@@ -253,29 +284,45 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme,
|
|
|
253
284
|
id={m.id}
|
|
254
285
|
mine={mine}
|
|
255
286
|
time={m.time ?? ""}
|
|
256
|
-
authorInitial={
|
|
287
|
+
authorInitial={
|
|
288
|
+
typeof m.author === "string" ? m.author : "U"
|
|
289
|
+
}
|
|
257
290
|
text={m.text ?? m.content}
|
|
258
|
-
businessCard={
|
|
259
|
-
|
|
291
|
+
businessCard={
|
|
292
|
+
m.businessCard as Parameters<
|
|
293
|
+
typeof ChatMessageItem
|
|
294
|
+
>[0]["businessCard"]
|
|
295
|
+
}
|
|
296
|
+
addressCard={
|
|
297
|
+
m.addressCard as Parameters<
|
|
298
|
+
typeof ChatMessageItem
|
|
299
|
+
>[0]["addressCard"]
|
|
300
|
+
}
|
|
260
301
|
images={m.images}
|
|
261
302
|
files={m.files}
|
|
262
303
|
audio={m.audio}
|
|
263
304
|
replyTo={m.replyTo}
|
|
264
305
|
initialSrc={m.avatarSrc}
|
|
265
306
|
showStatus={isLast}
|
|
266
|
-
status={
|
|
307
|
+
status={
|
|
308
|
+
activeThread?.status?.kind === "seen"
|
|
309
|
+
? "Seen"
|
|
310
|
+
: "Delivered"
|
|
311
|
+
}
|
|
267
312
|
onReply={() => setReplyTo(toRef(m))}
|
|
268
313
|
/>
|
|
269
314
|
);
|
|
270
315
|
})}
|
|
271
|
-
|
|
272
|
-
{/* Typing indicator */}
|
|
273
|
-
<div className="flex items-center justify-start">
|
|
274
|
-
<TypingIndicator />
|
|
275
|
-
</div>
|
|
276
316
|
</ChatScroll>
|
|
277
317
|
</div>
|
|
278
318
|
|
|
319
|
+
{/* Typing indicator — sticky above footer */}
|
|
320
|
+
<div className="shrink-0">
|
|
321
|
+
<div className="flex items-center justify-start py-2 px-[16px]">
|
|
322
|
+
<TypingIndicator />
|
|
323
|
+
</div>
|
|
324
|
+
</div>
|
|
325
|
+
|
|
279
326
|
{/* Footer */}
|
|
280
327
|
<div className="shrink-0">
|
|
281
328
|
<ChatFooter
|
|
@@ -24,7 +24,7 @@ export const FilePreviewChip: React.FC<{
|
|
|
24
24
|
ext: string;
|
|
25
25
|
onRemove: () => void;
|
|
26
26
|
}> = ({ name, sizeMB, ext, onRemove }) => (
|
|
27
|
-
<div className="mr-2 inline-flex items-center gap-
|
|
27
|
+
<div className="mr-2 inline-flex items-center gap-2 whitespace-nowrap rounded-sm border border-[#e1e1e1] bg-white px-3 py-2 h-[65px] max-w-[185px]">
|
|
28
28
|
<div className="flex min-w-0 items-center gap-2">
|
|
29
29
|
<div className="min-w-0">
|
|
30
30
|
<div className="flex items-center gap-1">
|
|
@@ -38,25 +38,30 @@ export const FilePreviewChip: React.FC<{
|
|
|
38
38
|
</div>
|
|
39
39
|
</div>
|
|
40
40
|
</div>
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
41
|
+
<span>
|
|
42
|
+
<button
|
|
43
|
+
type="button"
|
|
44
|
+
onClick={onRemove}
|
|
45
|
+
className="grid h-[24px] w-[24px] cursor-pointer place-items-center rounded-full bg-white text-[#3D3D3D] shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:bg-black/5"
|
|
46
|
+
title="Remove"
|
|
47
|
+
aria-label="Remove file"
|
|
48
|
+
>
|
|
49
|
+
<ChatXIcon className="h-[18px] w-[18px]" />
|
|
50
|
+
</button>
|
|
51
|
+
</span>
|
|
50
52
|
</div>
|
|
51
53
|
);
|
|
52
54
|
|
|
53
|
-
const ImageThumb: React.FC<{ url: string; onRemove: () => void }> = ({
|
|
55
|
+
const ImageThumb: React.FC<{ url: string; onRemove: () => void }> = ({
|
|
56
|
+
url,
|
|
57
|
+
onRemove,
|
|
58
|
+
}) => (
|
|
54
59
|
<div className="relative mr-2 inline-block h-[65px] w-[65px] rounded-sm border border-[#e1e1e1] bg-[#F7F7F7]">
|
|
55
60
|
<img src={url} alt="" className="h-full w-full object-cover rounded-sm" />
|
|
56
61
|
<button
|
|
57
62
|
type="button"
|
|
58
63
|
onClick={onRemove}
|
|
59
|
-
className="absolute left-1/2 top-1/2 z-10 grid h-6 w-6 -translate-x-1/2 -translate-y-1/2 place-items-center rounded-full bg-black/30 text-[#3D3D3D] shadow-[0px_2px_4px_0px_#A5A3AE4D]"
|
|
64
|
+
className="absolute left-1/2 top-1/2 z-10 grid h-6 w-6 -translate-x-1/2 -translate-y-1/2 cursor-pointer place-items-center rounded-full bg-black/30 text-[#3D3D3D] shadow-[0px_2px_4px_0px_#A5A3AE4D]"
|
|
60
65
|
aria-label="Remove image"
|
|
61
66
|
title="Remove image"
|
|
62
67
|
>
|
|
@@ -3,7 +3,12 @@
|
|
|
3
3
|
|
|
4
4
|
import clsx from "clsx";
|
|
5
5
|
import React, { useRef, useState } from "react";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ArrowSendAngleIcon,
|
|
8
|
+
ArrowSendIcon,
|
|
9
|
+
RecordMicIcon,
|
|
10
|
+
ChatXIcon,
|
|
11
|
+
} from "../icons";
|
|
7
12
|
|
|
8
13
|
type Props = {
|
|
9
14
|
recording: boolean;
|
|
@@ -63,18 +68,18 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
63
68
|
return (
|
|
64
69
|
<div className="flex w-full items-stretch gap-2">
|
|
65
70
|
<div
|
|
66
|
-
className="w-full rounded-
|
|
71
|
+
className="w-full rounded-[6px] p-px transition-[background] duration-200"
|
|
67
72
|
style={{
|
|
68
73
|
background: isActiveBorder ? activeGradient : idleGradient,
|
|
69
74
|
}}
|
|
70
75
|
>
|
|
71
|
-
<div className="flex min-h-[50px] w-full items-center justify-between rounded-[
|
|
76
|
+
<div className="flex min-h-[50px] w-full items-center justify-between rounded-[5px] bg-white">
|
|
72
77
|
<div className="flex w-full items-center justify-between p-[3px]">
|
|
73
78
|
{!isTyping ? (
|
|
74
79
|
<button
|
|
75
80
|
type="button"
|
|
76
81
|
onClick={startRecording}
|
|
77
|
-
className="grid h-[44px] w-[44px] place-items-center rounded-xs bg-[#f8f8f8] text-[#ff5301] hover:brightness-95"
|
|
82
|
+
className="grid h-[44px] w-[44px] cursor-pointer place-items-center rounded-xs bg-[#f8f8f8] text-[#ff5301] hover:brightness-95"
|
|
78
83
|
title="Record voice"
|
|
79
84
|
aria-label="Record voice"
|
|
80
85
|
>
|
|
@@ -117,7 +122,9 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
117
122
|
/>
|
|
118
123
|
</div>
|
|
119
124
|
|
|
120
|
-
{!canSendArrow &&
|
|
125
|
+
{!canSendArrow && (
|
|
126
|
+
<div className="grid h-full w-px place-items-center bg-[#E7E7E7]" />
|
|
127
|
+
)}
|
|
121
128
|
|
|
122
129
|
<div className="px-2">
|
|
123
130
|
{isTyping ? (
|
|
@@ -127,7 +134,7 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
127
134
|
type="button"
|
|
128
135
|
onClick={sendText}
|
|
129
136
|
className={clsx(
|
|
130
|
-
"ms-1 grid h-[40px] w-[40px] place-items-center rounded-full text-[#ff5301] hover:bg-[#f8f8f8]",
|
|
137
|
+
"ms-1 grid h-[40px] w-[40px] cursor-pointer place-items-center rounded-full text-[#ff5301] hover:bg-[#f8f8f8]",
|
|
131
138
|
)}
|
|
132
139
|
title={hasAttachments ? "Send attachments" : "Send"}
|
|
133
140
|
aria-label="Send"
|
|
@@ -144,7 +151,7 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
144
151
|
disabled={!hasAttachments}
|
|
145
152
|
className={clsx(
|
|
146
153
|
"ms-1 grid h-[40px] w-[40px] place-items-center rounded-full hover:bg-[#f8f8f8]",
|
|
147
|
-
hasAttachments ? "text-[#ff5301]" : "text-[#B9C3D4]",
|
|
154
|
+
hasAttachments ? "text-[#ff5301] cursor-pointer" : "text-[#B9C3D4] cursor-not-allowed",
|
|
148
155
|
)}
|
|
149
156
|
title={hasAttachments ? "Send attachments" : "Send"}
|
|
150
157
|
aria-label="Send"
|
|
@@ -163,7 +170,10 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
163
170
|
// Recording state UI
|
|
164
171
|
return (
|
|
165
172
|
<div className="flex w-full items-stretch gap-2">
|
|
166
|
-
<div
|
|
173
|
+
<div
|
|
174
|
+
className="w-full rounded-sm p-px"
|
|
175
|
+
style={{ background: activeGradient }}
|
|
176
|
+
>
|
|
167
177
|
<div className="flex min-h-[50px] w-full items-center justify-between rounded-[3px] bg-white">
|
|
168
178
|
<button
|
|
169
179
|
type="button"
|
|
@@ -174,9 +184,7 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
174
184
|
<RecordMicIcon
|
|
175
185
|
className={clsx(
|
|
176
186
|
"h-6 w-6",
|
|
177
|
-
seconds % 2 === 0
|
|
178
|
-
? "text-[#929292]"
|
|
179
|
-
: "text-[#ff5301]",
|
|
187
|
+
seconds % 2 === 0 ? "text-[#929292]" : "text-[#ff5301]",
|
|
180
188
|
)}
|
|
181
189
|
/>
|
|
182
190
|
</button>
|
|
@@ -187,7 +195,7 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
187
195
|
<button
|
|
188
196
|
type="button"
|
|
189
197
|
onClick={() => stopRecording(false)}
|
|
190
|
-
className="grid h-8 w-8 place-items-center rounded-full text-[#3D3D3D] hover:bg-black/5"
|
|
198
|
+
className="grid h-8 w-8 cursor-pointer place-items-center rounded-full text-[#3D3D3D] hover:bg-black/5"
|
|
191
199
|
title="Discard"
|
|
192
200
|
aria-label="Discard recording"
|
|
193
201
|
>
|
|
@@ -199,7 +207,7 @@ const ChatComposerBar: React.FC<Props> = ({
|
|
|
199
207
|
<button
|
|
200
208
|
type="button"
|
|
201
209
|
onClick={() => stopRecording(true)}
|
|
202
|
-
className="grid h-10 w-[40px] place-items-center rounded-full text-[#ff5301]"
|
|
210
|
+
className="grid h-10 w-[40px] cursor-pointer place-items-center rounded-full text-[#ff5301]"
|
|
203
211
|
title="Send"
|
|
204
212
|
aria-label="Send"
|
|
205
213
|
>
|
package/src/ui/ChatFooter.tsx
CHANGED
|
@@ -378,7 +378,7 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
378
378
|
return (
|
|
379
379
|
<span key={a.key} className="relative inline-flex">
|
|
380
380
|
<Tooltip text={a.title}>
|
|
381
|
-
<button ref={emojiBtnRef} type="button" onClick={() => setShowEmoji(
|
|
381
|
+
<button ref={emojiBtnRef} type="button" onClick={() => setShowEmoji(true)} className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
382
382
|
<span>{a.icon}</span>
|
|
383
383
|
</button>
|
|
384
384
|
</Tooltip>
|
|
@@ -388,7 +388,7 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
388
388
|
if (isTranslate) {
|
|
389
389
|
return (
|
|
390
390
|
<Tooltip key={a.key} text={a.title}>
|
|
391
|
-
<button type="button" onClick={() => setShowTranslate(true)} className="flex h-6 w-6 items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
391
|
+
<button type="button" onClick={() => setShowTranslate(true)} className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
392
392
|
<span>{a.icon}</span>
|
|
393
393
|
</button>
|
|
394
394
|
</Tooltip>
|
|
@@ -398,7 +398,7 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
398
398
|
return (
|
|
399
399
|
<span key={a.key} className="relative inline-flex">
|
|
400
400
|
<Tooltip text={a.title}>
|
|
401
|
-
<button ref={bizBtnRef} type="button" onClick={() => setShowBiz((v) => !v)} className="flex h-6 w-6 items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
401
|
+
<button ref={bizBtnRef} type="button" onClick={() => setShowBiz((v) => !v)} className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
402
402
|
<span>{a.icon}</span>
|
|
403
403
|
</button>
|
|
404
404
|
</Tooltip>
|
|
@@ -409,7 +409,7 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
409
409
|
return (
|
|
410
410
|
<span key={a.key} className="relative inline-flex">
|
|
411
411
|
<Tooltip text={a.title}>
|
|
412
|
-
<button ref={addrBtnRef} type="button" onClick={() => setShowAddress(true)} className="flex h-6 w-6 items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
412
|
+
<button ref={addrBtnRef} type="button" onClick={() => setShowAddress(true)} className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
413
413
|
<span>{a.icon}</span>
|
|
414
414
|
</button>
|
|
415
415
|
</Tooltip>
|
|
@@ -418,7 +418,7 @@ const ChatFooter: React.FC<Props> = ({
|
|
|
418
418
|
}
|
|
419
419
|
return (
|
|
420
420
|
<Tooltip key={a.key} text={a.title}>
|
|
421
|
-
<button type="button" onClick={isAttach ? () => fileInputRef.current?.click() : a.onClick} className="flex h-6 w-6 items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
421
|
+
<button type="button" onClick={isAttach ? () => fileInputRef.current?.click() : a.onClick} className="flex h-6 w-6 cursor-pointer items-center justify-center rounded-full text-[#0F0F0F] hover:bg-[#F4F6F8]">
|
|
422
422
|
<span>{a.icon}</span>
|
|
423
423
|
</button>
|
|
424
424
|
</Tooltip>
|
|
@@ -33,7 +33,7 @@ const ChatInquiryBar: React.FC<Props> = ({ id, onView, className, label, buttonL
|
|
|
33
33
|
<button
|
|
34
34
|
type="button"
|
|
35
35
|
onClick={onView}
|
|
36
|
-
className="group relative inline-flex w-fit items-center justify-end text-xs font-medium text-black"
|
|
36
|
+
className="group relative inline-flex w-fit cursor-pointer items-center justify-end text-xs font-medium text-black"
|
|
37
37
|
>
|
|
38
38
|
<span className="flex items-center transition-opacity duration-300 ease-in-out group-hover:opacity-0">
|
|
39
39
|
<span>{buttonLabel}</span>
|
|
@@ -63,7 +63,7 @@ const ChatThreadItem: React.FC<Props> = ({
|
|
|
63
63
|
<button
|
|
64
64
|
onClick={onClick}
|
|
65
65
|
className={clsx(
|
|
66
|
-
"relative w-full text-left px-5 py-2 hover:bg-[#f8f8f8] focus:outline-none h-[75px]",
|
|
66
|
+
"relative w-full cursor-pointer text-left px-5 py-2 hover:bg-[#f8f8f8] focus:outline-none h-[75px]",
|
|
67
67
|
active && "bg-[#f8f8f8]",
|
|
68
68
|
className,
|
|
69
69
|
)}
|
|
@@ -87,7 +87,7 @@ const MessageHoverActions: React.FC<Props> = ({
|
|
|
87
87
|
onReply?.();
|
|
88
88
|
}}
|
|
89
89
|
className={clsx(
|
|
90
|
-
"inline-flex h-[22px] w-[22px] items-center justify-center rounded-sm bg-white text-[#2c2c2c] shadow-[0_1px_3px_rgba(0,0,0,0.08)] hover:bg-[#f8f8f8]",
|
|
90
|
+
"inline-flex h-[22px] w-[22px] cursor-pointer items-center justify-center rounded-sm bg-white text-[#2c2c2c] shadow-[0_1px_3px_rgba(0,0,0,0.08)] hover:bg-[#f8f8f8]",
|
|
91
91
|
isActive("replay") && "bg-[#636363] text-white",
|
|
92
92
|
)}
|
|
93
93
|
>
|
|
@@ -118,7 +118,7 @@ const MessageHoverActions: React.FC<Props> = ({
|
|
|
118
118
|
onTranslate?.();
|
|
119
119
|
}}
|
|
120
120
|
className={clsx(
|
|
121
|
-
"inline-flex h-[22px] w-[22px] items-center justify-center rounded-sm bg-white text-[#2c2c2c] shadow-[0_1px_3px_rgba(0,0,0,0.08)] hover:bg-[#f8f8f8]",
|
|
121
|
+
"inline-flex h-[22px] w-[22px] cursor-pointer items-center justify-center rounded-sm bg-white text-[#2c2c2c] shadow-[0_1px_3px_rgba(0,0,0,0.08)] hover:bg-[#f8f8f8]",
|
|
122
122
|
isActive("translate") && "bg-[#636363]! text-white",
|
|
123
123
|
)}
|
|
124
124
|
>
|
package/src/ui/ReplyCard.tsx
CHANGED
|
@@ -129,7 +129,7 @@ const ReplyCard: React.FC<Props> = ({ refMsg, onClose, compact, className, jumpO
|
|
|
129
129
|
<button
|
|
130
130
|
type="button"
|
|
131
131
|
onClick={onClose}
|
|
132
|
-
className="absolute right-3 top-1/2 -translate-y-1/2 grid h-8 w-8 place-items-center rounded-full bg-white text-[#3D3D3D] shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:text-[#FF5300]"
|
|
132
|
+
className="absolute right-3 top-1/2 -translate-y-1/2 grid h-8 w-8 cursor-pointer place-items-center rounded-full bg-white text-[#3D3D3D] shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:text-[#FF5300]"
|
|
133
133
|
title="Remove"
|
|
134
134
|
aria-label="Remove"
|
|
135
135
|
>
|
package/src/ui/Select.tsx
CHANGED
|
@@ -37,6 +37,7 @@ const Select: React.FC<Props> = ({ options, value, onChange, placeholder = "Sele
|
|
|
37
37
|
className={cn(
|
|
38
38
|
"flex w-full items-center justify-between rounded-[4px] border border-[#cacaca] bg-white px-3 text-[13px] text-left",
|
|
39
39
|
disabled && "cursor-not-allowed opacity-50",
|
|
40
|
+
!disabled && "cursor-pointer",
|
|
40
41
|
)}
|
|
41
42
|
style={{ height: size }}
|
|
42
43
|
>
|
|
@@ -54,7 +55,7 @@ const Select: React.FC<Props> = ({ options, value, onChange, placeholder = "Sele
|
|
|
54
55
|
key={opt.value}
|
|
55
56
|
type="button"
|
|
56
57
|
className={cn(
|
|
57
|
-
"flex w-full items-center px-3 py-2 text-[13px] text-left hover:bg-black/5",
|
|
58
|
+
"flex w-full cursor-pointer items-center px-3 py-2 text-[13px] text-left hover:bg-black/5",
|
|
58
59
|
opt.value === value && "bg-black/5 font-medium",
|
|
59
60
|
)}
|
|
60
61
|
onClick={() => {
|
|
@@ -171,7 +171,7 @@ const BusinessCardDropup = ({
|
|
|
171
171
|
type="button"
|
|
172
172
|
onClick={onClose}
|
|
173
173
|
aria-label="Close"
|
|
174
|
-
className="grid h-8 w-8 place-items-center rounded-full hover:bg-black/5"
|
|
174
|
+
className="grid h-8 w-8 cursor-pointer place-items-center rounded-full hover:bg-black/5"
|
|
175
175
|
>
|
|
176
176
|
<ChatXIcon className="h-6 w-6" />
|
|
177
177
|
</button>
|
|
@@ -74,7 +74,8 @@ const EmojiDropup: React.FC<Props> = ({ open, onClose, onSelect, anchorRef, clas
|
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
76
|
const onDoc = (e: MouseEvent) => {
|
|
77
|
-
|
|
77
|
+
const target = e.target as Node;
|
|
78
|
+
if (panelRef.current && !panelRef.current.contains(target) && (!anchorRef?.current || !anchorRef.current.contains(target))) {
|
|
78
79
|
onClose();
|
|
79
80
|
}
|
|
80
81
|
};
|
|
@@ -111,10 +112,9 @@ const EmojiDropup: React.FC<Props> = ({ open, onClose, onSelect, anchorRef, clas
|
|
|
111
112
|
<button
|
|
112
113
|
key={i}
|
|
113
114
|
type="button"
|
|
114
|
-
className="emoji-dropup__item"
|
|
115
|
+
className="emoji-dropup__item cursor-pointer"
|
|
115
116
|
onClick={() => {
|
|
116
117
|
onSelect(e);
|
|
117
|
-
onClose();
|
|
118
118
|
}}
|
|
119
119
|
aria-label={`Insert ${e}`}
|
|
120
120
|
>
|
|
@@ -153,7 +153,7 @@ const ChatBubbleAudio: React.FC<{ mine: boolean; audio: ChatAudio }> = ({ mine,
|
|
|
153
153
|
aria-label={playing ? "Pause" : "Play"}
|
|
154
154
|
onClick={toggle}
|
|
155
155
|
className={clsx(
|
|
156
|
-
"grid h-7 w-[34px] place-items-center rounded-md transition-colors",
|
|
156
|
+
"grid h-7 w-[34px] cursor-pointer place-items-center rounded-md transition-colors",
|
|
157
157
|
mine ? "bg-[#F1F1F1] text-[#00486F]" : "bg-[#F1F1F1] text-[#00486F]",
|
|
158
158
|
)}
|
|
159
159
|
>
|
|
@@ -34,8 +34,14 @@ const FileChip: React.FC<{ file: ChatFile }> = ({ file }) => (
|
|
|
34
34
|
<div className="flex min-w-0 items-center gap-2">
|
|
35
35
|
<div className="min-w-0">
|
|
36
36
|
<div className="flex items-center gap-1">
|
|
37
|
-
<
|
|
38
|
-
|
|
37
|
+
<span>
|
|
38
|
+
<FileIcon
|
|
39
|
+
className={clsx("h-[18px] w-[18px]", extColor(file.ext))}
|
|
40
|
+
/>
|
|
41
|
+
</span>{" "}
|
|
42
|
+
<div className="truncate text-xs font-normal text-black">
|
|
43
|
+
{file.name}
|
|
44
|
+
</div>
|
|
39
45
|
</div>
|
|
40
46
|
<div className="flex items-center gap-2 text-[10px] text-[#636363] mt-2">
|
|
41
47
|
<span>{file.sizeMB.toFixed(1)} MB</span>
|