ajaxter-chat 3.0.7 → 3.0.8

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,9 +15,6 @@ import { BlockListScreen } from './BlockList';
15
15
  import { CallScreen } from './CallScreen';
16
16
  import { MaintenanceView } from './MaintenanceView';
17
17
  import { BottomTabs } from './Tabs/BottomTabs';
18
- /* ─── Drawer width ─────────────────────────────────────────────────────────── */
19
- const DRAWER_W_NORMAL = 380;
20
- const DRAWER_W_MAX = 480;
21
18
  export const ChatWidget = ({ theme: localTheme }) => {
22
19
  var _a, _b, _c, _d;
23
20
  /* SSR guard */
@@ -31,7 +28,6 @@ export const ChatWidget = ({ theme: localTheme }) => {
31
28
  const theme = mergeTheme((data === null || data === void 0 ? void 0 : data.widget) ? { primaryColor: data.widget.primaryColor, buttonLabel: data.widget.buttonLabel, buttonPosition: data.widget.buttonPosition } : undefined, localTheme);
32
29
  /* Drawer open state */
33
30
  const [isOpen, setIsOpen] = useState(false);
34
- const [isMaximized, setIsMaximized] = useState(false);
35
31
  const [closing, setClosing] = useState(false); // for slide-out animation
36
32
  /* Navigation */
37
33
  const [activeTab, setActiveTab] = useState('home');
@@ -168,7 +164,6 @@ export const ChatWidget = ({ theme: localTheme }) => {
168
164
  }, [endCall]);
169
165
  /* ── Derived ─────────────────────────────────────────────────────────── */
170
166
  const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
171
- const drawerW = isMaximized ? DRAWER_W_MAX : DRAWER_W_NORMAL;
172
167
  const widgetConfig = data === null || data === void 0 ? void 0 : data.widget;
173
168
  const primaryColor = theme.primaryColor;
174
169
  const allUsers = data ? [...data.developers, ...data.users] : [];
@@ -209,9 +204,20 @@ export const ChatWidget = ({ theme: localTheme }) => {
209
204
  const posStyle = theme.buttonPosition === 'bottom-left'
210
205
  ? { left: 24, right: 'auto' }
211
206
  : { right: 24, left: 'auto' };
207
+ /* No radius on top-left / bottom-left; left-docked panel keeps inner TR/BR curve */
212
208
  const drawerPosStyle = theme.buttonPosition === 'bottom-left'
213
- ? { left: 0, borderRadius: '0 16px 16px 0' }
214
- : { right: 0, borderRadius: '16px 0 0 16px' };
209
+ ? {
210
+ left: 0,
211
+ borderTopLeftRadius: 0,
212
+ borderBottomLeftRadius: 0,
213
+ borderTopRightRadius: 16,
214
+ borderBottomRightRadius: 16,
215
+ }
216
+ : {
217
+ right: 0,
218
+ borderTopLeftRadius: 0,
219
+ borderBottomLeftRadius: 0,
220
+ };
215
221
  /* ── Don't render until mounted (SSR safe) ──────────────────────────── */
216
222
  if (!mounted)
217
223
  return null;
@@ -235,6 +241,15 @@ export const ChatWidget = ({ theme: localTheme }) => {
235
241
 
236
242
  .cw-drawer-enter { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideInLeft' : 'cw-slideInRight'} 0.32s cubic-bezier(0.22,1,0.36,1) both; }
237
243
  .cw-drawer-exit { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideOutLeft' : 'cw-slideOutRight'} 0.28s cubic-bezier(0.55,0,1,0.45) both; }
244
+
245
+ .cw-drawer-panel {
246
+ width: 30%;
247
+ max-width: 100vw;
248
+ min-width: 0;
249
+ }
250
+ @media (max-width: 1024px) {
251
+ .cw-drawer-panel { width: 100%; }
252
+ }
238
253
  ` }), !isOpen && (_jsxs("button", { className: "cw-root", onClick: openDrawer, "aria-label": theme.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: 24, zIndex: 9999 }, posStyle), { display: 'flex', alignItems: 'center', gap: 10, padding: '13px 22px', backgroundColor: theme.buttonColor, color: theme.buttonTextColor, border: 'none', borderRadius: 50, cursor: 'pointer', fontSize: 15, fontWeight: 700, boxShadow: `0 8px 28px ${theme.buttonColor}55`, animation: 'cw-btnPop 0.4s cubic-bezier(0.34,1.56,0.64,1)', transition: 'transform 0.2s, box-shadow 0.2s' }), onMouseEnter: e => {
239
254
  e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
240
255
  e.currentTarget.style.boxShadow = `0 14px 36px ${theme.buttonColor}66`;
@@ -246,21 +261,19 @@ export const ChatWidget = ({ theme: localTheme }) => {
246
261
  backgroundColor: 'rgba(0,0,0,0.35)',
247
262
  opacity: closing ? 0 : 1,
248
263
  transition: 'opacity 0.3s',
249
- } })), isOpen && (_jsxs("div", { className: `cw-root ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, width: drawerW, maxWidth: '100vw', backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
264
+ } })), isOpen && (_jsxs("div", { className: `cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`, style: Object.assign(Object.assign({ position: 'fixed', top: 0, bottom: 0 }, drawerPosStyle), { zIndex: 9998, backgroundColor: '#fff', boxShadow: theme.buttonPosition === 'bottom-left'
250
265
  ? '4px 0 40px rgba(0,0,0,0.18)'
251
- : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden', transition: 'width 0.28s ease' }), children: [cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 }, children: [_jsx("div", { style: {
266
+ : '-4px 0 40px rgba(0,0,0,0.18)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }), children: [cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 16 }, children: [_jsx("div", { style: {
252
267
  width: 40, height: 40, borderRadius: '50%',
253
268
  border: `3px solid ${primaryColor}30`,
254
269
  borderTopColor: primaryColor,
255
270
  animation: 'spin 0.8s linear infinite',
256
- } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }), _jsx("p", { style: { fontSize: 14, color: '#7b8fa1' }, children: "Loading chat\u2026" })] })), cfgError && !cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\u26A0\uFE0F" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Could not load chat configuration" }), _jsx("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 }, children: cfgError }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), !cfgLoading && !cfgError && widgetConfig && (_jsxs(_Fragment, { children: [screen !== 'chat' && screen !== 'call' && (_jsxs("div", { style: {
271
+ } }), _jsx("style", { children: `@keyframes spin { to { transform: rotate(360deg); } }` }), _jsx("p", { style: { fontSize: 14, color: '#7b8fa1' }, children: "Loading chat\u2026" })] })), cfgError && !cfgLoading && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', gap: 12, padding: 32, textAlign: 'center' }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\u26A0\uFE0F" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Could not load chat configuration" }), _jsx("p", { style: { fontSize: 13, color: '#7b8fa1', lineHeight: 1.6 }, children: cfgError }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), !cfgLoading && !cfgError && widgetConfig && (_jsxs(_Fragment, { children: [screen !== 'chat' && screen !== 'call' && (_jsx("div", { style: {
257
272
  position: 'absolute', top: 12,
