ajaxter-chat 1.0.1 → 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 -191
- 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.js +19 -74
- 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 -13
- 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,157 +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 | Endpoint path to fetch users |
|
|
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
|
|
118
57
|
|
|
119
|
-
| Value |
|
|
120
|
-
|
|
121
|
-
| `SUPPORT` |
|
|
122
|
-
| `CHAT` |
|
|
123
|
-
| `BOTH` | Both
|
|
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 |
|
|
63
|
+
|
|
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 fetches users from:
|
|
130
|
-
|
|
131
70
|
```
|
|
132
|
-
GET
|
|
71
|
+
GET {CHAT_HOST_URL}[:{CHAT_HOST_PORT}]/{CHAT_USER_LIST}
|
|
133
72
|
```
|
|
134
73
|
|
|
135
74
|
Expected response:
|
|
136
|
-
|
|
137
75
|
```json
|
|
138
76
|
[
|
|
139
|
-
{
|
|
140
|
-
|
|
141
|
-
"uid": "uid_001",
|
|
142
|
-
"email": "alice@company.com",
|
|
143
|
-
"mobile": "+1234567890",
|
|
144
|
-
"project": "Platform Team",
|
|
145
|
-
"type": "developer"
|
|
146
|
-
},
|
|
147
|
-
{
|
|
148
|
-
"name": "Bob Smith",
|
|
149
|
-
"uid": "uid_002",
|
|
150
|
-
"email": "bob@client.com",
|
|
151
|
-
"mobile": "+0987654321",
|
|
152
|
-
"project": "Client Portal",
|
|
153
|
-
"type": "user"
|
|
154
|
-
}
|
|
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" }
|
|
155
79
|
]
|
|
156
80
|
```
|
|
157
81
|
|
|
@@ -159,132 +83,113 @@ Expected response:
|
|
|
159
83
|
|
|
160
84
|
## Usage
|
|
161
85
|
|
|
162
|
-
### React
|
|
163
|
-
|
|
86
|
+
### React
|
|
164
87
|
```tsx
|
|
165
88
|
// App.tsx
|
|
166
|
-
import { ChatWidget } from '
|
|
89
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
167
90
|
|
|
168
|
-
function App() {
|
|
91
|
+
export default function App() {
|
|
169
92
|
return (
|
|
170
|
-
|
|
171
|
-
<main>Your app
|
|
172
|
-
|
|
93
|
+
<>
|
|
94
|
+
<main>Your app</main>
|
|
173
95
|
<ChatWidget
|
|
174
96
|
theme={{
|
|
175
|
-
primaryColor:
|
|
176
|
-
|
|
177
|
-
buttonTextColor: '#ffffff',
|
|
178
|
-
buttonLabel: 'Chat with us',
|
|
97
|
+
primaryColor: '#1aaa96',
|
|
98
|
+
buttonLabel: 'Chat with us',
|
|
179
99
|
buttonPosition: 'bottom-right',
|
|
180
|
-
fontFamily: "'DM Sans', sans-serif",
|
|
181
|
-
borderRadius: '16px',
|
|
182
100
|
}}
|
|
183
101
|
/>
|
|
184
|
-
|
|
102
|
+
</>
|
|
185
103
|
);
|
|
186
104
|
}
|
|
187
105
|
```
|
|
188
106
|
|
|
189
107
|
### Next.js — App Router
|
|
190
|
-
|
|
191
108
|
```tsx
|
|
192
109
|
// app/ChatWidgetWrapper.tsx
|
|
193
110
|
'use client';
|
|
194
|
-
import { ChatWidget } from '
|
|
195
|
-
|
|
111
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
196
112
|
export function ChatWidgetWrapper() {
|
|
197
|
-
return <ChatWidget theme={{ primaryColor: '#
|
|
113
|
+
return <ChatWidget theme={{ primaryColor: '#1aaa96' }} />;
|
|
198
114
|
}
|
|
199
|
-
```
|
|
200
115
|
|
|
201
|
-
```tsx
|
|
202
116
|
// app/layout.tsx
|
|
203
117
|
import { ChatWidgetWrapper } from './ChatWidgetWrapper';
|
|
204
|
-
|
|
205
118
|
export default function RootLayout({ children }) {
|
|
206
|
-
return
|
|
207
|
-
<html lang="en">
|
|
208
|
-
<body>
|
|
209
|
-
{children}
|
|
210
|
-
<ChatWidgetWrapper />
|
|
211
|
-
</body>
|
|
212
|
-
</html>
|
|
213
|
-
);
|
|
119
|
+
return <html><body>{children}<ChatWidgetWrapper /></body></html>;
|
|
214
120
|
}
|
|
215
121
|
```
|
|
216
122
|
|
|
217
123
|
### Next.js — Pages Router
|
|
218
|
-
|
|
219
124
|
```tsx
|
|
220
125
|
// pages/_app.tsx
|
|
221
|
-
import { ChatWidget } from '
|
|
222
|
-
|
|
126
|
+
import { ChatWidget } from 'react-chat-widget-extension';
|
|
223
127
|
export default function MyApp({ Component, pageProps }) {
|
|
224
|
-
return
|
|
225
|
-
<>
|
|
226
|
-
<Component {...pageProps} />
|
|
227
|
-
<ChatWidget theme={{ primaryColor: '#10b981' }} />
|
|
228
|
-
</>
|
|
229
|
-
);
|
|
128
|
+
return <><Component {...pageProps} /><ChatWidget theme={{ primaryColor: '#1aaa96' }} /></>;
|
|
230
129
|
}
|
|
231
130
|
```
|
|
232
131
|
|
|
233
132
|
---
|
|
234
133
|
|
|
235
|
-
## Theme Props
|
|
134
|
+
## Theme Props (all optional)
|
|
236
135
|
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
buttonPosition?: 'bottom-right' | 'bottom-left'; // Default: 'bottom-right'
|
|
248
|
-
borderRadius?: string; // Default: '16px'
|
|
249
|
-
}
|
|
250
|
-
```
|
|
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 |
|
|
251
146
|
|
|
252
147
|
---
|
|
253
148
|
|
|
254
|
-
##
|
|
149
|
+
## Folder Structure
|
|
255
150
|
|
|
256
|
-
|
|
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
|
+
```
|
|
257
171
|
|
|
258
|
-
|
|
259
|
-
- `src/hooks/useChat.ts` → Add `socket.on('message', ...)` listener in `selectUser`
|
|
260
|
-
- `src/components/ChatWindow/index.tsx` → Initialize socket connection on mount
|
|
172
|
+
---
|
|
261
173
|
|
|
262
|
-
|
|
174
|
+
## WebSocket Integration Points
|
|
175
|
+
|
|
176
|
+
Look for `// TODO:` comments in `src/hooks/useChat.ts`:
|
|
263
177
|
|
|
264
178
|
```ts
|
|
265
|
-
// In
|
|
266
|
-
socket.emit('
|
|
179
|
+
// In selectUser — connect and listen:
|
|
180
|
+
// socket.emit('join', user.uid);
|
|
181
|
+
// socket.on('message', (msg) => setMessages(prev => [...prev, msg]));
|
|
267
182
|
|
|
268
|
-
// In
|
|
269
|
-
socket.
|
|
270
|
-
setMessages((prev) => [...prev, msg]);
|
|
271
|
-
});
|
|
183
|
+
// In sendMessage — emit outgoing:
|
|
184
|
+
// socket.emit('message', newMsg);
|
|
272
185
|
```
|
|
273
186
|
|
|
274
187
|
---
|
|
275
188
|
|
|
276
|
-
##
|
|
277
|
-
|
|
278
|
-
```bash
|
|
279
|
-
cd my_first_project
|
|
280
|
-
npm install
|
|
281
|
-
npm run build # Compile TypeScript → dist/
|
|
282
|
-
npm run type-check # Verify types without emitting
|
|
283
|
-
npm run dev # Watch mode
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
---
|
|
189
|
+
## Resize (Maximize / Minimize)
|
|
287
190
|
|
|
288
|
-
|
|
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.
|
|
289
195
|
|
|
290
|
-
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;
|