@banbox/chat 1.0.3 → 1.0.4
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 +477 -295
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +475 -293
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/chat/InboxPopup.tsx +208 -115
- package/src/chat/SinglePopup.tsx +55 -40
- package/src/ui/chat/ChatHeader.tsx +32 -24
- package/src/ui/chat/ChatListHeader.tsx +157 -156
- package/src/ui/chat/ChatScroll.tsx +71 -64
- package/src/ui/chat/TypingIndicator.tsx +36 -17
package/src/chat/SinglePopup.tsx
CHANGED
|
@@ -13,12 +13,11 @@ import ChatMessageItem from "../ui/chat/ChatMessageItem";
|
|
|
13
13
|
import ChatScroll from "../ui/chat/ChatScroll";
|
|
14
14
|
import TypingIndicator from "../ui/chat/TypingIndicator";
|
|
15
15
|
|
|
16
|
-
import type { Thread, Message, MessageRef, Reference } from "../types";
|
|
17
16
|
import type { ChatAdapter, ChatUICallbacks } from "../adapter/types";
|
|
17
|
+
import type { Message, MessageRef, Reference, Thread } from "../types";
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
────────────────────────────────────────────────────────────── */
|
|
19
|
+
const GRADIENT_BORDER =
|
|
20
|
+
"linear-gradient(236.83deg, rgba(51,201,212,0.3) 0.4%, rgba(39,83,251,0.3) 30.28%, rgba(39,83,251,0.3) 50.2%, rgba(39,83,251,0.3) 65.14%, rgba(235,67,255,0.3) 100%)";
|
|
22
21
|
|
|
23
22
|
function coalesceThreadId(reference: Reference | undefined, threads: Thread[]): string {
|
|
24
23
|
const referenceId = reference?.id;
|
|
@@ -45,21 +44,11 @@ function toRef(m: Message): MessageRef {
|
|
|
45
44
|
};
|
|
46
45
|
}
|
|
47
46
|
|
|
48
|
-
/* ─────────────────────────────────────────────────────────────
|
|
49
|
-
Props
|
|
50
|
-
────────────────────────────────────────────────────────────── */
|
|
51
|
-
|
|
52
47
|
export type SinglePopupProps = {
|
|
53
|
-
/** The unified data adapter */
|
|
54
48
|
adapter: ChatAdapter;
|
|
55
|
-
/** UI-level callbacks (toast, navigation) */
|
|
56
49
|
uiCallbacks?: ChatUICallbacks;
|
|
57
50
|
};
|
|
58
51
|
|
|
59
|
-
/* ─────────────────────────────────────────────────────────────
|
|
60
|
-
Component
|
|
61
|
-
────────────────────────────────────────────────────────────── */
|
|
62
|
-
|
|
63
52
|
const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
64
53
|
const { close, reference } = useChatUI();
|
|
65
54
|
|
|
@@ -86,14 +75,12 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
|
86
75
|
const online = meta.online ?? activeThread?.online ?? true;
|
|
87
76
|
const subtitle = meta.subtitle ?? "Customer";
|
|
88
77
|
|
|
89
|
-
/* ─── Messages ─── */
|
|
90
78
|
const [messages, setMessages] = React.useState<Message[]>(() =>
|
|
91
79
|
activeId ? adapter.messages.list(activeId) : [],
|
|
92
80
|
);
|
|
93
81
|
const [scrollKey, setScrollKey] = React.useState<number>(Date.now());
|
|
94
82
|
const [replyTo, setReplyTo] = React.useState<MessageRef | undefined>(undefined);
|
|
95
83
|
|
|
96
|
-
// Subscribe to real-time updates
|
|
97
84
|
React.useEffect(() => {
|
|
98
85
|
if (!activeId || !adapter.messages.subscribe) return;
|
|
99
86
|
const unsub = adapter.messages.subscribe(activeId, () => {
|
|
@@ -109,46 +96,54 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
|
109
96
|
setReplyTo(undefined);
|
|
110
97
|
}, [activeId, adapter]);
|
|
111
98
|
|
|
112
|
-
const statusText = activeThread?.status?.kind === "seen" ? "Seen" : "Delivered";
|
|
113
|
-
|
|
114
|
-
/* ─── Unused callbacks acknowledged ─── */
|
|
115
99
|
void uiCallbacks;
|
|
116
100
|
|
|
117
101
|
return (
|
|
118
|
-
<div
|
|
102
|
+
<div style={{ position: "fixed", bottom: 16, right: 16, zIndex: 10002 }}>
|
|
119
103
|
{/* Backdrop */}
|
|
120
104
|
<motion.button
|
|
121
105
|
aria-label="Close chat"
|
|
122
106
|
onClick={close}
|
|
123
|
-
|
|
107
|
+
style={{ position: "fixed", inset: 0, background: "transparent", border: "none", cursor: "auto" }}
|
|
124
108
|
initial={{ opacity: 0 }}
|
|
125
109
|
animate={{ opacity: 1 }}
|
|
126
110
|
exit={{ opacity: 0 }}
|
|
127
111
|
transition={{ type: "tween", duration: 0.25 }}
|
|
128
112
|
/>
|
|
129
113
|
|
|
130
|
-
{/* Outer gradient
|
|
114
|
+
{/* Outer gradient border */}
|
|
131
115
|
<motion.div
|
|
132
116
|
role="dialog"
|
|
133
117
|
aria-modal="true"
|
|
134
|
-
className="relative h-[650px] w-[450px] rounded-[20px] p-[2px]"
|
|
135
118
|
style={{
|
|
119
|
+
position: "relative",
|
|
120
|
+
height: 650,
|
|
121
|
+
width: 450,
|
|
122
|
+
borderRadius: 20,
|
|
123
|
+
padding: 2,
|
|
136
124
|
boxShadow: "0px 2px 12px 0px #3B33331A",
|
|
137
|
-
background:
|
|
138
|
-
"linear-gradient(236.83deg, rgba(51, 201, 212, 0.3) 0.4%, rgba(39, 83, 251, 0.3) 30.28%, rgba(39, 83, 251, 0.3) 50.2%, rgba(39, 83, 251, 0.3) 65.14%, rgba(235, 67, 255, 0.3) 100%)",
|
|
125
|
+
background: GRADIENT_BORDER,
|
|
139
126
|
}}
|
|
140
127
|
initial={{ x: "110%" }}
|
|
141
128
|
animate={{ x: 0 }}
|
|
142
129
|
exit={{ x: "110%" }}
|
|
143
130
|
transition={{ type: "tween", duration: 0.3, ease: "easeOut" }}
|
|
144
131
|
>
|
|
145
|
-
{/* Inner card */}
|
|
132
|
+
{/* Inner white card */}
|
|
146
133
|
<div
|
|
147
|
-
|
|
148
|
-
|
|
134
|
+
style={{
|
|
135
|
+
display: "flex",
|
|
136
|
+
flexDirection: "column",
|
|
137
|
+
height: "100%",
|
|
138
|
+
width: "100%",
|
|
139
|
+
overflow: "hidden",
|
|
140
|
+
borderRadius: 18,
|
|
141
|
+
backgroundColor: "#fff",
|
|
142
|
+
overscrollBehavior: "contain",
|
|
143
|
+
}}
|
|
149
144
|
>
|
|
150
|
-
{/* Header */}
|
|
151
|
-
<div
|
|
145
|
+
{/* Header — 64px */}
|
|
146
|
+
<div style={{ height: 64, flexShrink: 0 }}>
|
|
152
147
|
<ChatHeader
|
|
153
148
|
left={
|
|
154
149
|
<ChatIdentity
|
|
@@ -165,7 +160,19 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
|
165
160
|
<button
|
|
166
161
|
type="button"
|
|
167
162
|
onClick={close}
|
|
168
|
-
|
|
163
|
+
style={{
|
|
164
|
+
display: "flex",
|
|
165
|
+
alignItems: "center",
|
|
166
|
+
justifyContent: "center",
|
|
167
|
+
height: 34,
|
|
168
|
+
width: 34,
|
|
169
|
+
borderRadius: "50%",
|
|
170
|
+
backgroundColor: "#fff",
|
|
171
|
+
color: "#000",
|
|
172
|
+
border: "none",
|
|
173
|
+
boxShadow: "0px 2px 4px 0px #A5A3AE4D",
|
|
174
|
+
cursor: "pointer",
|
|
175
|
+
}}
|
|
169
176
|
>
|
|
170
177
|
<ChatXIcon className="h-6 w-6" />
|
|
171
178
|
</button>
|
|
@@ -173,9 +180,13 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
|
173
180
|
/>
|
|
174
181
|
</div>
|
|
175
182
|
|
|
176
|
-
{/* Messages */}
|
|
177
|
-
<div
|
|
178
|
-
<ChatScroll
|
|
183
|
+
{/* Messages — flex-1 */}
|
|
184
|
+
<div style={{ flex: 1, minHeight: 0, position: "relative" }}>
|
|
185
|
+
<ChatScroll
|
|
186
|
+
bottomAlignWhenShort={false}
|
|
187
|
+
scrollKey={scrollKey}
|
|
188
|
+
style={{ height: "100%", overflowY: "auto" }}
|
|
189
|
+
>
|
|
179
190
|
{messages.map((m, idx) => {
|
|
180
191
|
const mine = m.author === "you";
|
|
181
192
|
const isLast = idx === messages.length - 1;
|
|
@@ -187,29 +198,33 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
|
|
|
187
198
|
time={m.time ?? ""}
|
|
188
199
|
authorInitial={typeof m.author === "string" ? m.author : "U"}
|
|
189
200
|
text={m.text ?? m.content}
|
|
190
|
-
businessCard={
|
|
191
|
-
|
|
201
|
+
businessCard={
|
|
202
|
+
m.businessCard as Parameters<typeof ChatMessageItem>[0]["businessCard"]
|
|
203
|
+
}
|
|
204
|
+
addressCard={
|
|
205
|
+
m.addressCard as Parameters<typeof ChatMessageItem>[0]["addressCard"]
|
|
206
|
+
}
|
|
192
207
|
images={m.images}
|
|
193
208
|
files={m.files}
|
|
194
209
|
audio={m.audio}
|
|
195
210
|
replyTo={m.replyTo}
|
|
196
211
|
initialSrc={m.avatarSrc}
|
|
197
212
|
showStatus={isLast}
|
|
198
|
-
status={
|
|
213
|
+
status={activeThread?.status?.kind === "seen" ? "Seen" : "Delivered"}
|
|
199
214
|
onReply={() => setReplyTo(toRef(m))}
|
|
200
215
|
/>
|
|
201
216
|
);
|
|
202
217
|
})}
|
|
203
218
|
|
|
204
|
-
{/* Typing */}
|
|
205
|
-
<div
|
|
219
|
+
{/* Typing indicator */}
|
|
220
|
+
<div style={{ display: "flex", alignItems: "center", justifyContent: "flex-start" }}>
|
|
206
221
|
<TypingIndicator />
|
|
207
222
|
</div>
|
|
208
223
|
</ChatScroll>
|
|
209
224
|
</div>
|
|
210
225
|
|
|
211
226
|
{/* Footer */}
|
|
212
|
-
<div
|
|
227
|
+
<div style={{ flexShrink: 0 }}>
|
|
213
228
|
<ChatFooter
|
|
214
229
|
variant="single"
|
|
215
230
|
replyTo={replyTo}
|
|
@@ -1,24 +1,32 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
1
|
+
"use client";
|
|
2
|
+
import React from "react";
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
left: React.ReactNode;
|
|
6
|
+
right?: React.ReactNode;
|
|
7
|
+
below?: React.ReactNode;
|
|
8
|
+
className?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export default function ChatHeader({ left, right, below }: Props) {
|
|
12
|
+
return (
|
|
13
|
+
<div>
|
|
14
|
+
<div
|
|
15
|
+
style={{
|
|
16
|
+
borderBottom: "1px solid #e1e1e1",
|
|
17
|
+
height: 64,
|
|
18
|
+
display: "flex",
|
|
19
|
+
alignItems: "flex-start",
|
|
20
|
+
justifyContent: "space-between",
|
|
21
|
+
paddingLeft: 16,
|
|
22
|
+
paddingRight: 16,
|
|
23
|
+
paddingTop: 10,
|
|
24
|
+
}}
|
|
25
|
+
>
|
|
26
|
+
<div style={{ display: "flex", alignItems: "flex-start", gap: 12 }}>{left}</div>
|
|
27
|
+
{right}
|
|
28
|
+
</div>
|
|
29
|
+
{below && <>{below}</>}
|
|
30
|
+
</div>
|
|
31
|
+
);
|
|
32
|
+
}
|
|
@@ -1,156 +1,157 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const [
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type { Variants } from "framer-motion";
|
|
4
|
+
import { AnimatePresence, motion } from "framer-motion";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { ChatSearchIcon, ChatXIcon, MessageIcon } from "../../icons";
|
|
7
|
+
|
|
8
|
+
type Props = {
|
|
9
|
+
className?: string;
|
|
10
|
+
onClose?: () => void;
|
|
11
|
+
onSearchChange?: (value: string) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const ChatListHeader: React.FC<Props> = ({ onClose, onSearchChange }) => {
|
|
15
|
+
const [searching, setSearching] = React.useState(false);
|
|
16
|
+
const [q, setQ] = React.useState("");
|
|
17
|
+
const inputRef = React.useRef<HTMLInputElement>(null);
|
|
18
|
+
|
|
19
|
+
React.useEffect(() => {
|
|
20
|
+
const timer = searching
|
|
21
|
+
? setTimeout(() => { inputRef.current?.focus(); }, 220)
|
|
22
|
+
: undefined;
|
|
23
|
+
return () => { clearTimeout(timer); };
|
|
24
|
+
}, [searching]);
|
|
25
|
+
|
|
26
|
+
React.useEffect(() => {
|
|
27
|
+
if (!searching) return;
|
|
28
|
+
const onKey = (e: KeyboardEvent) => {
|
|
29
|
+
if (e.key === "Escape") {
|
|
30
|
+
setSearching(false);
|
|
31
|
+
setQ("");
|
|
32
|
+
onSearchChange?.("");
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
window.addEventListener("keydown", onKey);
|
|
36
|
+
return () => { window.removeEventListener("keydown", onKey); };
|
|
37
|
+
}, [searching, onSearchChange]);
|
|
38
|
+
|
|
39
|
+
const variants: Variants = {
|
|
40
|
+
inFromRight: { opacity: 1, x: 0, transition: { duration: 0.18, ease: [0.16, 1, 0.3, 1] } },
|
|
41
|
+
outToLeft: { opacity: 0, x: -24, transition: { duration: 0.16, ease: [0.4, 0, 1, 1] } },
|
|
42
|
+
inFromLeft: { opacity: 1, x: 0, transition: { duration: 0.18, ease: [0.16, 1, 0.3, 1] } },
|
|
43
|
+
outToRight: { opacity: 0, x: 24, transition: { duration: 0.16, ease: [0.4, 0, 1, 1] } },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const btnStyle: React.CSSProperties = {
|
|
47
|
+
height: 36,
|
|
48
|
+
width: 36,
|
|
49
|
+
display: "flex",
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
justifyContent: "center",
|
|
52
|
+
borderRadius: "50%",
|
|
53
|
+
border: "none",
|
|
54
|
+
background: "transparent",
|
|
55
|
+
cursor: "pointer",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div
|
|
60
|
+
style={{
|
|
61
|
+
height: 64,
|
|
62
|
+
borderBottom: "1px solid #ededed",
|
|
63
|
+
display: "flex",
|
|
64
|
+
alignItems: "center",
|
|
65
|
+
paddingLeft: 20,
|
|
66
|
+
paddingRight: 20,
|
|
67
|
+
}}
|
|
68
|
+
>
|
|
69
|
+
<AnimatePresence initial={false} mode="wait">
|
|
70
|
+
{!searching ? (
|
|
71
|
+
<motion.div
|
|
72
|
+
key="normal"
|
|
73
|
+
style={{ display: "flex", width: "100%", alignItems: "center", justifyContent: "space-between" }}
|
|
74
|
+
initial={{ opacity: 0, x: -24 }}
|
|
75
|
+
animate="inFromLeft"
|
|
76
|
+
exit="outToLeft"
|
|
77
|
+
variants={variants}
|
|
78
|
+
>
|
|
79
|
+
{/* Title */}
|
|
80
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8, color: "#2c2c2c" }}>
|
|
81
|
+
<MessageIcon className="w-6 h-6" />
|
|
82
|
+
<span style={{ fontSize: 22, fontWeight: 600 }}>Messenger</span>
|
|
83
|
+
</div>
|
|
84
|
+
|
|
85
|
+
{/* Actions */}
|
|
86
|
+
<div style={{ display: "flex", alignItems: "center", gap: 8 }}>
|
|
87
|
+
<button title="Search" onClick={() => setSearching(true)} style={btnStyle}>
|
|
88
|
+
<ChatSearchIcon className="w-5 h-5" />
|
|
89
|
+
</button>
|
|
90
|
+
<button title="Close" onClick={onClose} style={btnStyle}>
|
|
91
|
+
<ChatXIcon className="w-6 h-6" />
|
|
92
|
+
</button>
|
|
93
|
+
</div>
|
|
94
|
+
</motion.div>
|
|
95
|
+
) : (
|
|
96
|
+
<motion.div
|
|
97
|
+
key="search"
|
|
98
|
+
style={{ display: "flex", width: "100%", alignItems: "center", gap: 12 }}
|
|
99
|
+
initial={{ opacity: 0, x: 24 }}
|
|
100
|
+
animate="inFromRight"
|
|
101
|
+
exit="outToRight"
|
|
102
|
+
variants={variants}
|
|
103
|
+
>
|
|
104
|
+
<div
|
|
105
|
+
style={{
|
|
106
|
+
display: "flex",
|
|
107
|
+
flex: 1,
|
|
108
|
+
height: 40,
|
|
109
|
+
alignItems: "center",
|
|
110
|
+
borderRadius: 9999,
|
|
111
|
+
border: "1px solid #6A6A6A",
|
|
112
|
+
backgroundColor: "#fff",
|
|
113
|
+
padding: "0 4px",
|
|
114
|
+
gap: 6,
|
|
115
|
+
}}
|
|
116
|
+
>
|
|
117
|
+
<div style={{ display: "flex", alignItems: "center", flex: 1, marginLeft: 12 }}>
|
|
118
|
+
<span style={{ marginRight: 8, color: "#929292", flexShrink: 0 }}>
|
|
119
|
+
<ChatSearchIcon className="w-5 h-5" />
|
|
120
|
+
</span>
|
|
121
|
+
<span style={{ marginRight: 8, height: 24, width: 1, flexShrink: 0, backgroundColor: "#e1e1e1" }} />
|
|
122
|
+
<input
|
|
123
|
+
ref={inputRef}
|
|
124
|
+
value={q}
|
|
125
|
+
onChange={(e) => {
|
|
126
|
+
setQ(e.target.value);
|
|
127
|
+
onSearchChange?.(e.target.value);
|
|
128
|
+
}}
|
|
129
|
+
placeholder="Search"
|
|
130
|
+
style={{
|
|
131
|
+
flex: 1,
|
|
132
|
+
height: "100%",
|
|
133
|
+
background: "transparent",
|
|
134
|
+
fontSize: 15,
|
|
135
|
+
outline: "none",
|
|
136
|
+
border: "none",
|
|
137
|
+
color: "#2c2c2c",
|
|
138
|
+
}}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
142
|
+
<button
|
|
143
|
+
title="Close search"
|
|
144
|
+
onClick={() => { setSearching(false); setQ(""); onSearchChange?.(""); }}
|
|
145
|
+
style={{ ...btnStyle, height: 32, width: 32 }}
|
|
146
|
+
>
|
|
147
|
+
<ChatXIcon className="w-5 h-5" />
|
|
148
|
+
</button>
|
|
149
|
+
</div>
|
|
150
|
+
</motion.div>
|
|
151
|
+
)}
|
|
152
|
+
</AnimatePresence>
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export default ChatListHeader;
|