258
273
  right: theme.buttonPosition === 'bottom-left' ? 'auto' : 12,
259
274
  left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
260
275
  zIndex: 20, display: 'flex', gap: 6,
261
- }, children: [_jsx(CornerBtn, { onClick: () => setIsMaximized(m => !m), title: isMaximized ? 'Minimize' : 'Maximize', children: isMaximized
262
- ? _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3v5H3M21 8h-5V3M3 16h5v5M16 21v-5h5", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) })
263
- : _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3", stroke: "#fff", strokeWidth: "2.2", strokeLinecap: "round" }) }) }), _jsx(CornerBtn, { onClick: closeDrawer, title: "Close", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) })] })), widgetConfig.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: primaryColor })), widgetConfig.status === 'DISABLE' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\uD83D\uDD12" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Chat is disabled" }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), widgetConfig.status === 'ACTIVE' && (_jsxs("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [screen === 'home' && (_jsx(HomeScreen, { config: widgetConfig, onNavigate: handleCardClick, tickets: tickets })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_d = widgetConfig.viewerType) !== null && _d !== void 0 ? _d : 'user', onBack: () => setScreen('home'), onSelectUser: handleSelectUser })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: () => { clearChat(); setScreen('home'); setActiveTab('home'); }, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper })), screen === 'call' && callSession.peer && (_jsx(CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: handleSelectUser })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onRaiseTicket: handleRaiseTicket })), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
276
+ }, children: _jsx(CornerBtn, { onClick: closeDrawer, title: "Close", children: _jsx("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M18 6L6 18M6 6l12 12", stroke: "#fff", strokeWidth: "2.5", strokeLinecap: "round" }) }) }) })), widgetConfig.status === 'MAINTENANCE' && (_jsx(MaintenanceView, { primaryColor: primaryColor })), widgetConfig.status === 'DISABLE' && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', padding: 32, textAlign: 'center', gap: 12 }, children: [_jsx("div", { style: { fontSize: 40 }, children: "\uD83D\uDD12" }), _jsx("p", { style: { fontWeight: 700, color: '#1a2332' }, children: "Chat is disabled" }), _jsx("button", { onClick: closeDrawer, style: { padding: '9px 20px', borderRadius: 10, border: 'none', background: primaryColor, color: '#fff', cursor: 'pointer', fontWeight: 700 }, children: "Close" })] })), widgetConfig.status === 'ACTIVE' && (_jsxs("div", { className: "cw-scroll", style: { flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [screen === 'home' && (_jsx(HomeScreen, { config: widgetConfig, onNavigate: handleCardClick, tickets: tickets })), screen === 'user-list' && (_jsx(UserListScreen, { context: userListCtx, users: filteredUsers, primaryColor: primaryColor, viewerType: (_d = widgetConfig.viewerType) !== null && _d !== void 0 ? _d : 'user', onBack: () => setScreen('home'), onSelectUser: handleSelectUser })), screen === 'chat' && activeUser && (_jsx(ChatScreen, { activeUser: activeUser, messages: messages, config: widgetConfig, isPaused: isPaused, isReported: isReported, isBlocked: isBlocked, onSend: sendMessage, onBack: () => { clearChat(); setScreen('home'); setActiveTab('home'); }, onClose: closeDrawer, onTogglePause: handleTogglePause, onReport: reportChat, onBlock: handleBlock, onStartCall: handleStartCall, onNavAction: handleNavFromMenu, otherDevelopers: otherDevelopers, onTransferToDeveloper: handleTransferToDeveloper })), screen === 'call' && callSession.peer && (_jsx(CallScreen, { session: callSession, localVideoRef: localVideoRef, remoteVideoRef: remoteVideoRef, onEnd: handleEndCall, onToggleMute: toggleMute, onToggleCamera: toggleCamera, primaryColor: primaryColor })), screen === 'recent-chats' && (_jsx(RecentChatsScreen, { chats: recentChats, config: widgetConfig, onSelectChat: handleSelectUser })), screen === 'tickets' && (_jsx(TicketScreen, { tickets: tickets, config: widgetConfig, onRaiseTicket: handleRaiseTicket })), screen === 'block-list' && (_jsx(BlockListScreen, { blockedUsers: blockedUsers, config: widgetConfig, onUnblock: handleUnblock, onBack: () => { setScreen('home'); setActiveTab('home'); } }))] })), widgetConfig.status === 'ACTIVE' &&
264
277
  screen !== 'chat' &&
