ajaxter-chat 1.0.3 → 3.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.
Files changed (75) hide show
  1. package/README.md +124 -241
  2. package/dist/components/BlockList/index.d.ts +10 -0
  3. package/dist/components/BlockList/index.js +33 -0
  4. package/dist/components/CallScreen/index.d.ts +13 -0
  5. package/dist/components/CallScreen/index.js +48 -0
  6. package/dist/components/ChatScreen/index.d.ts +19 -0
  7. package/dist/components/ChatScreen/index.js +168 -0
  8. package/dist/components/ChatWidget.d.ts +0 -24
  9. package/dist/components/ChatWidget.js +228 -43
  10. package/dist/components/EmojiPicker/index.d.ts +8 -0
  11. package/dist/components/EmojiPicker/index.js +18 -0
  12. package/dist/components/HomeScreen/index.d.ts +8 -0
  13. package/dist/components/HomeScreen/index.js +55 -0
  14. package/dist/components/MaintenanceView/index.d.ts +0 -1
  15. package/dist/components/MaintenanceView/index.js +13 -52
  16. package/dist/components/RecentChatsScreen/index.d.ts +17 -0
  17. package/dist/components/RecentChatsScreen/index.js +8 -0
  18. package/dist/components/Tabs/BottomTabs.d.ts +10 -0
  19. package/dist/components/Tabs/BottomTabs.js +34 -0
  20. package/dist/components/TicketScreen/index.d.ts +9 -0
  21. package/dist/components/TicketScreen/index.js +54 -0
  22. package/dist/components/UserListScreen/index.d.ts +11 -0
  23. package/dist/components/UserListScreen/index.js +35 -0
  24. package/dist/config/index.d.ts +3 -16
  25. package/dist/config/index.js +20 -103
  26. package/dist/hooks/useChat.d.ts +10 -9
  27. package/dist/hooks/useChat.js +22 -40
  28. package/dist/hooks/useRemoteConfig.d.ts +6 -0
  29. package/dist/hooks/useRemoteConfig.js +22 -0
  30. package/dist/hooks/useWebRTC.d.ts +11 -0
  31. package/dist/hooks/useWebRTC.js +112 -0
  32. package/dist/index.d.ts +16 -11
  33. package/dist/index.js +15 -16
  34. package/dist/types/index.d.ts +66 -38
  35. package/dist/utils/chat.d.ts +13 -0
  36. package/dist/utils/chat.js +62 -0
  37. package/dist/utils/theme.d.ts +3 -2
  38. package/dist/utils/theme.js +13 -21
  39. package/package.json +10 -20
  40. package/public/chatData.json +162 -0
  41. package/src/components/BlockList/index.tsx +94 -0
  42. package/src/components/CallScreen/index.tsx +144 -0
  43. package/src/components/ChatScreen/index.tsx +469 -0
  44. package/src/components/ChatWidget.tsx +471 -0
  45. package/src/components/EmojiPicker/index.tsx +48 -0
  46. package/src/components/HomeScreen/index.tsx +106 -0
  47. package/src/components/MaintenanceView/index.tsx +38 -0
  48. package/src/components/RecentChatsScreen/index.tsx +63 -0
  49. package/src/components/Tabs/BottomTabs.tsx +90 -0
  50. package/src/components/TicketScreen/index.tsx +124 -0
  51. package/src/components/UserListScreen/index.tsx +103 -0
  52. package/src/config/index.ts +40 -0
  53. package/src/hooks/useChat.ts +48 -0
  54. package/src/hooks/useRemoteConfig.ts +20 -0
  55. package/src/hooks/useWebRTC.ts +130 -0
  56. package/src/index.ts +29 -0
  57. package/src/types/index.ts +127 -0
  58. package/src/utils/chat.ts +70 -0
  59. package/src/utils/theme.ts +27 -0
  60. package/dist/components/BottomNav/index.d.ts +0 -10
  61. package/dist/components/BottomNav/index.js +0 -32
  62. package/dist/components/ChatBox/index.d.ts +0 -15
  63. package/dist/components/ChatBox/index.js +0 -228
  64. package/dist/components/ChatButton/index.d.ts +0 -9
  65. package/dist/components/ChatButton/index.js +0 -17
  66. package/dist/components/ChatWindow/index.d.ts +0 -10
  67. package/dist/components/ChatWindow/index.js +0 -286
  68. package/dist/components/HomeView/index.d.ts +0 -12
  69. package/dist/components/HomeView/index.js +0 -51
  70. package/dist/components/UserList/index.d.ts +0 -13
  71. package/dist/components/UserList/index.js +0 -136
  72. package/dist/hooks/useUsers.d.ts +0 -14
  73. package/dist/hooks/useUsers.js +0 -32
  74. package/dist/services/userService.d.ts +0 -7
  75. package/dist/services/userService.js +0 -18
