@banbox/chat 1.0.4 → 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.
@@ -15,7 +15,9 @@ import TypingIndicator from "../ui/chat/TypingIndicator";
15
15
 
16
16
  import type { ChatAdapter, ChatUICallbacks } from "../adapter/types";
17
17
  import type { Message, MessageRef, Reference, Thread } from "../types";
18
+ import type { ChatTheme } from "./InboxPopup";
18
19
 
20
+ /* ─── Helpers ─── */
19
21
  const GRADIENT_BORDER =
20
22
  "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%)";
21
23
 
@@ -44,12 +46,31 @@ function toRef(m: Message): MessageRef {
44
46
  };
45
47
  }
46
48
 
49
+ function getThemeAttr(theme?: ChatTheme): string {
50
+ if (!theme || theme === "marketplace") return "marketplace";
51
+ if (theme === "admin") return "admin";
52
+ return "custom";
53
+ }
54
+
55
+ function getThemeVars(theme?: ChatTheme): React.CSSProperties {
56
+ if (!theme || theme === "marketplace" || theme === "admin") return {};
57
+ const vars: Record<string, string> = {};
58
+ if (theme.primary) vars["--color-banbox-primary"] = theme.primary;
59
+ if (theme.primaryActive) vars["--color-banbox-primary-active"] = theme.primaryActive;
60
+ if (theme.surfaceLow) vars["--color-banbox-surface-container-low"] = theme.surfaceLow;
61
+ return vars as React.CSSProperties;
62
+ }
63
+
47
64
  export type SinglePopupProps = {
48
65
  adapter: ChatAdapter;
49
66
  uiCallbacks?: ChatUICallbacks;
67
+ theme?: ChatTheme;
50
68
  };
51
69
 
52
- const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
70
+ /* ══════════════════════════════════════════════════
71
+ Component
72
+ ══════════════════════════════════════════════════ */
73
+ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks, theme }) => {
53
74
  const { close, reference } = useChatUI();
54
75
 
55
76
  const threads = adapter.threads.list(reference);
@@ -99,30 +120,29 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
99
120
  void uiCallbacks;
100
121
 
101
122
  return (
102
- <div style={{ position: "fixed", bottom: 16, right: 16, zIndex: 10002 }}>
123
+ <div className="fixed bottom-4 right-4 z-[10002]">
103
124
  {/* Backdrop */}
104
125
  <motion.button
105
126
  aria-label="Close chat"
106
127
  onClick={close}
107
- style={{ position: "fixed", inset: 0, background: "transparent", border: "none", cursor: "auto" }}
128
+ className="fixed inset-0 cursor-auto!"
129
+ style={{ background: "transparent", border: "none" }}
108
130
  initial={{ opacity: 0 }}
109
131
  animate={{ opacity: 1 }}
110
132
  exit={{ opacity: 0 }}
111
133
  transition={{ type: "tween", duration: 0.25 }}
112
134
  />
113
135
 
114
- {/* Outer gradient border */}
136
+ {/* Outer gradient border + theme root */}
115
137
  <motion.div
116
138
  role="dialog"
117
139
  aria-modal="true"
140
+ data-theme={getThemeAttr(theme)}
141
+ className="banbox-chat-root relative h-[650px] w-[450px] rounded-[20px] p-[2px]"
118
142
  style={{
119
- position: "relative",
120
- height: 650,
121
- width: 450,
122
- borderRadius: 20,
123
- padding: 2,
124
143
  boxShadow: "0px 2px 12px 0px #3B33331A",
125
144
  background: GRADIENT_BORDER,
145
+ ...getThemeVars(theme),
126
146
  }}
127
147
  initial={{ x: "110%" }}
128
148
  animate={{ x: 0 }}
@@ -131,19 +151,11 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
131
151
  >
132
152
  {/* Inner white card */}
133
153
  <div
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
- }}
154
+ className="flex h-full w-full flex-col overflow-hidden rounded-[18px] bg-white"
155
+ style={{ overscrollBehavior: "contain" }}
144
156
  >
145
157
  {/* Header — 64px */}
146
- <div style={{ height: 64, flexShrink: 0 }}>
158
+ <div className="h-[64px] shrink-0">
147
159
  <ChatHeader
148
160
  left={
149
161
  <ChatIdentity
@@ -160,19 +172,7 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
160
172
  <button
161
173
  type="button"
162
174
  onClick={close}
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
- }}
175
+ className="flex h-[34px] w-[34px] items-center justify-center rounded-full bg-white text-black shadow-[0px_2px_4px_0px_#A5A3AE4D] hover:bg-black/5 hover:text-[var(--color-banbox-warning)] cursor-pointer border-none"
176
176
  >
