ajaxter-chat 1.0.3 → 2.0.1
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/README.md +96 -204
- package/dist/components/ChatScreen/index.d.ts +12 -0
- package/dist/components/ChatScreen/index.js +83 -0
- package/dist/components/ChatWidget.d.ts +0 -24
- package/dist/components/ChatWidget.js +129 -38
- package/dist/components/HomeScreen/index.d.ts +9 -0
- package/dist/components/HomeScreen/index.js +71 -0
- package/dist/components/MaintenanceView/index.d.ts +1 -1
- package/dist/components/MaintenanceView/index.js +15 -52
- package/dist/components/RecentChatsScreen/index.d.ts +16 -0
- package/dist/components/RecentChatsScreen/index.js +38 -0
- package/dist/components/Tabs/BottomTabs.d.ts +10 -0
- package/dist/components/Tabs/BottomTabs.js +29 -0
- package/dist/components/TicketScreen/index.d.ts +9 -0
- package/dist/components/TicketScreen/index.js +71 -0
- package/dist/components/UserListScreen/index.d.ts +13 -0
- package/dist/components/UserListScreen/index.js +64 -0
- package/dist/config/index.d.ts +0 -13
- package/dist/config/index.js +20 -95
- package/dist/hooks/useChat.d.ts +3 -7
- package/dist/hooks/useChat.js +8 -30
- package/dist/hooks/useUsers.d.ts +3 -10
- package/dist/hooks/useUsers.js +5 -11
- package/dist/index.d.ts +8 -7
- package/dist/index.js +7 -12
- package/dist/services/userService.d.ts +0 -5
- package/dist/services/userService.js +6 -15
- package/dist/src/components/ChatScreen/index.d.ts +12 -0
- package/dist/src/components/ChatScreen/index.js +83 -0
- package/dist/src/components/ChatWidget.d.ts +4 -0
- package/dist/src/components/ChatWidget.js +141 -0
- package/dist/src/components/HomeScreen/index.d.ts +9 -0
- package/dist/src/components/HomeScreen/index.js +71 -0
- package/dist/src/components/MaintenanceView/index.d.ts +7 -0
- package/dist/src/components/MaintenanceView/index.js +16 -0
- package/dist/src/components/RecentChatsScreen/index.d.ts +16 -0
- package/dist/src/components/RecentChatsScreen/index.js +38 -0
- package/dist/src/components/Tabs/BottomTabs.d.ts +10 -0
- package/dist/src/components/Tabs/BottomTabs.js +29 -0
- package/dist/src/components/TicketScreen/index.d.ts +9 -0
- package/dist/src/components/TicketScreen/index.js +71 -0
- package/dist/src/components/UserListScreen/index.d.ts +13 -0
- package/dist/src/components/UserListScreen/index.js +64 -0
- package/dist/src/config/index.d.ts +3 -0
- package/dist/src/config/index.js +38 -0
- package/dist/src/hooks/useChat.d.ts +8 -0
- package/dist/src/hooks/useChat.js +26 -0
- package/dist/src/hooks/useUsers.d.ts +7 -0
- package/dist/src/hooks/useUsers.js +26 -0
- package/dist/src/index.d.ts +14 -0
- package/dist/src/index.js +13 -0
- package/dist/src/services/userService.d.ts +2 -0
- package/dist/src/services/userService.js +9 -0
- package/dist/src/types/index.d.ts +59 -0
- package/dist/src/types/index.js +1 -0
- package/dist/src/utils/theme.d.ts +3 -0
- package/dist/src/utils/theme.js +13 -0
- package/dist/types/index.d.ts +23 -36
- package/dist/utils/theme.d.ts +0 -1
- package/dist/utils/theme.js +3 -18
- package/package.json +10 -20
- package/src/components/ChatScreen/index.tsx +205 -0
- package/src/components/ChatWidget.tsx +327 -0
- package/src/components/HomeScreen/index.tsx +130 -0
- package/src/components/MaintenanceView/index.tsx +41 -0
- package/src/components/RecentChatsScreen/index.tsx +108 -0
- package/src/components/Tabs/BottomTabs.tsx +82 -0
- package/src/components/TicketScreen/index.tsx +170 -0
- package/src/components/UserListScreen/index.tsx +181 -0
- package/src/config/index.ts +46 -0
- package/src/hooks/useChat.ts +31 -0
- package/src/hooks/useUsers.ts +27 -0
- package/src/index.ts +18 -0
- package/src/services/userService.ts +9 -0
- package/src/types/index.ts +82 -0
- package/src/utils/theme.ts +16 -0
- package/dist/components/BottomNav/index.d.ts +0 -10
- package/dist/components/BottomNav/index.js +0 -32
- package/dist/components/ChatBox/index.d.ts +0 -15
- package/dist/components/ChatBox/index.js +0 -228
- package/dist/components/ChatButton/index.d.ts +0 -9
- package/dist/components/ChatButton/index.js +0 -17
- package/dist/components/ChatWindow/index.d.ts +0 -10
- package/dist/components/ChatWindow/index.js +0 -286
- package/dist/components/HomeView/index.d.ts +0 -12
- package/dist/components/HomeView/index.js +0 -51
- package/dist/components/UserList/index.d.ts +0 -13
- package/dist/components/UserList/index.js +0 -136
package/README.md
CHANGED
|
@@ -1,170 +1,81 @@
|
|
|
1
|
-
#
|
|
1
|
+
# react-chat-widget-extension v2
|
|
2
2
|
|
|
3
|
-
A
|
|
4
|
-
|
|
5
|
-
- ✅ Environment-variable-driven behavior (status, type, API endpoint)
|
|
6
|
-
- ✅ Fully themeable via a `theme` prop (colors, fonts, button, position)
|
|
7
|
-
- ✅ SSR-safe — works with Next.js App Router and Pages Router
|
|
8
|
-
- ✅ TypeScript-first
|
|
9
|
-
- ✅ WebSocket-ready architecture
|
|
10
|
-
- ✅ Loading skeletons, empty states, error handling built in
|
|
3
|
+
A drop-in floating chat widget for **React.js** and **Next.js** with a multi-screen UI, ticket system, and full theme control.
|
|
11
4
|
|
|
12
5
|
---
|
|
13
6
|
|
|
14
|
-
##
|
|
7
|
+
## Screens & Navigation Flow
|
|
15
8
|
|
|
16
9
|
```
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
│
|
|
21
|
-
│
|
|
22
|
-
│
|
|
23
|
-
│
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
│ │ ├── useUsers.ts # Fetch & filter users hook
|
|
28
|
-
│ │ └── useChat.ts # Chat state & message management hook
|
|
29
|
-
│ ├── utils/
|
|
30
|
-
│ │ └── theme.ts # Theme merging & CSS variable utilities
|
|
31
|
-
│ └── components/
|
|
32
|
-
│ ├── ChatWidget.tsx # 🏠 Root widget — mount this in your app
|
|
33
|
-
│ ├── ChatButton/
|
|
34
|
-
│ │ └── index.tsx # Floating action button
|
|
35
|
-
│ ├── ChatWindow/
|
|
36
|
-
│ │ └── index.tsx # Expandable chat panel
|
|
37
|
-
│ ├── UserList/
|
|
38
|
-
│ │ └── index.tsx # User list with loading/error/empty states
|
|
39
|
-
│ ├── ChatBox/
|
|
40
|
-
│ │ └── index.tsx # Conversation panel + message input
|
|
41
|
-
│ └── MaintenanceView/
|
|
42
|
-
│ └── index.tsx # Shown when CHAT_STATUS=MAINTENANCE
|
|
43
|
-
├── examples/
|
|
44
|
-
│ ├── react-app/
|
|
45
|
-
│ │ ├── .env.example # React env variables template
|
|
46
|
-
│ │ └── App.tsx # React usage example
|
|
47
|
-
│ └── nextjs-app/
|
|
48
|
-
│ ├── .env.local.example # Next.js env variables template
|
|
49
|
-
│ ├── app/
|
|
50
|
-
│ │ ├── layout.tsx # App Router: root layout
|
|
51
|
-
│ │ ├── ChatWidgetWrapper.tsx # App Router: 'use client' boundary
|
|
52
|
-
│ │ └── page.tsx # App Router: home page
|
|
53
|
-
│ └── pages/
|
|
54
|
-
│ ├── _app.tsx # Pages Router: global app
|
|
55
|
-
│ └── index.tsx # Pages Router: index page
|
|
56
|
-
├── package.json
|
|
57
|
-
├── tsconfig.json
|
|
58
|
-
└── README.md
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Installation
|
|
64
|
-
|
|
65
|
-
```bash
|
|
66
|
-
npm install ajaxter-chat
|
|
67
|
-
# or
|
|
68
|
-
yarn add ajaxter-chat
|
|
10
|
+
Floating Button
|
|
11
|
+
└── Opens Widget Window
|
|
12
|
+
├── [Home Screen] ← default on open
|
|
13
|
+
│ ├── Need Support → [User List Screen: developers] → [Chat Screen]
|
|
14
|
+
│ ├── New Convo → [User List Screen: users] → [Chat Screen]
|
|
15
|
+
│ └── Raise Ticket → [Ticket Screen] (always shown)
|
|
16
|
+
│
|
|
17
|
+
├── [Bottom Tab: Home] ← Home screen
|
|
18
|
+
├── [Bottom Tab: Chats] ← Recent conversations list
|
|
19
|
+
└── [Bottom Tab: Tickets] ← All raised tickets + new ticket form
|
|
69
20
|
```
|
|
70
21
|
|
|
71
22
|
---
|
|
72
23
|
|
|
73
24
|
## Environment Variables
|
|
74
25
|
|
|
75
|
-
### React
|
|
76
|
-
|
|
26
|
+
### React (.env)
|
|
77
27
|
```env
|
|
78
28
|
REACT_APP_CHAT_HOST_URL=http://your-api.com
|
|
79
|
-
REACT_APP_CHAT_HOST_PORT=4000
|
|
80
|
-
REACT_APP_CHAT_USER_LIST=api/v1/
|
|
81
|
-
REACT_APP_CHAT_STATUS=ACTIVE
|
|
82
|
-
REACT_APP_CHAT_TYPE=BOTH
|
|
29
|
+
REACT_APP_CHAT_HOST_PORT=4000 # Optional — omit to use URL default port
|
|
30
|
+
REACT_APP_CHAT_USER_LIST=api/v1/users
|
|
31
|
+
REACT_APP_CHAT_STATUS=ACTIVE # ACTIVE | DISABLE | MAINTENANCE
|
|
32
|
+
REACT_APP_CHAT_TYPE=BOTH # SUPPORT | CHAT | BOTH
|
|
83
33
|
```
|
|
84
34
|
|
|
85
35
|
### Next.js (.env.local)
|
|
86
|
-
|
|
87
36
|
```env
|
|
88
37
|
NEXT_PUBLIC_CHAT_HOST_URL=http://your-api.com
|
|
89
|
-
NEXT_PUBLIC_CHAT_HOST_PORT=4000
|
|
90
|
-
NEXT_PUBLIC_CHAT_USER_LIST=api/v1/
|
|
38
|
+
NEXT_PUBLIC_CHAT_HOST_PORT=4000 # Optional
|
|
39
|
+
NEXT_PUBLIC_CHAT_USER_LIST=api/v1/users
|
|
91
40
|
NEXT_PUBLIC_CHAT_STATUS=ACTIVE
|
|
92
41
|
NEXT_PUBLIC_CHAT_TYPE=BOTH
|
|
93
42
|
```
|
|
94
43
|
|
|
95
|
-
### Variable Reference
|
|
96
|
-
|
|
97
|
-
| Variable | Type | Description |
|
|
98
|
-
|--------------------|---------------------------------------|------------------------------------------|
|
|
99
|
-
| `CHAT_HOST_URL` | string | Base URL of your chat/user API |
|
|
100
|
-
| `CHAT_HOST_PORT` | number | Port for your API server |
|
|
101
|
-
| `CHAT_USER_LIST` | string | User list URL — see **User List API** below |
|
|
102
|
-
| `CHAT_STATUS` | `ACTIVE` \| `DISABLE` \| `MAINTENANCE` | Controls widget visibility & state |
|
|
103
|
-
| `CHAT_TYPE` | `SUPPORT` \| `CHAT` \| `BOTH` | Controls which users are shown |
|
|
104
|
-
|
|
105
44
|
---
|
|
106
45
|
|
|
107
|
-
## CHAT_STATUS
|
|
46
|
+
## CHAT_STATUS Behaviour
|
|
108
47
|
|
|
109
|
-
| Value
|
|
110
|
-
|
|
111
|
-
| `ACTIVE`
|
|
112
|
-
| `DISABLE`
|
|
113
|
-
| `MAINTENANCE`
|
|
48
|
+
| Value | Behaviour |
|
|
49
|
+
|---------------|--------------------------------------------------------|
|
|
50
|
+
| `ACTIVE` | Widget fully enabled |
|
|
51
|
+
| `DISABLE` | Widget **not rendered at all** — zero DOM footprint |
|
|
52
|
+
| `MAINTENANCE` | Widget opens but shows a maintenance message |
|
|
114
53
|
|
|
115
54
|
---
|
|
116
55
|
|
|
117
|
-
## CHAT_TYPE
|
|
56
|
+
## CHAT_TYPE Behaviour
|
|
57
|
+
|
|
58
|
+
| Value | Home Cards Shown | User List Filter |
|
|
59
|
+
|-----------|-------------------------------------|-------------------|
|
|
60
|
+
| `SUPPORT` | Need Support only | `type=developer` |
|
|
61
|
+
| `CHAT` | New Conversation only | `type=user` |
|
|
62
|
+
| `BOTH` | Both cards | Per card clicked |
|
|
118
63
|
|
|
119
|
-
|
|
120
|
-
|-----------|------------------------------------------|-----------------------------|
|
|
121
|
-
| `SUPPORT` | Only `type: "developer"` users | Single panel |
|
|
122
|
-
| `CHAT` | Only `type: "user"` users | Single panel |
|
|
123
|
-
| `BOTH` | Both developers and users | Two tabs (Support / Users) |
|
|
64
|
+
> **Raise Ticket is always shown regardless of CHAT_TYPE.**
|
|
124
65
|
|
|
125
66
|
---
|
|
126
67
|
|
|
127
68
|
## User List API
|
|
128
69
|
|
|
129
|
-
The widget calls `CHAT_USER_LIST` in three ways:
|
|
130
|
-
|
|
131
|
-
1. **Same-origin / BFF (recommended)** — value starts with `/`, e.g. `/api/v1/chat/users`. The browser only shows a request to **your** app; your route handler proxies to the real API server-side, so the upstream URL (e.g. `http://your-api.com:4000/...`) does not appear as the client request URL in DevTools.
|
|
132
|
-
2. **Full URL** — value starts with `http://` or `https://`; that exact URL is fetched (visible in Network).
|
|
133
|
-
3. **Legacy** — otherwise it is built as
|
|
134
|
-
`CHAT_HOST_URL` + optional `:CHAT_HOST_PORT` + path.
|
|
135
|
-
|
|
136
|
-
Example (Next.js route at `app/api/v1/chat/users/route.ts` that forwards to your backend):
|
|
137
|
-
|
|
138
|
-
```env
|
|
139
|
-
NEXT_PUBLIC_CHAT_USER_LIST=/api/v1/chat/users
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Legacy example:
|
|
143
|
-
|
|
144
70
|
```
|
|
145
|
-
GET
|
|
71
|
+
GET {CHAT_HOST_URL}[:{CHAT_HOST_PORT}]/{CHAT_USER_LIST}
|
|
146
72
|
```
|
|
147
73
|
|
|
148
74
|
Expected response:
|
|
149
|
-
|
|
150
75
|
```json
|
|
151
76
|
[
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
"uid": "uid_001",
|
|
155
|
-
"email": "alice@company.com",
|
|
156
|
-
"mobile": "+1234567890",
|
|
157
|
-
"project": "Platform Team",
|
|
158
|
-
"type": "developer"
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
"name": "Bob Smith",
|
|
162
|
-
"uid": "uid_002",
|
|
163
|
-
"email": "bob@client.com",
|
|
164
|
-
"mobile": "+0987654321",
|
|
165
|
-
"project": "Client Portal",
|
|
166
|
-
"type": "user"
|
|
167
|
-
}
|
|
77
|
+
{ "name": "Alice", "uid": "u1", "email": "alice@co.com", "mobile": "", "project": "Platform", "type": "developer" },
|
|
78
|
+
{ "name": "Bob", "uid": "u2", "email": "bob@co.com", "mobile": "", "project": "Portal", "type": "user" }
|
|
168
79
|
]
|
|
169
80
|
```
|
|
170
81
|
|
|
@@ -172,132 +83,113 @@ Expected response:
|
|
|
172
83
|
|
|
173
84
|
## Usage
|
|
174
85
|
|
|
175
|
-
### React
|
|
176
|
-
|
|
86
|
+
### React
|
|
177
87
|
```tsx
|
|
178
88
|
// App.tsx
|
|
179
|
-
import { ChatWidget } from '
|
|
89
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
180
90
|
|
|
181
|
-
function App() {
|
|
91
|
+
export default function App() {
|
|
182
92
|
return (
|
|
183
|
-
|
|
184
|
-
<main>Your app
|
|
185
|
-
|
|
93
|
+
<>
|
|
94
|
+
<main>Your app</main>
|
|
186
95
|
<ChatWidget
|
|
187
96
|
theme={{
|
|
188
|
-
primaryColor:
|
|
189
|
-
|
|
190
|
-
buttonTextColor: '#ffffff',
|
|
191
|
-
buttonLabel: 'Chat with us',
|
|
97
|
+
primaryColor: '#1aaa96',
|
|
98
|
+
buttonLabel: 'Chat with us',
|
|
192
99
|
buttonPosition: 'bottom-right',
|
|
193
|
-
fontFamily: "'DM Sans', sans-serif",
|
|
194
|
-
borderRadius: '16px',
|
|
195
100
|
}}
|
|
196
101
|
/>
|
|
197
|
-
|
|
102
|
+
</>
|
|
198
103
|
);
|
|
199
104
|
}
|
|
200
105
|
```
|
|
201
106
|
|
|
202
107
|
### Next.js — App Router
|
|
203
|
-
|
|
204
108
|
```tsx
|
|
205
109
|
// app/ChatWidgetWrapper.tsx
|
|
206
110
|
'use client';
|
|
207
|
-
import { ChatWidget } from '
|
|
208
|
-
|
|
111
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
209
112
|
export function ChatWidgetWrapper() {
|
|
210
|
-
return <ChatWidget theme={{ primaryColor: '#
|
|
113
|
+
return <ChatWidget theme={{ primaryColor: '#1aaa96' }} />;
|
|
211
114
|
}
|
|
212
|
-
```
|
|
213
115
|
|
|
214
|
-
```tsx
|
|
215
116
|
// app/layout.tsx
|
|
216
117
|
import { ChatWidgetWrapper } from './ChatWidgetWrapper';
|
|
217
|
-
|
|
218
118
|
export default function RootLayout({ children }) {
|
|
219
|
-
return
|
|
220
|
-
<html lang="en">
|
|
221
|
-
<body>
|
|
222
|
-
{children}
|
|
223
|
-
<ChatWidgetWrapper />
|
|
224
|
-
</body>
|
|
225
|
-
</html>
|
|
226
|
-
);
|
|
119
|
+
return <html><body>{children}<ChatWidgetWrapper /></body></html>;
|
|
227
120
|
}
|
|
228
121
|
```
|
|
229
122
|
|
|
230
123
|
### Next.js — Pages Router
|
|
231
|
-
|
|
232
124
|
```tsx
|
|
233
125
|
// pages/_app.tsx
|
|
234
|
-
import { ChatWidget } from '
|
|
235
|
-
|
|
126
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
236
127
|
export default function MyApp({ Component, pageProps }) {
|
|
237
|
-
return
|
|
238
|
-
<>
|
|
239
|
-
<Component {...pageProps} />
|
|
240
|
-
<ChatWidget theme={{ primaryColor: '#10b981' }} />
|
|
241
|
-
</>
|
|
242
|
-
);
|
|
128
|
+
return <><Component {...pageProps} /><ChatWidget theme={{ primaryColor: '#1aaa96' }} /></>;
|
|
243
129
|
}
|
|
244
130
|
```
|
|
245
131
|
|
|
246
132
|
---
|
|
247
133
|
|
|
248
|
-
## Theme Props
|
|
134
|
+
## Theme Props (all optional)
|
|
249
135
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
buttonPosition?: 'bottom-right' | 'bottom-left'; // Default: 'bottom-right'
|
|
261
|
-
borderRadius?: string; // Default: '16px'
|
|
262
|
-
}
|
|
263
|
-
```
|
|
136
|
+
| Prop | Type | Default | Description |
|
|
137
|
+
|-------------------|------------------------------------|----------------------------------|--------------------------------------|
|
|
138
|
+
| `primaryColor` | `string` | `'#1aaa96'` | Header, active states, send button |
|
|
139
|
+
| `buttonColor` | `string` | `'#1aaa96'` | Floating button background |
|
|
140
|
+
| `buttonTextColor` | `string` | `'#ffffff'` | Floating button text/icon |
|
|
141
|
+
| `buttonLabel` | `string` | `'Chat with us'` | Floating button label |
|
|
142
|
+
| `buttonPosition` | `'bottom-right' \| 'bottom-left'` | `'bottom-right'` | Floating button corner |
|
|
143
|
+
| `fontFamily` | `string` | `"'DM Sans', 'Segoe UI', ..."` | Font used across the widget |
|
|
144
|
+
| `borderRadius` | `string` | `'16px'` | Widget panel corner radius |
|
|
145
|
+
| `backgroundColor` | `string` | `'#ffffff'` | Widget panel background |
|
|
264
146
|
|
|
265
147
|
---
|
|
266
148
|
|
|
267
|
-
##
|
|
149
|
+
## Folder Structure
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
src/
|
|
153
|
+
├── index.ts ← Public API exports
|
|
154
|
+
├── types/index.ts ← All TypeScript types
|
|
155
|
+
├── config/index.ts ← Env loader (NEXT_PUBLIC_ + REACT_APP_)
|
|
156
|
+
├── services/userService.ts ← fetch() user list API
|
|
157
|
+
├── hooks/
|
|
158
|
+
│ ├── useUsers.ts ← Fetch & filter users
|
|
159
|
+
│ └── useChat.ts ← Message state, WebSocket-ready
|
|
160
|
+
├── utils/theme.ts ← defaultTheme + mergeTheme
|
|
161
|
+
└── components/
|
|
162
|
+
├── ChatWidget.tsx ← 🏠 Root — mount this in your app
|
|
163
|
+
├── HomeScreen/ ← Hi there + option cards
|
|
164
|
+
├── UserListScreen/ ← Slide-in user picker
|
|
165
|
+
├── ChatScreen/ ← Conversation + input bar
|
|
166
|
+
├── RecentChatsScreen/ ← Bottom tab: Chats
|
|
167
|
+
├── TicketScreen/ ← Bottom tab: Tickets + raise form
|
|
168
|
+
├── MaintenanceView/ ← Shown when MAINTENANCE
|
|
169
|
+
└── Tabs/BottomTabs.tsx ← Home / Chats / Tickets tabs
|
|
170
|
+
```
|
|
268
171
|
|
|
269
|
-
|
|
172
|
+
---
|
|
270
173
|
|
|
271
|
-
|
|
272
|
-
- `src/hooks/useChat.ts` → Add `socket.on('message', ...)` listener in `selectUser`
|
|
273
|
-
- `src/components/ChatWindow/index.tsx` → Initialize socket connection on mount
|
|
174
|
+
## WebSocket Integration Points
|
|
274
175
|
|
|
275
|
-
|
|
176
|
+
Look for `// TODO:` comments in `src/hooks/useChat.ts`:
|
|
276
177
|
|
|
277
178
|
```ts
|
|
278
|
-
// In
|
|
279
|
-
socket.emit('
|
|
179
|
+
// In selectUser — connect and listen:
|
|
180
|
+
// socket.emit('join', user.uid);
|
|
181
|
+
// socket.on('message', (msg) => setMessages(prev => [...prev, msg]));
|
|
280
182
|
|
|
281
|
-
// In
|
|
282
|
-
socket.
|
|
283
|
-
setMessages((prev) => [...prev, msg]);
|
|
284
|
-
});
|
|
183
|
+
// In sendMessage — emit outgoing:
|
|
184
|
+
// socket.emit('message', newMsg);
|
|
285
185
|
```
|
|
286
186
|
|
|
287
187
|
---
|
|
288
188
|
|
|
289
|
-
##
|
|
290
|
-
|
|
291
|
-
```bash
|
|
292
|
-
cd my_first_project
|
|
293
|
-
npm install
|
|
294
|
-
npm run build # Compile TypeScript → dist/
|
|
295
|
-
npm run type-check # Verify types without emitting
|
|
296
|
-
npm run dev # Watch mode
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
---
|
|
189
|
+
## Resize (Maximize / Minimize)
|
|
300
190
|
|
|
301
|
-
|
|
191
|
+
The widget has a maximize/minimize toggle button in the top-right of the panel header.
|
|
192
|
+
- **Normal size**: 380×560px
|
|
193
|
+
- **Maximized**: 480×720px
|
|
194
|
+
- Both sizes respect `max-width: calc(100vw - 32px)` and `max-height: calc(100vh - 110px)` for mobile safety.
|
|
302
195
|
|
|
303
|
-
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { ChatMessage, ChatUser, ChatWidgetTheme } from '../../types';
|
|
3
|
+
interface ChatScreenProps {
|
|
4
|
+
activeUser: ChatUser;
|
|
5
|
+
messages: ChatMessage[];
|
|
6
|
+
onSend: (text: string) => void;
|
|
7
|
+
onBack: () => void;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
theme?: ChatWidgetTheme;
|
|
10
|
+
}
|
|
11
|
+
export declare const ChatScreen: React.FC<ChatScreenProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState, useRef, useEffect } from 'react';
|
|
3
|
+
import { mergeTheme } from '../../utils/theme';
|
|
4
|
+
export const ChatScreen = ({ activeUser, messages, onSend, onBack, onClose, theme, }) => {
|
|
5
|
+
const t = mergeTheme(theme);
|
|
6
|
+
const [text, setText] = useState('');
|
|
7
|
+
const endRef = useRef(null);
|
|
8
|
+
const inputRef = useRef(null);
|
|
9
|
+
useEffect(() => { var _a; (_a = endRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' }); }, [messages]);
|
|
10
|
+
const handleSend = () => {
|
|
11
|
+
var _a;
|
|
12
|
+
if (!text.trim())
|
|
13
|
+
return;
|
|
14
|
+
onSend(text);
|
|
15
|
+
setText('');
|
|
16
|
+
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
|
|
17
|
+
};
|
|
18
|
+
const handleKey = (e) => {
|
|
19
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
20
|
+
e.preventDefault();
|
|
21
|
+
handleSend();
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const initials = activeUser.name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
|
|
25
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', height: '100%', animation: 'cw-slideInRight 0.25s ease' }, children: [_jsxs("div", { style: {
|
|
26
|
+
backgroundColor: t.primaryColor,
|
|
27
|
+
padding: '14px 18px',
|
|
28
|
+
display: 'flex',
|
|
29
|
+
alignItems: 'center',
|
|
30
|
+
gap: '10px',
|
|
31
|
+
flexShrink: 0,
|
|
32
|
+
}, children: [_jsx("button", { onClick: onBack, style: iconBtnStyle, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M19 12H5M5 12L12 19M5 12L12 5", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("div", { style: {
|
|
33
|
+
width: 36, height: 36, borderRadius: '50%',
|
|
34
|
+
backgroundColor: 'rgba(255,255,255,0.25)',
|
|
35
|
+
display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
36
|
+
fontWeight: 700, fontSize: '13px', color: '#fff', flexShrink: 0,
|
|
37
|
+
}, children: initials }), _jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [_jsx("div", { style: { fontWeight: 700, fontSize: '14px', color: '#fff', fontFamily: t.fontFamily }, children: activeUser.name }), _jsxs("div", { style: { fontSize: '11px', color: 'rgba(255,255,255,0.8)', display: 'flex', alignItems: 'center', gap: 4 }, children: [_jsx("span", { style: { width: 6, height: 6, borderRadius: '50%', backgroundColor: '#a8f0c6', display: 'inline-block' } }), "Online"] })] }), _jsx("button", { onClick: onClose, style: iconBtnStyle, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })] }), _jsxs("div", { style: {
|
|
38
|
+
flex: 1, overflowY: 'auto', padding: '18px 16px',
|
|
39
|
+
display: 'flex', flexDirection: 'column', gap: '10px',
|
|
40
|
+
backgroundColor: '#f7f8fc',
|
|
41
|
+
}, children: [messages.length === 0 && (_jsxs("div", { style: {
|
|
42
|
+
margin: 'auto', textAlign: 'center',
|
|
43
|
+
fontSize: '13px', color: '#b0bec5',
|
|
44
|
+
fontFamily: t.fontFamily,
|
|
45
|
+
}, children: [_jsx("div", { style: { fontSize: '28px', marginBottom: 8 }, children: "\uD83D\uDCAC" }), "Say hi to ", activeUser.name, "!"] })), messages.map(msg => (_jsx(Bubble, { msg: msg, primaryColor: t.primaryColor, font: t.fontFamily }, msg.id))), _jsx("div", { ref: endRef })] }), _jsxs("div", { style: {
|
|
46
|
+
borderTop: '1px solid #eef0f5',
|
|
47
|
+
padding: '10px 14px',
|
|
48
|
+
backgroundColor: '#fff',
|
|
49
|
+
display: 'flex',
|
|
50
|
+
alignItems: 'flex-end',
|
|
51
|
+
gap: '10px',
|
|
52
|
+
flexShrink: 0,
|
|
53
|
+
}, children: [_jsx("textarea", { ref: inputRef, value: text, onChange: e => setText(e.target.value), onKeyDown: handleKey, placeholder: "Type and press [enter]..", rows: 1, style: {
|
|
54
|
+
flex: 1, resize: 'none', border: 'none', outline: 'none',
|
|
55
|
+
fontFamily: t.fontFamily, fontSize: '14px', lineHeight: '1.5',
|
|
56
|
+
maxHeight: '80px', overflowY: 'auto', color: '#1a2332',
|
|
57
|
+
padding: '6px 0', backgroundColor: 'transparent',
|
|
58
|
+
} }), _jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: '8px', flexShrink: 0 }, children: [_jsx(IconBtn, { onClick: () => { }, title: "Reaction", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M14 9h.01M10 9h.01M12 2a10 10 0 100 20A10 10 0 0012 2zm0 14s-4-1.5-4-4", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M8 15s1.5 2 4 2 4-2 4-2", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round" })] }) }), _jsx(IconBtn, { onClick: () => { }, title: "Attach", children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66L9.41 17.41a2 2 0 01-2.83-2.83l8.49-8.48", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx(IconBtn, { onClick: () => { }, title: "Emoji", children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#9aa3af", strokeWidth: "1.8" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2", stroke: "#9aa3af", strokeWidth: "1.8", strokeLinecap: "round" }), _jsx("line", { x1: "9", y1: "9", x2: "9.01", y2: "9", stroke: "#9aa3af", strokeWidth: "2.5", strokeLinecap: "round" }), _jsx("line", { x1: "15", y1: "9", x2: "15.01", y2: "9", stroke: "#9aa3af", strokeWidth: "2.5", strokeLinecap: "round" })] }) }), text.trim() && (_jsx("button", { onClick: handleSend, style: {
|
|
59
|
+
width: 36, height: 36, borderRadius: '50%',
|
|
60
|
+
backgroundColor: t.primaryColor, border: 'none',
|
|
61
|
+
cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
62
|
+
transition: 'transform 0.15s',
|
|
63
|
+
}, children: _jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: "#fff", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] })] })] }));
|
|
64
|
+
};
|
|
65
|
+
const Bubble = ({ msg, primaryColor, font }) => {
|
|
66
|
+
const isMe = msg.senderId === 'me';
|
|
67
|
+
const time = new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
|
68
|
+
return (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: isMe ? 'flex-end' : 'flex-start', gap: 3 }, children: [_jsx("div", { style: {
|
|
69
|
+
maxWidth: '75%', padding: '10px 14px',
|
|
70
|
+
borderRadius: isMe ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
|
|
71
|
+
backgroundColor: isMe ? primaryColor : '#fff',
|
|
72
|
+
color: isMe ? '#fff' : '#1a2332',
|
|
73
|
+
fontSize: '14px', lineHeight: '1.5',
|
|
74
|
+
boxShadow: '0 1px 4px rgba(0,0,0,0.07)',
|
|
75
|
+
fontFamily: font, wordBreak: 'break-word',
|
|
76
|
+
}, children: msg.text }), _jsx("span", { style: { fontSize: '11px', color: '#b0bec5', padding: '0 4px' }, children: time })] }));
|
|
77
|
+
};
|
|
78
|
+
const IconBtn = ({ onClick, title, children }) => (_jsx("button", { onClick: onClick, title: title, style: { background: 'none', border: 'none', cursor: 'pointer', padding: '4px', display: 'flex', alignItems: 'center', borderRadius: '6px' }, onMouseEnter: e => e.currentTarget.style.background = '#f3f4f6', onMouseLeave: e => e.currentTarget.style.background = 'none', children: children }));
|
|
79
|
+
const iconBtnStyle = {
|
|
80
|
+
background: 'rgba(255,255,255,0.2)', border: 'none', borderRadius: '50%',
|
|
81
|
+
width: 34, height: 34, display: 'flex', alignItems: 'center', justifyContent: 'center',
|
|
82
|
+
cursor: 'pointer', flexShrink: 0,
|
|
83
|
+
};
|
|
@@ -1,28 +1,4 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { ChatWidgetProps } from '../types';
|
|
3
|
-
/**
|
|
4
|
-
* ChatWidget
|
|
5
|
-
*
|
|
6
|
-
* Drop-in chat widget for React.js and Next.js apps.
|
|
7
|
-
* All behavior is configured via environment variables.
|
|
8
|
-
* All UI styling is configured via the `theme` prop.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* // Basic usage
|
|
12
|
-
* <ChatWidget />
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* // With custom theme
|
|
16
|
-
* <ChatWidget
|
|
17
|
-
* theme={{
|
|
18
|
-
* primaryColor: '#FF6B6B',
|
|
19
|
-
* buttonColor: '#FF6B6B',
|
|
20
|
-
* buttonLabel: 'Need Help?',
|
|
21
|
-
* buttonPosition: 'bottom-left',
|
|
22
|
-
* fontFamily: "'Inter', sans-serif",
|
|
23
|
-
* borderRadius: '12px',
|
|
24
|
-
* }}
|
|
25
|
-
* />
|
|
26
|
-
*/
|
|
27
3
|
export declare const ChatWidget: React.FC<ChatWidgetProps>;
|
|
28
4
|
export default ChatWidget;
|