@banbox/chat 1.0.3 → 1.0.5
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 +214 -192
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +2273 -0
- package/dist/index.d.cts +20 -14
- package/dist/index.d.ts +20 -14
- package/dist/index.js +212 -190
- package/dist/index.js.map +1 -1
- package/package.json +9 -3
- package/src/chat/ChatRoot.tsx +15 -10
- package/src/chat/InboxPopup.tsx +97 -75
- package/src/chat/SinglePopup.tsx +40 -29
- package/src/index.ts +3 -0
- package/src/styles/index.css +231 -0
- package/src/ui/chat/ChatHeader.tsx +21 -24
- package/src/ui/chat/ChatListHeader.tsx +128 -156
- package/src/ui/chat/ChatScroll.tsx +52 -64
- package/src/ui/chat/TypingIndicator.tsx +10 -21
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @banbox/chat — Compiled CSS
|
|
3
|
+
* Import once in your app: import "@banbox/chat/dist/index.css";
|
|
4
|
+
*
|
|
5
|
+
* Theme usage:
|
|
6
|
+
* <ChatUIProvider theme="marketplace"> — orange primary (#ff5300)
|
|
7
|
+
* <ChatUIProvider theme="admin"> — black primary (#1a1a1a)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
@import "tailwindcss";
|
|
11
|
+
|
|
12
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
13
|
+
THEME: Default / Marketplace / Retailers (orange primary)
|
|
14
|
+
═══════════════════════════════════════════════════════════════ */
|
|
15
|
+
.banbox-chat-root {
|
|
16
|
+
/* Primary */
|
|
17
|
+
--color-banbox-primary: #ff5300;
|
|
18
|
+
--color-banbox-primary-active: #dc4c07;
|
|
19
|
+
--color-banbox-secondary: #2079b0;
|
|
20
|
+
--color-banbox-tertiary: #74a380;
|
|
21
|
+
|
|
22
|
+
/* Primary containers */
|
|
23
|
+
--color-banbox-primary-container: #f9d2c4;
|
|
24
|
+
--color-banbox-on-primary-container: #9e3505;
|
|
25
|
+
--color-banbox-secondary-container: #b2e0fc;
|
|
26
|
+
--color-banbox-on-secondary-container: #17547b;
|
|
27
|
+
--color-banbox-tertiary-container: #ceefd6;
|
|
28
|
+
--color-banbox-on-tertiary-container: #3b7249;
|
|
29
|
+
|
|
30
|
+
/* Surfaces */
|
|
31
|
+
--color-banbox-surface-dim: #f1f1f1;
|
|
32
|
+
--color-banbox-surface: #f2f3f7;
|
|
33
|
+
--color-banbox-surface-bright: #fffcfa;
|
|
34
|
+
--color-banbox-surface-container-lowest: #ffffff;
|
|
35
|
+
--color-banbox-surface-container-low: #fff1ec;
|
|
36
|
+
--color-banbox-surface-container: #fceae5;
|
|
37
|
+
--color-banbox-surface-container-high: #f7e4df;
|
|
38
|
+
--color-banbox-surface-container-highest: #f1dfd9;
|
|
39
|
+
|
|
40
|
+
/* Text / outline */
|
|
41
|
+
--color-banbox-on-surface: #2c2c2c;
|
|
42
|
+
--color-banbox-text-dynamic: #636363;
|
|
43
|
+
--color-banbox-outline: #e0d6cf;
|
|
44
|
+
--color-banbox-outline-variant: #a89e97;
|
|
45
|
+
|
|
46
|
+
/* Semantic colors */
|
|
47
|
+
--color-banbox-blue: #0d99ff;
|
|
48
|
+
--color-banbox-blue-deep: #1078d8;
|
|
49
|
+
--color-banbox-blue-dim-deep: #006fd6;
|
|
50
|
+
--color-banbox-blue-h: #0a7acc;
|
|
51
|
+
--color-banbox-red: #eb2127;
|
|
52
|
+
--color-banbox-link: #005694;
|
|
53
|
+
--color-banbox-link-h: #004576;
|
|
54
|
+
--color-banbox-review: #fcb532;
|
|
55
|
+
--color-banbox-green: #28c76f;
|
|
56
|
+
--color-banbox-deep-green: #097000;
|
|
57
|
+
--color-banbox-warning: #ff5301;
|
|
58
|
+
--color-banbox-error: #eb2127;
|
|
59
|
+
|
|
60
|
+
/* Named neutrals */
|
|
61
|
+
--color-banbox-ed: #ededed;
|
|
62
|
+
--color-banbox-f8: #f8f8f8;
|
|
63
|
+
--color-banbox-e1: #e1e1e1;
|
|
64
|
+
--color-banbox-fc: #fcfcfc;
|
|
65
|
+
--color-banbox-ca: #cacaca;
|
|
66
|
+
--color-banbox-e5: #e5e5e5;
|
|
67
|
+
--color-banbox-92: #929292;
|
|
68
|
+
--color-banbox-63: #636363;
|
|
69
|
+
--color-banbox-2c: #2c2c2c;
|
|
70
|
+
--color-banbox-f1: #f1f1f1;
|
|
71
|
+
|
|
72
|
+
/* Borders */
|
|
73
|
+
--border-banbox-primary: #636363;
|
|
74
|
+
--border-banbox-e1: #e1e1e1;
|
|
75
|
+
--border-banbox-dark: #374151;
|
|
76
|
+
|
|
77
|
+
/* Radii */
|
|
78
|
+
--radius-banbox-sm: 4px;
|
|
79
|
+
--radius-banbox-md: 6px;
|
|
80
|
+
--radius-lg: 12px;
|
|
81
|
+
--radius-full: 100px;
|
|
82
|
+
|
|
83
|
+
/* Shadows */
|
|
84
|
+
--shadow-banbox-modal-primary: 0px 2px 12px 0px #3b33331a;
|
|
85
|
+
--shadow-banbox-card-primary: 0px 6px 12px 0px #3b33331a;
|
|
86
|
+
--shadow-banbox-card-secondary: 0px 2px 2px 0px #2f2f2f14;
|
|
87
|
+
--shadow-banbox-footer-primary: 0px -2px 4px 0px #0000000a;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
91
|
+
THEME: Marketplace (same as default, orange)
|
|
92
|
+
═══════════════════════════════════════════════════════════════ */
|
|
93
|
+
.banbox-chat-root[data-theme="marketplace"] {
|
|
94
|
+
--color-banbox-primary: #ff5300;
|
|
95
|
+
--color-banbox-primary-active: #dc4c07;
|
|
96
|
+
--color-banbox-surface-container-low: #fff1ec;
|
|
97
|
+
--color-banbox-surface-container: #fceae5;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
101
|
+
THEME: Admin (black primary)
|
|
102
|
+
═══════════════════════════════════════════════════════════════ */
|
|
103
|
+
.banbox-chat-root[data-theme="admin"] {
|
|
104
|
+
--color-banbox-primary: #1a1a1a;
|
|
105
|
+
--color-banbox-primary-active: #000000;
|
|
106
|
+
--color-banbox-secondary: #374151;
|
|
107
|
+
--color-banbox-surface-container-lowest: #ffffff;
|
|
108
|
+
--color-banbox-surface-container-low: #f5f5f5;
|
|
109
|
+
--color-banbox-surface-container: #eeeeee;
|
|
110
|
+
--color-banbox-surface-container-high: #e8e8e8;
|
|
111
|
+
--color-banbox-surface-container-highest: #e0e0e0;
|
|
112
|
+
--color-banbox-outline: #d4d4d4;
|
|
113
|
+
--color-banbox-outline-variant: #9e9e9e;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
117
|
+
CUSTOM UTILITY: Scrollbars
|
|
118
|
+
Same as marketplace globals.css .custom-scroll
|
|
119
|
+
═══════════════════════════════════════════════════════════════ */
|
|
120
|
+
.banbox-chat-root .custom-scroll,
|
|
121
|
+
.banbox-chat-root .custom-scroll-hidden {
|
|
122
|
+
--sb-size: 6px;
|
|
123
|
+
--sb-track: #f1f1f1;
|
|
124
|
+
--sb-thumb: #c1c1c1;
|
|
125
|
+
--sb-thumb-hover: #898989;
|
|
126
|
+
|
|
127
|
+
overflow-y: auto;
|
|
128
|
+
overscroll-behavior: contain;
|
|
129
|
+
scrollbar-width: thin;
|
|
130
|
+
scrollbar-color: #c1c1c1 #f1f1f1;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.banbox-chat-root .custom-scroll::-webkit-scrollbar,
|
|
134
|
+
.banbox-chat-root .custom-scroll-hidden::-webkit-scrollbar {
|
|
135
|
+
width: 6px;
|
|
136
|
+
height: 6px;
|
|
137
|
+
display: block;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.banbox-chat-root .custom-scroll::-webkit-scrollbar-track,
|
|
141
|
+
.banbox-chat-root .custom-scroll-hidden::-webkit-scrollbar-track {
|
|
142
|
+
background: #f1f1f1;
|
|
143
|
+
border-radius: 9999px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.banbox-chat-root .custom-scroll::-webkit-scrollbar-thumb,
|
|
147
|
+
.banbox-chat-root .custom-scroll-hidden::-webkit-scrollbar-thumb {
|
|
148
|
+
background-color: #c1c1c1;
|
|
149
|
+
border-radius: 9999px;
|
|
150
|
+
border: 1px solid #f1f1f1;
|
|
151
|
+
min-height: 40px;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.banbox-chat-root .custom-scroll::-webkit-scrollbar-thumb:hover,
|
|
155
|
+
.banbox-chat-root .custom-scroll-hidden::-webkit-scrollbar-thumb:hover {
|
|
156
|
+
background-color: #898989;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/* hidden scrollbar variant (no visible track) */
|
|
160
|
+
.banbox-chat-root .custom-scroll-hidden {
|
|
161
|
+
scrollbar-width: none;
|
|
162
|
+
}
|
|
163
|
+
.banbox-chat-root .custom-scroll-hidden::-webkit-scrollbar {
|
|
164
|
+
display: none;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
168
|
+
no-scrollbar utility
|
|
169
|
+
═══════════════════════════════════════════════════════════════ */
|
|
170
|
+
.banbox-chat-root .no-scrollbar {
|
|
171
|
+
-ms-overflow-style: none;
|
|
172
|
+
scrollbar-width: none;
|
|
173
|
+
}
|
|
174
|
+
.banbox-chat-root .no-scrollbar::-webkit-scrollbar {
|
|
175
|
+
display: none;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
179
|
+
Chat-specific animations
|
|
180
|
+
═══════════════════════════════════════════════════════════════ */
|
|
181
|
+
@keyframes banbox-chat-fade-in {
|
|
182
|
+
from { opacity: 0; transform: translateY(6px); }
|
|
183
|
+
to { opacity: 1; transform: translateY(0); }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.banbox-chat-root .animate-fade-in {
|
|
187
|
+
animation: banbox-chat-fade-in 0.2s ease-out forwards;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
191
|
+
Chat bubble — mine (right) background uses primary color
|
|
192
|
+
═══════════════════════════════════════════════════════════════ */
|
|
193
|
+
.banbox-chat-root .chat-bubble-mine {
|
|
194
|
+
background-color: var(--color-banbox-primary);
|
|
195
|
+
color: #fff;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
199
|
+
Thread item — active state uses primary surface color
|
|
200
|
+
═══════════════════════════════════════════════════════════════ */
|
|
201
|
+
.banbox-chat-root .chat-thread-active {
|
|
202
|
+
background-color: var(--color-banbox-surface-container-low);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
206
|
+
Send button — primary color
|
|
207
|
+
═══════════════════════════════════════════════════════════════ */
|
|
208
|
+
.banbox-chat-root .chat-btn-send {
|
|
209
|
+
background-color: var(--color-banbox-primary);
|
|
210
|
+
color: #fff;
|
|
211
|
+
}
|
|
212
|
+
.banbox-chat-root .chat-btn-send:hover {
|
|
213
|
+
background-color: var(--color-banbox-primary-active);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
217
|
+
Unread badge — primary color
|
|
218
|
+
═══════════════════════════════════════════════════════════════ */
|
|
219
|
+
.banbox-chat-root .chat-badge-unread {
|
|
220
|
+
background-color: var(--color-banbox-primary);
|
|
221
|
+
color: #fff;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* ═══════════════════════════════════════════════════════════════
|
|
225
|
+
Tailwind @layer utilities — so these work as Tailwind classes
|
|
226
|
+
═══════════════════════════════════════════════════════════════ */
|
|
227
|
+
@layer utilities {
|
|
228
|
+
.banbox-chat-root * {
|
|
229
|
+
box-sizing: border-box;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -1,24 +1,21 @@
|
|
|
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
|
-
</div>
|
|
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, className }: Props) {
|
|
12
|
+
return (
|
|
13
|
+
<div>
|
|
14
|
+
<div className={`border-b border-[#e1e1e1] h-[64px] flex items-start justify-between px-4 pt-2.5${className ? ` ${className}` : ""}`}>
|
|
15
|
+
<div className="flex items-start gap-3">{left}</div>
|
|
16
|
+
{right}
|
|
17
|
+
</div>
|
|
18
|
+
{below && <>{below}</>}
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -1,156 +1,128 @@
|
|
|
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
|
-
className="h-full w-full flex-1 bg-transparent text-[15px] outline-none placeholder:text-[#9C9C9C]"
|
|
130
|
-
/>
|
|
131
|
-
</div>
|
|
132
|
-
|
|
133
|
-
<div>
|
|
134
|
-
<button
|
|
135
|
-
title="Close search"
|
|
136
|
-
onClick={() => {
|
|
137
|
-
setSearching(false);
|
|
138
|
-
setQ("");
|
|
139
|
-
onSearchChange?.("");
|
|
140
|
-
}}
|
|
141
|
-
className="grid h-8 w-8 place-items-center rounded-full text-xl hover:bg-black/5"
|
|
142
|
-
>
|
|
143
|
-
<ChatXIcon className="w-5 h-5" />
|
|
144
|
-
</button>
|
|
145
|
-
</div>
|
|
146
|
-
</div>
|
|
147
|
-
</div>
|
|
148
|
-
</motion.div>
|
|
149
|
-
)}
|
|
150
|
-
</AnimatePresence>
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
);
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
export default ChatListHeader;
|
|
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> = ({ className, 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
|
+
return (
|
|
47
|
+
<div className={`h-[64px] border-b border-[#ededed]${className ? ` ${className}` : ""}`}>
|
|
48
|
+
<div className="flex h-full items-center px-[20px]">
|
|
49
|
+
<AnimatePresence initial={false} mode="wait">
|
|
50
|
+
{!searching ? (
|
|
51
|
+
<motion.div
|
|
52
|
+
key="normal"
|
|
53
|
+
className="flex w-full items-center justify-between"
|
|
54
|
+
initial={{ opacity: 0, x: -24 }}
|
|
55
|
+
animate="inFromLeft"
|
|
56
|
+
exit="outToLeft"
|
|
57
|
+
variants={variants}
|
|
58
|
+
>
|
|
59
|
+
<div className="flex items-center gap-3">
|
|
60
|
+
<div className="text-[#2c2c2c] flex items-center gap-2">
|
|
61
|
+
<MessageIcon className="w-6 h-6" />
|
|
62
|
+
<span className="text-[22px] font-semibold">Messenger</span>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<div className="flex items-center gap-2">
|
|
67
|
+
<button
|
|
68
|
+
title="Search"
|
|
69
|
+
onClick={() => setSearching(true)}
|
|
70
|
+
className="h-9 w-9 place-items-center rounded-full hover:bg-black/5 flex items-center justify-center cursor-pointer border-none bg-transparent"
|
|
71
|
+
>
|
|
72
|
+
<ChatSearchIcon className="w-5 h-5" />
|
|
73
|
+
</button>
|
|
74
|
+
<button
|
|
75
|
+
title="Close"
|
|
76
|
+
onClick={onClose}
|
|
77
|
+
className="h-9 w-9 place-items-center rounded-full hover:bg-black/5 flex items-center justify-center cursor-pointer border-none bg-transparent"
|
|
78
|
+
>
|
|
79
|
+
<ChatXIcon className="w-6 h-6" />
|
|
80
|
+
</button>
|
|
81
|
+
</div>
|
|
82
|
+
</motion.div>
|
|
83
|
+
) : (
|
|
84
|
+
<motion.div
|
|
85
|
+
key="search"
|
|
86
|
+
className="flex w-full items-center gap-3"
|
|
87
|
+
initial={{ opacity: 0, x: 24 }}
|
|
88
|
+
animate="inFromRight"
|
|
89
|
+
exit="outToRight"
|
|
90
|
+
variants={variants}
|
|
91
|
+
>
|
|
92
|
+
<div className="relative flex-1">
|
|
93
|
+
<div className="flex h-10 w-full items-center rounded-full border border-[#6A6A6A] bg-white px-[4px] gap-1.5">
|
|
94
|
+
<div className="flex items-center ms-[12px] w-full">
|
|
95
|
+
<span className="mr-2 grid h-6 w-6 shrink-0 place-items-center text-[#929292]">
|
|
96
|
+
<ChatSearchIcon className="w-5 h-5" />
|
|
97
|
+
</span>
|
|
98
|
+
<span className="mr-2 h-6 w-px shrink-0 bg-[#e1e1e1]" />
|
|
99
|
+
<input
|
|
100
|
+
ref={inputRef}
|
|
101
|
+
value={q}
|
|
102
|
+
onChange={(e) => {
|
|
103
|
+
setQ(e.target.value);
|
|
104
|
+
onSearchChange?.(e.target.value);
|
|
105
|
+
}}
|
|
106
|
+
placeholder="Search"
|
|
107
|
+
className="h-full w-full flex-1 bg-transparent text-[15px] outline-none placeholder:text-[#9C9C9C] border-none"
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
|
|
111
|
+
<button
|
|
112
|
+
title="Close search"
|
|
113
|
+
onClick={() => { setSearching(false); setQ(""); onSearchChange?.(""); }}
|
|
114
|
+
className="grid h-8 w-8 place-items-center rounded-full text-xl hover:bg-black/5 cursor-pointer border-none bg-transparent"
|
|
115
|
+
>
|
|
116
|
+
<ChatXIcon className="w-5 h-5" />
|
|
117
|
+
</button>
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</motion.div>
|
|
121
|
+
)}
|
|
122
|
+
</AnimatePresence>
|
|
123
|
+
</div>
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export default ChatListHeader;
|