177
177
  <ChatXIcon className="h-6 w-6" />
178
178
  </button>
@@ -181,11 +181,11 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
181
181
  </div>
182
182
 
183
183
  {/* Messages — flex-1 */}
184
- <div style={{ flex: 1, minHeight: 0, position: "relative" }}>
184
+ <div className="flex-1 min-h-0">
185
185
  <ChatScroll
186
+ className="h-full"
186
187
  bottomAlignWhenShort={false}
187
188
  scrollKey={scrollKey}
188
- style={{ height: "100%", overflowY: "auto" }}
189
189
  >
190
190
  {messages.map((m, idx) => {
191
191
  const mine = m.author === "you";
@@ -198,12 +198,8 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
198
198
  time={m.time ?? ""}
199
199
  authorInitial={typeof m.author === "string" ? m.author : "U"}
200
200
  text={m.text ?? m.content}
201
- businessCard={
202
- m.businessCard as Parameters<typeof ChatMessageItem>[0]["businessCard"]
203
- }
204
- addressCard={
205
- m.addressCard as Parameters<typeof ChatMessageItem>[0]["addressCard"]
206
- }
201
+ businessCard={m.businessCard as Parameters<typeof ChatMessageItem>[0]["businessCard"]}
202
+ addressCard={m.addressCard as Parameters<typeof ChatMessageItem>[0]["addressCard"]}
207
203
  images={m.images}
208
204
  files={m.files}
209
205
  audio={m.audio}
@@ -217,14 +213,14 @@ const SinglePopup: React.FC<SinglePopupProps> = ({ adapter, uiCallbacks }) => {
217
213
  })}
218
214
 
219
215
  {/* Typing indicator */}
220
- <div style={{ display: "flex", alignItems: "center", justifyContent: "flex-start" }}>
216
+ <div className="flex items-center justify-start">
221
217
  <TypingIndicator />
222
218
  </div>
223
219
  </ChatScroll>
224
220
  </div>
225
221
 
226
222
  {/* Footer */}
227
- <div style={{ flexShrink: 0 }}>
223
+ <div className="shrink-0">
228
224
  <ChatFooter
229
225
  variant="single"
230
226
  replyTo={replyTo}
package/src/index.ts CHANGED
@@ -8,6 +8,9 @@
8
8
  export { default as ChatRoot } from "./chat/ChatRoot";
9
9
  export type { ChatRootProps } from "./chat/ChatRoot";
10
10
 
11
+ // ── Theme type ────────────────────────────────────────────────────────────────
12
+ export type { ChatTheme } from "./chat/InboxPopup";
13
+
11
14
  // ── Adapter interfaces (implement these in your host app) ─────────────────────
12
15
  export type { ChatAdapter, ChatUICallbacks, KebabMenuOpts, ToastOpts } from "./adapter/types";
13
16
 
@@ -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
+ }
@@ -8,22 +8,11 @@ type Props = {
8
8
  className?: string;
9
9
  };
10
10
 
11
- export default function ChatHeader({ left, right, below }: Props) {
11
+ export default function ChatHeader({ left, right, below, className }: Props) {
12
12
  return (
13
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>
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>
27
16
  {right}
28
17
  </div>
29
18
  {below && <>{below}</>}
@@ -11,7 +11,7 @@ type Props = {
11
11
  onSearchChange?: (value: string) => void;
12
12
  };
13
13
 
14
- const ChatListHeader: React.FC<Props> = ({ onClose, onSearchChange }) => {
14
+ const ChatListHeader: React.FC<Props> = ({ className, onClose, onSearchChange }) => {
15
15
  const [searching, setSearching] = React.useState(false);
16
16
  const [q, setQ] = React.useState("");
17
17
  const inputRef = React.useRef<HTMLInputElement>(null);
@@ -43,113 +43,84 @@ const ChatListHeader: React.FC<Props> = ({ onClose, onSearchChange }) => {
43
43
  outToRight: { opacity: 0, x: 24, transition: { duration: 0.16, ease: [0.4, 0, 1, 1] } },
44
44
  };
45
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
46
  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
- }}
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}
116
58
  >
117
- <div style={{ display: "flex", alignItems: "center", flex: 1, marginLeft: 12 }}>
118
- <span style={{ marginRight: 8, color: "#929292", flexShrink: 0 }}>
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
+ >
119
72
  <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
- />
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>
140
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>
141
110
 
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>
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>
153
124
  </div>
154
125
  );
155
126
  };