265
278
  screen !== 'call' &&
266
279
  screen !== 'user-list' &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ajaxter-chat",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "description": "Drawer-based chat widget with support chat, tickets, WebRTC calling, voice messages, block list, and transcript download.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, { useState, useEffect, useCallback, useRef } from 'react';
3
+ import React, { useState, useEffect, useCallback } from 'react';
4
4
  import { ChatWidgetProps, BottomTab, Screen, UserListContext, ChatUser, Ticket, RecentChat, ChatMessage } from '../types';
5
5
  import { loadLocalConfig } from '../config';
6
6
  import { mergeTheme } from '../utils/theme';
@@ -18,10 +18,6 @@ import { CallScreen } from './CallScreen';
18
18
  import { MaintenanceView } from './MaintenanceView';
19
19
  import { BottomTabs } from './Tabs/BottomTabs';
20
20
 
21
- /* ─── Drawer width ─────────────────────────────────────────────────────────── */
22
- const DRAWER_W_NORMAL = 380;
23
- const DRAWER_W_MAX = 480;
24
-
25
21
  export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) => {
26
22
  /* SSR guard */
27
23
  const [mounted, setMounted] = useState(false);
@@ -41,7 +37,6 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
41
37
 
42
38
  /* Drawer open state */
43
39
  const [isOpen, setIsOpen] = useState(false);
44
- const [isMaximized, setIsMaximized] = useState(false);
45
40
  const [closing, setClosing] = useState(false); // for slide-out animation
46
41
 
47
42
  /* Navigation */
@@ -191,7 +186,6 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
191
186
 
192
187
  /* ── Derived ─────────────────────────────────────────────────────────── */
193
188
  const isBlocked = activeUser ? blockedUids.includes(activeUser.uid) : false;
194
- const drawerW = isMaximized ? DRAWER_W_MAX : DRAWER_W_NORMAL;
195
189
  const widgetConfig = data?.widget;
196
190
  const primaryColor = theme.primaryColor;
197
191
 
@@ -235,9 +229,21 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
235
229
  ? { left: 24, right: 'auto' }
236
230
  : { right: 24, left: 'auto' };
237
231
 
238
- const drawerPosStyle: React.CSSProperties = theme.buttonPosition === 'bottom-left'
239
- ? { left: 0, borderRadius: '0 16px 16px 0' }
240
- : { right: 0, borderRadius: '16px 0 0 16px' };
232
+ /* No radius on top-left / bottom-left; left-docked panel keeps inner TR/BR curve */
233
+ const drawerPosStyle: React.CSSProperties =
234
+ theme.buttonPosition === 'bottom-left'
235
+ ? {
236
+ left: 0,
237
+ borderTopLeftRadius: 0,
238
+ borderBottomLeftRadius: 0,
239
+ borderTopRightRadius: 16,
240
+ borderBottomRightRadius: 16,
241
+ }
242
+ : {
243
+ right: 0,
244
+ borderTopLeftRadius: 0,
245
+ borderBottomLeftRadius: 0,
246
+ };
241
247
 
242
248
  /* ── Don't render until mounted (SSR safe) ──────────────────────────── */
243
249
  if (!mounted) return null;
@@ -265,6 +271,15 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
265
271
 
266
272
  .cw-drawer-enter { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideInLeft' : 'cw-slideInRight'} 0.32s cubic-bezier(0.22,1,0.36,1) both; }
267
273
  .cw-drawer-exit { animation: ${theme.buttonPosition === 'bottom-left' ? 'cw-slideOutLeft' : 'cw-slideOutRight'} 0.28s cubic-bezier(0.55,0,1,0.45) both; }
274
+
275
+ .cw-drawer-panel {
276
+ width: 30%;
277
+ max-width: 100vw;
278
+ min-width: 0;
279
+ }
280
+ @media (max-width: 1024px) {
281
+ .cw-drawer-panel { width: 100%; }
282
+ }
268
283
  `}</style>
269
284
 
270
285
  {/* ── Floating Button ── */}
@@ -319,15 +334,13 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
319
334
  {/* ── Drawer / Slider ── */}
320
335
  {isOpen && (
321
336
  <div
322
- className={`cw-root ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`}
337
+ className={`cw-root cw-drawer-panel ${closing ? 'cw-drawer-exit' : 'cw-drawer-enter'}`}
323
338
  style={{
324
339
  position: 'fixed',
325
340
  top: 0,
326
341
  bottom: 0,
327
342
  ...drawerPosStyle,
328
343
  zIndex: 9998,
329
- width: drawerW,
330
- maxWidth: '100vw',
331
344
  backgroundColor: '#fff',
332
345
  boxShadow: theme.buttonPosition === 'bottom-left'
333
346
  ? '4px 0 40px rgba(0,0,0,0.18)'
@@ -335,7 +348,6 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
335
348
  display: 'flex',
336
349
  flexDirection: 'column',
337
350
  overflow: 'hidden',
338
- transition: 'width 0.28s ease',
339
351
  }}
340
352
  >
341
353
  {/* ── Loading state ── */}
@@ -373,12 +385,6 @@ export const ChatWidget: React.FC<ChatWidgetProps> = ({ theme: localTheme }) =>
373
385
  left: theme.buttonPosition === 'bottom-left' ? 12 : 'auto',
374
386
  zIndex: 20, display: 'flex', gap: 6,
375
387
  }}>
376
- <CornerBtn onClick={() => setIsMaximized(m => !m)} title={isMaximized ? 'Minimize' : 'Maximize'}>
377
- {isMaximized
378
- ? <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M8 3v5H3M21 8h-5V3M3 16h5v5M16 21v-5h5" stroke="#fff" strokeWidth="2.2" strokeLinecap="round"/></svg>
379
- : <svg width="12" height="12" viewBox="0 0 24 24" fill="none"><path d="M8 3H5a2 2 0 00-2 2v3M21 8V5a2 2 0 00-2-2h-3M3 16v3a2 2 0 002 2h3M16 21h3a2 2 0 002-2v-3" stroke="#fff" strokeWidth="2.2" strokeLinecap="round"/></svg>
380
- }
381
- </CornerBtn>
382
388
  <CornerBtn onClick={closeDrawer} title="Close">
383
389
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none">
384
390
  <path d="M18 6L6 18M6 6l12 12" stroke="#fff" strokeWidth="2.5" strokeLinecap="round"/>