@@ -0,0 +1,127 @@
1
+ // ─── Remote Config (from chatData.json) ────────────────────────────────────
2
+ export interface WidgetConfig {
3
+ id: string;
4
+ apiKey: string;
5
+ status: ChatStatus;
6
+ chatType: ChatType;
7
+ primaryColor: string;
8
+ buttonLabel: string;
9
+ buttonPosition: 'bottom-right' | 'bottom-left';
10
+ welcomeTitle: string;
11
+ welcomeSubtitle: string;
12
+ allowVoiceMessage: boolean;
13
+ allowAttachment: boolean;
14
+ allowEmoji: boolean;
15
+ allowWebCall: boolean;
16
+ maxEmojiCount: number;
17
+ allowTranscriptDownload: boolean;
18
+ allowReport: boolean;
19
+ allowBlock: boolean;
20
+ }
21
+
22
+ export interface RemoteChatData {
23
+ widget: WidgetConfig;
24
+ developers: ChatUser[];
25
+ users: ChatUser[];
26
+ sampleChats: Record<string, ChatMessage[]>;
27
+ sampleTickets: Ticket[];
28
+ blockedUsers: string[];
29
+ }
30
+
31
+ // ─── Enums ──────────────────────────────────────────────────────────────────
32
+ export type ChatStatus = 'ACTIVE' | 'DISABLE' | 'MAINTENANCE';
33
+ export type ChatType = 'SUPPORT' | 'CHAT' | 'BOTH';
34
+ export type UserType = 'developer' | 'user';
35
+ export type OnlineStatus = 'online' | 'away' | 'offline';
36
+ export type BottomTab = 'home' | 'chats' | 'tickets';
37
+ export type Screen =
38
+ | 'home'
39
+ | 'user-list'
40
+ | 'chat'
41
+ | 'recent-chats'
42
+ | 'tickets'
43
+ | 'block-list'
44
+ | 'call';
45
+ export type UserListContext = 'support' | 'conversation';
46
+ export type MessageType = 'text' | 'voice' | 'attachment' | 'emoji';
47
+
48
+ // ─── User ───────────────────────────────────────────────────────────────────
49
+ export interface ChatUser {
50
+ uid: string;
51
+ name: string;
52
+ email: string;
53
+ mobile: string;
54
+ project: string;
55
+ type: UserType;
56
+ avatar: string | null;
57
+ status: OnlineStatus;
58
+ designation: string;
59
+ }
60
+
61
+ // ─── Message ────────────────────────────────────────────────────────────────
62
+ export interface ChatMessage {
63
+ id: string;
64
+ senderId: string;
65
+ receiverId: string;
66
+ text: string;
67
+ timestamp: string;
68
+ type: MessageType;
69
+ status: 'sent' | 'delivered' | 'read';
70
+ attachmentName?: string;
71
+ attachmentSize?: string;
72
+ voiceDuration?: number; // seconds
73
+ }
74
+
75
+ // ─── Ticket ─────────────────────────────────────────────────────────────────
76
+ export interface Ticket {
77
+ id: string;
78
+ title: string;
79
+ description: string;
80
+ status: 'open' | 'in-progress' | 'resolved' | 'closed';
81
+ priority: 'low' | 'medium' | 'high';
82
+ createdAt: string;
83
+ updatedAt: string;
84
+ assignedTo: string | null;
85
+ }
86
+
87
+ // ─── Recent Chat ─────────────────────────────────────────────────────────────
88
+ export interface RecentChat {
89
+ id: string;
90
+ user: ChatUser;
91
+ lastMessage: string;
92
+ lastTime: string;
93
+ unread: number;
94
+ isPaused: boolean;
95
+ }
96
+
97
+ // ─── Call ───────────────────────────────────────────────────────────────────
98
+ export type CallState = 'idle' | 'calling' | 'connected' | 'ended';
99
+
100
+ export interface CallSession {
101
+ state: CallState;
102
+ peer: ChatUser | null;
103
+ startedAt: Date | null;
104
+ isMuted: boolean;
105
+ isCameraOn: boolean;
106
+ }
107
+
108
+ // ─── Local env config ────────────────────────────────────────────────────────
109
+ export interface LocalEnvConfig {
110
+ apiKey: string;
111
+ widgetId: string;
112
+ }
113
+
114
+ // ─── Theme prop (still overridable) ─────────────────────────────────────────
115
+ export interface ChatWidgetTheme {
116
+ primaryColor?: string;
117
+ fontFamily?: string;
118
+ buttonColor?: string;
119
+ buttonTextColor?: string;
120
+ buttonLabel?: string;
121
+ buttonPosition?: 'bottom-right' | 'bottom-left';
122
+ borderRadius?: string;
123
+ }
124
+
125
+ export interface ChatWidgetProps {
126
+ theme?: ChatWidgetTheme;
127
+ }
@@ -0,0 +1,70 @@
1
+ import { ChatMessage, ChatUser } from '../types';
2
+
3
+ /** Generate a consistent avatar color from a name */
4
+ export function avatarColor(name: string): string {
5
+ const palette = [
6
+ '#2563EB','#7C3AED','#059669','#D97706',
7
+ '#DC2626','#0891B2','#4F46E5','#BE185D',
8
+ ];
9
+ let hash = 0;
10
+ for (let i = 0; i < name.length; i++) hash = name.charCodeAt(i) + ((hash << 5) - hash);
11
+ return palette[Math.abs(hash) % palette.length];
12
+ }
13
+
14
+ /** Get initials from full name */
15
+ export function initials(name: string): string {
16
+ return name.split(' ').map(n => n[0]).join('').toUpperCase().slice(0, 2);
17
+ }
18
+
19
+ /** Format timestamp to readable time string */
20
+ export function formatTime(ts: string | Date): string {
21
+ return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
22
+ }
23
+
24
+ /** Format date for chat separators */
25
+ export function formatDate(ts: string | Date): string {
26
+ const d = new Date(ts);
27
+ const today = new Date();
28
+ const yesterday = new Date(today); yesterday.setDate(today.getDate() - 1);
29
+ if (d.toDateString() === today.toDateString()) return 'Today';
30
+ if (d.toDateString() === yesterday.toDateString()) return 'Yesterday';
31
+ return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' });
32
+ }
33
+
34
+ /** Generate a plain-text transcript from messages */
35
+ export function generateTranscript(
36
+ messages: ChatMessage[],
37
+ peer: ChatUser,
38
+ myName = 'Me'
39
+ ): string {
40
+ const header = [
41
+ '═══════════════════════════════════════',
42
+ ' CHAT TRANSCRIPT',
43
+ ` Conversation with: ${peer.name} (${peer.email})`,
44
+ ` Downloaded: ${new Date().toLocaleString()}`,
45
+ '═══════════════════════════════════════',
46
+ '',
47
+ ].join('\n');
48
+
49
+ const body = messages.map(m => {
50
+ const sender = m.senderId === 'me' ? myName : peer.name;
51
+ const time = new Date(m.timestamp).toLocaleString();
52
+ const label = m.type === 'voice' ? '[Voice Message]'
53
+ : m.type === 'attachment' ? `[Attachment: ${m.attachmentName ?? 'file'}]`
54
+ : m.text;
55
+ return `[${time}] ${sender}: ${label}`;
56
+ }).join('\n');
57
+
58
+ return header + body;
59
+ }
60
+
61
+ /** Trigger a file download in the browser */
62
+ export function downloadText(content: string, filename: string) {
63
+ const blob = new Blob([content], { type: 'text/plain' });
64
+ const url = URL.createObjectURL(blob);
65
+ const a = document.createElement('a');
66
+ a.href = url;
67
+ a.download = filename;
68
+ a.click();
69
+ URL.revokeObjectURL(url);
70
+ }
@@ -0,0 +1,27 @@
1
+ import { ChatWidgetTheme } from '../types';
2
+
3
+ export const defaultTheme: Required<ChatWidgetTheme> = {
4
+ primaryColor: '#2563EB',
5
+ fontFamily: "'DM Sans', 'Segoe UI', sans-serif",
6
+ buttonColor: '#2563EB',
7
+ buttonTextColor: '#ffffff',
8
+ buttonLabel: 'Support',
9
+ buttonPosition: 'bottom-right',
10
+ borderRadius: '0px', // drawer uses 16px only on top-left / bottom-left
11
+ };
12
+
13
+ export function mergeTheme(
14
+ remote?: Partial<ChatWidgetTheme>,
15
+ local?: ChatWidgetTheme
16
+ ): Required<ChatWidgetTheme> {
17
+ return { ...defaultTheme, ...remote, ...local };
18
+ }
19
+
20
+ /** Darken a hex color by `amount` (0-255) */
21
+ export function darken(hex: string, amount = 20): string {
22
+ const n = parseInt(hex.replace('#', ''), 16);
23
+ const r = Math.max(0, (n >> 16) - amount);
24
+ const g = Math.max(0, ((n >> 8) & 0xff) - amount);
25
+ const b = Math.max(0, (n & 0xff) - amount);
26
+ return `#${((r << 16) | (g << 8) | b).toString(16).padStart(6, '0')}`;
27
+ }
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { BottomNavTab } from '../../types';
3
- interface BottomNavProps {
4
- active: BottomNavTab;
5
- onChange: (tab: BottomNavTab) => void;
6
- primaryColor: string;
7
- fontFamily: string;
8
- }
9
- export declare const BottomNav: React.FC<BottomNavProps>;
10
- export {};
@@ -1,32 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- export const BottomNav = ({ active, onChange, primaryColor, fontFamily, }) => {
3
- const item = (tab, label, icon) => {
4
- const isOn = active === tab;
5
- return (_jsxs("button", { type: "button", onClick: () => onChange(tab), style: {
6
- flex: 1,
7
- display: 'flex',
8
- flexDirection: 'column',
9
- alignItems: 'center',
10
- justifyContent: 'center',
11
- gap: '4px',
12
- padding: '10px 8px',
13
- border: 'none',
14
- background: 'transparent',
15
- cursor: 'pointer',
16
- fontFamily,
17
- color: isOn ? primaryColor : '#b0b0b0',
18
- transition: 'color 0.2s',
19
- }, children: [_jsx("span", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center' }, children: icon }), _jsx("span", { style: { fontSize: '11px', fontWeight: isOn ? 600 : 500 }, children: label })] }));
20
- };
21
- return (_jsxs("nav", { style: {
22
- flexShrink: 0,
23
- display: 'flex',
24
- alignItems: 'stretch',
25
- backgroundColor: '#fff',
26
- borderTop: '1px solid #eee',
27
- paddingBottom: 'env(safe-area-inset-bottom, 0)',
28
- }, "aria-label": "Widget sections", children: [item('home', 'Home', _jsx(HomeIcon, { active: active === 'home', color: primaryColor, muted: "#b0b0b0" })), item('chats', 'Chat', _jsx(ChatBubbleIcon, { active: active === 'chats', color: primaryColor, muted: "#b0b0b0" })), item('tickets', 'Tickets', _jsx(TicketIcon, { active: active === 'tickets', color: primaryColor, muted: "#b0b0b0" }))] }));
29
- };
30
- const HomeIcon = ({ active, color, muted, }) => (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: _jsx("path", { d: "M3 9.5L12 3l9 6.5V20a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1V9.5z", stroke: active ? color : muted, strokeWidth: "2", strokeLinejoin: "round" }) }));
31
- const ChatBubbleIcon = ({ active, color, muted, }) => (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", stroke: active ? color : muted, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }));
32
- const TicketIcon = ({ active, color, muted, }) => (_jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: _jsx("path", { d: "M4 4h16v4H4V4zm0 6h10v10H4V10zm12 0h4v10h-4V10z", stroke: active ? color : muted, strokeWidth: "2", strokeLinejoin: "round" }) }));
@@ -1,15 +0,0 @@
1
- import React from 'react';
2
- import { ChatMessage, ChatUser } from '../../types';
3
- export interface ChatBoxProps {
4
- activeUser: ChatUser | null;
5
- messages: ChatMessage[];
6
- onSendMessage: (text: string) => void;
7
- primaryColor: string;
8
- fontFamily: string;
9
- /** Minimal: teal bar with back/close, footer with extra actions (reference UI). */
10
- variant?: 'default' | 'minimal';
11
- onBack?: () => void;
12
- onClose?: () => void;
13
- headerTitle?: string;
14
- }
15
- export declare const ChatBox: React.FC<ChatBoxProps>;
@@ -1,228 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useRef, useEffect } from 'react';
3
- export const ChatBox = ({ activeUser, messages, onSendMessage, primaryColor, fontFamily, variant = 'default', onBack, onClose, headerTitle, }) => {
4
- const [inputText, setInputText] = useState('');
5
- const messagesEndRef = useRef(null);
6
- const inputRef = useRef(null);
7
- useEffect(() => {
8
- var _a;
9
- (_a = messagesEndRef.current) === null || _a === void 0 ? void 0 : _a.scrollIntoView({ behavior: 'smooth' });
10
- }, [messages]);
11
- const handleSend = () => {
12
- var _a;
13
- if (!inputText.trim())
14
- return;
15
- onSendMessage(inputText);
16
- setInputText('');
17
- (_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus();
18
- };
19
- const handleKeyDown = (e) => {
20
- if (e.key === 'Enter' && !e.shiftKey) {
21
- e.preventDefault();
22
- handleSend();
23
- }
24
- };
25
- if (variant === 'minimal') {
26
- return (_jsxs("div", { style: {
27
- flex: 1,
28
- display: 'flex',
29
- flexDirection: 'column',
30
- fontFamily,
31
- overflow: 'hidden',
32
- backgroundColor: '#fff',
33
- }, children: [_jsxs("div", { style: {
34
- flexShrink: 0,
35
- padding: '14px 16px',
36
- backgroundColor: primaryColor,
37
- color: '#fff',
38
- display: 'flex',
39
- alignItems: 'center',
40
- justifyContent: 'space-between',
41
- }, children: [_jsx("button", { type: "button", onClick: onBack, "aria-label": "Back", style: {
42
- background: 'transparent',
43
- border: 'none',
44
- color: '#fff',
45
- cursor: 'pointer',
46
- padding: '4px',
47
- display: 'flex',
48
- alignItems: 'center',
49
- }, children: _jsx("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M15 18L9 12L15 6", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }), _jsx("div", { style: { fontWeight: 600, fontSize: '15px', flex: 1, textAlign: 'center' }, children: headerTitle !== null && headerTitle !== void 0 ? headerTitle : (activeUser ? activeUser.name : 'Chat') }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close", style: {
50
- background: 'transparent',
51
- border: 'none',
52
- color: '#fff',
53
- cursor: 'pointer',
54
- padding: '4px',
55
- display: 'flex',
56
- }, children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" })] }) })] }), _jsxs("div", { style: {
57
- flex: 1,
58
- overflowY: 'auto',
59
- padding: '16px',
60
- backgroundColor: '#fff',
61
- }, children: [!activeUser ? (_jsx("div", { style: { textAlign: 'center', color: '#bbb', fontSize: '14px', marginTop: '32px' }, children: "Select someone to chat" })) : messages.length === 0 ? (_jsxs("div", { style: { textAlign: 'center', color: '#ccc', fontSize: '13px', marginTop: '32px' }, children: ["Say hello to ", activeUser.name, "! \uD83D\uDC4B"] })) : (messages.map((msg) => (_jsx(MessageBubble, { message: msg, primaryColor: primaryColor }, msg.id)))), _jsx("div", { ref: messagesEndRef })] }), _jsxs("div", { style: {
62
- borderTop: '1px solid #eee',
63
- backgroundColor: '#fff',
64
- padding: '10px 12px',
65
- display: 'flex',
66
- alignItems: 'center',
67
- gap: '8px',
68
- }, children: [_jsx("textarea", { ref: inputRef, value: inputText, onChange: (e) => setInputText(e.target.value), onKeyDown: handleKeyDown, placeholder: "Type and press [enter]..", rows: 1, disabled: !activeUser, style: {
69
- flex: 1,
70
- resize: 'none',
71
- border: 'none',
72
- outline: 'none',
73
- fontFamily,
74
- fontSize: '14px',
75
- color: '#1a1a2e',
76
- maxHeight: '80px',
77
- lineHeight: 1.45,
78
- } }), _jsx(IconButton, { label: "Like", disabled: !activeUser, children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "#888", strokeWidth: "1.8", children: _jsx("path", { d: "M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3" }) }) }), _jsx(IconButton, { label: "Attach", disabled: !activeUser, children: _jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "#888", strokeWidth: "1.8", children: _jsx("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) }), _jsx(IconButton, { label: "Emoji", disabled: !activeUser, children: _jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "#888", strokeWidth: "1.8", children: [_jsx("circle", { cx: "12", cy: "12", r: "10" }), _jsx("path", { d: "M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01" })] }) })] })] }));
79
- }
80
- if (!activeUser) {
81
- return (_jsxs("div", { style: {
82
- flex: 1,
83
- display: 'flex',
84
- flexDirection: 'column',
85
- alignItems: 'center',
86
- justifyContent: 'center',
87
- fontFamily,
88
- gap: '16px',
89
- padding: '24px',
90
- textAlign: 'center',
91
- }, children: [_jsx("div", { style: {
92
- width: '80px',
93
- height: '80px',
94
- borderRadius: '50%',
95
- backgroundColor: `${primaryColor}12`,
96
- display: 'flex',
97
- alignItems: 'center',
98
- justifyContent: 'center',
99
- }, children: _jsx("svg", { width: "36", height: "36", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", stroke: primaryColor, strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", fill: `${primaryColor}15` }) }) }), _jsxs("div", { children: [_jsx("p", { style: {
100
- margin: '0 0 6px',
101
- fontWeight: 700,
102
- fontSize: '16px',
103
- color: '#1a1a2e',
104
- letterSpacing: '-0.02em',
105
- }, children: "Select a conversation" }), _jsx("p", { style: { margin: 0, fontSize: '13px', color: '#aaa', lineHeight: 1.6 }, children: "Choose someone from the list to start chatting" })] })] }));
106
- }
107
- const initials = activeUser.name
108
- .split(' ')
109
- .map((n) => n[0])
110
- .join('')
111
- .toUpperCase()
112
- .slice(0, 2);
113
- return (_jsxs("div", { style: {
114
- flex: 1,
115
- display: 'flex',
116
- flexDirection: 'column',
117
- fontFamily,
118
- overflow: 'hidden',
119
- }, children: [_jsxs("div", { style: {
120
- padding: '14px 20px',
121
- borderBottom: '1px solid #f0f0f5',
122
- display: 'flex',
123
- alignItems: 'center',
124
- gap: '12px',
125
- background: '#fafafa',
126
- }, children: [_jsx("div", { style: {
127
- width: '38px',
128
- height: '38px',
129
- borderRadius: '50%',
130
- backgroundColor: primaryColor,
131
- display: 'flex',
132
- alignItems: 'center',
133
- justifyContent: 'center',
134
- color: '#fff',
135
- fontSize: '13px',
136
- fontWeight: 700,
137
- }, children: initials }), _jsxs("div", { children: [_jsx("div", { style: { fontWeight: 700, fontSize: '14px', color: '#1a1a2e' }, children: activeUser.name }), _jsxs("div", { style: { fontSize: '12px', color: '#aaa', display: 'flex', alignItems: 'center', gap: '5px' }, children: [_jsx("span", { style: {
138
- width: '6px',
139
- height: '6px',
140
- borderRadius: '50%',
141
- backgroundColor: '#4caf50',
142
- display: 'inline-block',
143
- } }), "Online"] })] })] }), _jsxs("div", { style: {
144
- flex: 1,
145
- overflowY: 'auto',
146
- padding: '20px 16px',
147
- display: 'flex',
148
- flexDirection: 'column',
149
- gap: '10px',
150
- backgroundColor: '#f8f8fc',
151
- }, children: [messages.length === 0 ? (_jsxs("div", { style: {
152
- textAlign: 'center',
153
- color: '#ccc',
154
- fontSize: '13px',
155
- marginTop: 'auto',
156
- marginBottom: 'auto',
157
- }, children: ["Say hello to ", activeUser.name, "! \uD83D\uDC4B"] })) : (messages.map((msg) => (_jsx(MessageBubble, { message: msg, primaryColor: primaryColor }, msg.id)))), _jsx("div", { ref: messagesEndRef })] }), _jsxs("div", { style: {
158
- padding: '12px 16px',
159
- borderTop: '1px solid #f0f0f5',
160
- backgroundColor: '#fff',
161
- display: 'flex',
162
- alignItems: 'flex-end',
163
- gap: '10px',
164
- }, children: [_jsx("textarea", { ref: inputRef, value: inputText, onChange: (e) => setInputText(e.target.value), onKeyDown: handleKeyDown, placeholder: "Type a message... (Enter to send)", rows: 1, style: {
165
- flex: 1,
166
- resize: 'none',
167
- border: '1.5px solid #eee',
168
- borderRadius: '12px',
169
- padding: '10px 14px',
170
- fontFamily,
171
- fontSize: '14px',
172
- outline: 'none',
173
- lineHeight: '1.5',
174
- maxHeight: '90px',
175
- overflowY: 'auto',
176
- transition: 'border-color 0.2s',
177
- color: '#1a1a2e',
178
- }, onFocus: (e) => {
179
- e.target.style.borderColor = primaryColor;
180
- }, onBlur: (e) => {
181
- e.target.style.borderColor = '#eee';
182
- } }), _jsx("button", { onClick: handleSend, disabled: !inputText.trim(), "aria-label": "Send message", style: {
183
- width: '42px',
184
- height: '42px',
185
- borderRadius: '50%',
186
- backgroundColor: inputText.trim() ? primaryColor : '#eee',
187
- border: 'none',
188
- cursor: inputText.trim() ? 'pointer' : 'not-allowed',
189
- display: 'flex',
190
- alignItems: 'center',
191
- justifyContent: 'center',
192
- flexShrink: 0,
193
- transition: 'all 0.2s ease',
194
- }, children: _jsx("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M22 2L11 13M22 2L15 22L11 13L2 9L22 2Z", stroke: inputText.trim() ? '#fff' : '#bbb', strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) })] })] }));
195
- };
196
- const IconButton = ({ label, disabled, children }) => (_jsx("button", { type: "button", "aria-label": label, disabled: disabled, style: {
197
- background: 'transparent',
198
- border: 'none',
199
- padding: '6px',
200
- cursor: disabled ? 'not-allowed' : 'pointer',
201
- opacity: disabled ? 0.45 : 1,
202
- display: 'flex',
203
- alignItems: 'center',
204
- justifyContent: 'center',
205
- }, children: children }));
206
- const MessageBubble = ({ message, primaryColor }) => {
207
- const isMe = message.senderId === 'me';
208
- const time = new Date(message.timestamp).toLocaleTimeString([], {
209
- hour: '2-digit',
210
- minute: '2-digit',
211
- });
212
- return (_jsxs("div", { style: {
213
- display: 'flex',
214
- flexDirection: 'column',
215
- alignItems: isMe ? 'flex-end' : 'flex-start',
216
- gap: '3px',
217
- }, children: [_jsx("div", { style: {
218
- maxWidth: '78%',
219
- padding: '10px 14px',
220
- borderRadius: isMe ? '18px 18px 4px 18px' : '18px 18px 18px 4px',
221
- backgroundColor: isMe ? primaryColor : '#fff',
222
- color: isMe ? '#fff' : '#1a1a2e',
223
- fontSize: '14px',
224
- lineHeight: '1.5',
225
- boxShadow: '0 1px 3px rgba(0,0,0,0.08)',
226
- wordBreak: 'break-word',
227
- }, children: message.text }), _jsx("span", { style: { fontSize: '11px', color: '#bbb', paddingLeft: '4px', paddingRight: '4px' }, children: time })] }));
228
- };
@@ -1,9 +0,0 @@
1
- import React from 'react';
2
- import { ChatWidgetTheme } from '../../types';
3
- interface ChatButtonProps {
4
- isOpen: boolean;
5
- onClick: () => void;
6
- theme?: ChatWidgetTheme;
7
- }
8
- export declare const ChatButton: React.FC<ChatButtonProps>;
9
- export {};
@@ -1,17 +0,0 @@
1
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { mergeTheme } from '../../utils/theme';
3
- export const ChatButton = ({ isOpen, onClick, theme, }) => {
4
- const t = mergeTheme(theme);
5
- const positionStyle = t.buttonPosition === 'bottom-left'
6
- ? { left: '24px', right: 'auto' }
7
- : { right: '24px', left: 'auto' };
8
- return (_jsx("button", { onClick: onClick, "aria-label": isOpen ? 'Close chat' : t.buttonLabel, style: Object.assign(Object.assign({ position: 'fixed', bottom: '24px' }, positionStyle), { zIndex: 9999, display: 'flex', alignItems: 'center', gap: '10px', padding: isOpen ? '14px' : '14px 22px', backgroundColor: t.buttonColor, color: t.buttonTextColor, border: 'none', borderRadius: '50px', cursor: 'pointer', fontFamily: t.fontFamily, fontSize: '15px', fontWeight: 600, boxShadow: `0 8px 32px ${t.buttonColor}55`, transition: 'all 0.3s cubic-bezier(0.34, 1.56, 0.64, 1)', transform: isOpen ? 'scale(0.95)' : 'scale(1)', letterSpacing: '0.01em', minWidth: isOpen ? '52px' : 'auto', justifyContent: 'center' }), onMouseEnter: (e) => {
9
- e.currentTarget.style.transform = 'scale(1.06) translateY(-2px)';
10
- e.currentTarget.style.boxShadow = `0 12px 40px ${t.buttonColor}77`;
11
- }, onMouseLeave: (e) => {
12
- e.currentTarget.style.transform = isOpen ? 'scale(0.95)' : 'scale(1)';
13
- e.currentTarget.style.boxShadow = `0 8px 32px ${t.buttonColor}55`;
14
- }, children: isOpen ? (_jsx(CloseIcon, { color: t.buttonTextColor })) : (_jsxs(_Fragment, { children: [_jsx(ChatIcon, { color: t.buttonTextColor }), _jsx("span", { children: t.buttonLabel })] })) }));
15
- };
16
- const ChatIcon = ({ color }) => (_jsx("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: _jsx("path", { d: "M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", fill: "none" }) }));
17
- const CloseIcon = ({ color }) => (_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", children: [_jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18", stroke: color, strokeWidth: "2.5", strokeLinecap: "round" }), _jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18", stroke: color, strokeWidth: "2.5", strokeLinecap: "round" })] }));
@@ -1,10 +0,0 @@
1
- import React from 'react';
2
- import { ChatConfig, ChatWidgetTheme } from '../../types';
3
- interface ChatWindowProps {
4
- config: ChatConfig;
5
- theme?: ChatWidgetTheme;
6
- buttonPosition: 'bottom-right' | 'bottom-left';
7
- onClose: () => void;
8
- }
9
- export declare const ChatWindow: React.FC<ChatWindowProps>;
10
- export {};