@dora-cell/sdk-react 0.1.1-beta.10
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 +378 -0
- package/dist/index.d.mts +102 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.js +716 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +709 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +48 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import { forwardRef, createElement, createContext, useState, useRef, useEffect, useContext } from 'react';
|
|
2
|
+
import { DoraCell } from '@dora-cell/sdk';
|
|
3
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
|
+
|
|
5
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/shared/src/utils.js
|
|
6
|
+
var toKebabCase = (string) => string.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
|
|
7
|
+
var toCamelCase = (string) => string.replace(
|
|
8
|
+
/^([A-Z])|[\s-_]+(\w)/g,
|
|
9
|
+
(match, p1, p2) => p2 ? p2.toUpperCase() : p1.toLowerCase()
|
|
10
|
+
);
|
|
11
|
+
var toPascalCase = (string) => {
|
|
12
|
+
const camelCase = toCamelCase(string);
|
|
13
|
+
return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
|
|
14
|
+
};
|
|
15
|
+
var mergeClasses = (...classes) => classes.filter((className, index, array) => {
|
|
16
|
+
return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
|
|
17
|
+
}).join(" ").trim();
|
|
18
|
+
var hasA11yProp = (props) => {
|
|
19
|
+
for (const prop in props) {
|
|
20
|
+
if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/defaultAttributes.js
|
|
27
|
+
var defaultAttributes = {
|
|
28
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
29
|
+
width: 24,
|
|
30
|
+
height: 24,
|
|
31
|
+
viewBox: "0 0 24 24",
|
|
32
|
+
fill: "none",
|
|
33
|
+
stroke: "currentColor",
|
|
34
|
+
strokeWidth: 2,
|
|
35
|
+
strokeLinecap: "round",
|
|
36
|
+
strokeLinejoin: "round"
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/Icon.js
|
|
40
|
+
var Icon = forwardRef(
|
|
41
|
+
({
|
|
42
|
+
color = "currentColor",
|
|
43
|
+
size = 24,
|
|
44
|
+
strokeWidth = 2,
|
|
45
|
+
absoluteStrokeWidth,
|
|
46
|
+
className = "",
|
|
47
|
+
children,
|
|
48
|
+
iconNode,
|
|
49
|
+
...rest
|
|
50
|
+
}, ref) => createElement(
|
|
51
|
+
"svg",
|
|
52
|
+
{
|
|
53
|
+
ref,
|
|
54
|
+
...defaultAttributes,
|
|
55
|
+
width: size,
|
|
56
|
+
height: size,
|
|
57
|
+
stroke: color,
|
|
58
|
+
strokeWidth: absoluteStrokeWidth ? Number(strokeWidth) * 24 / Number(size) : strokeWidth,
|
|
59
|
+
className: mergeClasses("lucide", className),
|
|
60
|
+
...!children && !hasA11yProp(rest) && { "aria-hidden": "true" },
|
|
61
|
+
...rest
|
|
62
|
+
},
|
|
63
|
+
[
|
|
64
|
+
...iconNode.map(([tag, attrs]) => createElement(tag, attrs)),
|
|
65
|
+
...Array.isArray(children) ? children : [children]
|
|
66
|
+
]
|
|
67
|
+
)
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/createLucideIcon.js
|
|
71
|
+
var createLucideIcon = (iconName, iconNode) => {
|
|
72
|
+
const Component = forwardRef(
|
|
73
|
+
({ className, ...props }, ref) => createElement(Icon, {
|
|
74
|
+
ref,
|
|
75
|
+
iconNode,
|
|
76
|
+
className: mergeClasses(
|
|
77
|
+
`lucide-${toKebabCase(toPascalCase(iconName))}`,
|
|
78
|
+
`lucide-${iconName}`,
|
|
79
|
+
className
|
|
80
|
+
),
|
|
81
|
+
...props
|
|
82
|
+
})
|
|
83
|
+
);
|
|
84
|
+
Component.displayName = toPascalCase(iconName);
|
|
85
|
+
return Component;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-down-left.js
|
|
89
|
+
var __iconNode = [
|
|
90
|
+
["path", { d: "M17 7 7 17", key: "15tmo1" }],
|
|
91
|
+
["path", { d: "M17 17H7V7", key: "1org7z" }]
|
|
92
|
+
];
|
|
93
|
+
var ArrowDownLeft = createLucideIcon("arrow-down-left", __iconNode);
|
|
94
|
+
|
|
95
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/arrow-up-right.js
|
|
96
|
+
var __iconNode2 = [
|
|
97
|
+
["path", { d: "M7 7h10v10", key: "1tivn9" }],
|
|
98
|
+
["path", { d: "M7 17 17 7", key: "1vkiza" }]
|
|
99
|
+
];
|
|
100
|
+
var ArrowUpRight = createLucideIcon("arrow-up-right", __iconNode2);
|
|
101
|
+
|
|
102
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/delete.js
|
|
103
|
+
var __iconNode3 = [
|
|
104
|
+
[
|
|
105
|
+
"path",
|
|
106
|
+
{
|
|
107
|
+
d: "M10 5a2 2 0 0 0-1.344.519l-6.328 5.74a1 1 0 0 0 0 1.481l6.328 5.741A2 2 0 0 0 10 19h10a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2z",
|
|
108
|
+
key: "1yo7s0"
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
["path", { d: "m12 9 6 6", key: "anjzzh" }],
|
|
112
|
+
["path", { d: "m18 9-6 6", key: "1fp51s" }]
|
|
113
|
+
];
|
|
114
|
+
var Delete = createLucideIcon("delete", __iconNode3);
|
|
115
|
+
|
|
116
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/maximize.js
|
|
117
|
+
var __iconNode4 = [
|
|
118
|
+
["path", { d: "M8 3H5a2 2 0 0 0-2 2v3", key: "1dcmit" }],
|
|
119
|
+
["path", { d: "M21 8V5a2 2 0 0 0-2-2h-3", key: "1e4gt3" }],
|
|
120
|
+
["path", { d: "M3 16v3a2 2 0 0 0 2 2h3", key: "wsl5sc" }],
|
|
121
|
+
["path", { d: "M16 21h3a2 2 0 0 0 2-2v-3", key: "18trek" }]
|
|
122
|
+
];
|
|
123
|
+
var Maximize = createLucideIcon("maximize", __iconNode4);
|
|
124
|
+
|
|
125
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/mic-off.js
|
|
126
|
+
var __iconNode5 = [
|
|
127
|
+
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
128
|
+
["path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33", key: "1gzdoj" }],
|
|
129
|
+
["path", { d: "M16.95 16.95A7 7 0 0 1 5 12v-2", key: "cqa7eg" }],
|
|
130
|
+
["path", { d: "M18.89 13.23A7 7 0 0 0 19 12v-2", key: "16hl24" }],
|
|
131
|
+
["path", { d: "m2 2 20 20", key: "1ooewy" }],
|
|
132
|
+
["path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12", key: "r2i35w" }]
|
|
133
|
+
];
|
|
134
|
+
var MicOff = createLucideIcon("mic-off", __iconNode5);
|
|
135
|
+
|
|
136
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/mic.js
|
|
137
|
+
var __iconNode6 = [
|
|
138
|
+
["path", { d: "M12 19v3", key: "npa21l" }],
|
|
139
|
+
["path", { d: "M19 10v2a7 7 0 0 1-14 0v-2", key: "1vc78b" }],
|
|
140
|
+
["rect", { x: "9", y: "2", width: "6", height: "13", rx: "3", key: "s6n7sd" }]
|
|
141
|
+
];
|
|
142
|
+
var Mic = createLucideIcon("mic", __iconNode6);
|
|
143
|
+
|
|
144
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/minimize-2.js
|
|
145
|
+
var __iconNode7 = [
|
|
146
|
+
["path", { d: "m14 10 7-7", key: "oa77jy" }],
|
|
147
|
+
["path", { d: "M20 10h-6V4", key: "mjg0md" }],
|
|
148
|
+
["path", { d: "m3 21 7-7", key: "tjx5ai" }],
|
|
149
|
+
["path", { d: "M4 14h6v6", key: "rmj7iw" }]
|
|
150
|
+
];
|
|
151
|
+
var Minimize2 = createLucideIcon("minimize-2", __iconNode7);
|
|
152
|
+
|
|
153
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/phone.js
|
|
154
|
+
var __iconNode8 = [
|
|
155
|
+
[
|
|
156
|
+
"path",
|
|
157
|
+
{
|
|
158
|
+
d: "M13.832 16.568a1 1 0 0 0 1.213-.303l.355-.465A2 2 0 0 1 17 15h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2A18 18 0 0 1 2 4a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v3a2 2 0 0 1-.8 1.6l-.468.351a1 1 0 0 0-.292 1.233 14 14 0 0 0 6.392 6.384",
|
|
159
|
+
key: "9njp5v"
|
|
160
|
+
}
|
|
161
|
+
]
|
|
162
|
+
];
|
|
163
|
+
var Phone = createLucideIcon("phone", __iconNode8);
|
|
164
|
+
|
|
165
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/user.js
|
|
166
|
+
var __iconNode9 = [
|
|
167
|
+
["path", { d: "M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2", key: "975kel" }],
|
|
168
|
+
["circle", { cx: "12", cy: "7", r: "4", key: "17ys0d" }]
|
|
169
|
+
];
|
|
170
|
+
var User = createLucideIcon("user", __iconNode9);
|
|
171
|
+
|
|
172
|
+
// ../../node_modules/.pnpm/lucide-react@0.555.0_react@19.2.4/node_modules/lucide-react/dist/esm/icons/x.js
|
|
173
|
+
var __iconNode10 = [
|
|
174
|
+
["path", { d: "M18 6 6 18", key: "1bl5f8" }],
|
|
175
|
+
["path", { d: "m6 6 12 12", key: "d8bk6v" }]
|
|
176
|
+
];
|
|
177
|
+
var X = createLucideIcon("x", __iconNode10);
|
|
178
|
+
function CallInterface({
|
|
179
|
+
isOpen = false,
|
|
180
|
+
onOpenChange,
|
|
181
|
+
onCallEnded,
|
|
182
|
+
maximizeIcon,
|
|
183
|
+
minimizeIcon
|
|
184
|
+
}) {
|
|
185
|
+
const {
|
|
186
|
+
callStatus,
|
|
187
|
+
currentCall,
|
|
188
|
+
callDuration,
|
|
189
|
+
isMuted,
|
|
190
|
+
hangup,
|
|
191
|
+
answerCall,
|
|
192
|
+
toggleMute
|
|
193
|
+
} = useCall();
|
|
194
|
+
const [isMinimized, setIsMinimized] = useState(false);
|
|
195
|
+
const [localMuted, setLocalMuted] = useState(false);
|
|
196
|
+
const [wasConnected, setWasConnected] = useState(false);
|
|
197
|
+
const [closeCountdown, setCloseCountdown] = useState(null);
|
|
198
|
+
const [connectingCountdown, setConnectingCountdown] = useState(null);
|
|
199
|
+
const audioRef = useRef(null);
|
|
200
|
+
const ringtoneRef = useRef(null);
|
|
201
|
+
const isIncoming = currentCall?.direction === "inbound";
|
|
202
|
+
const remoteNumber = currentCall?.remoteNumber || "Unknown";
|
|
203
|
+
const displayName = remoteNumber;
|
|
204
|
+
useEffect(() => {
|
|
205
|
+
if (!audioRef.current) return;
|
|
206
|
+
}, [currentCall]);
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
if (callStatus === "ringing" && isIncoming) {
|
|
209
|
+
onOpenChange?.(true);
|
|
210
|
+
if (ringtoneRef.current) {
|
|
211
|
+
ringtoneRef.current.play().catch(() => {
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
if (ringtoneRef.current) {
|
|
216
|
+
ringtoneRef.current.pause();
|
|
217
|
+
ringtoneRef.current.currentTime = 0;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}, [callStatus, isIncoming, onOpenChange]);
|
|
221
|
+
useEffect(() => {
|
|
222
|
+
if (callStatus === "ongoing") {
|
|
223
|
+
setWasConnected(true);
|
|
224
|
+
}
|
|
225
|
+
}, [callStatus]);
|
|
226
|
+
useEffect(() => {
|
|
227
|
+
if (isOpen && callStatus === "ended") {
|
|
228
|
+
onCallEnded?.();
|
|
229
|
+
const closeDelay = wasConnected ? 1e3 : 5e3;
|
|
230
|
+
const countdownStart = Date.now();
|
|
231
|
+
const countdownInterval = setInterval(() => {
|
|
232
|
+
const elapsed = Date.now() - countdownStart;
|
|
233
|
+
const remaining = Math.max(0, closeDelay - elapsed);
|
|
234
|
+
const seconds = Math.ceil(remaining / 1e3);
|
|
235
|
+
setCloseCountdown(seconds);
|
|
236
|
+
if (remaining <= 0) {
|
|
237
|
+
clearInterval(countdownInterval);
|
|
238
|
+
onOpenChange?.(false);
|
|
239
|
+
}
|
|
240
|
+
}, 100);
|
|
241
|
+
return () => clearInterval(countdownInterval);
|
|
242
|
+
} else {
|
|
243
|
+
setCloseCountdown(null);
|
|
244
|
+
}
|
|
245
|
+
}, [callStatus, isOpen, onOpenChange, wasConnected, onCallEnded]);
|
|
246
|
+
useEffect(() => {
|
|
247
|
+
let interval;
|
|
248
|
+
const isConnecting = callStatus === "connecting";
|
|
249
|
+
if (isOpen && isConnecting && !isIncoming) {
|
|
250
|
+
setConnectingCountdown(60);
|
|
251
|
+
interval = setInterval(() => {
|
|
252
|
+
setConnectingCountdown((prev) => {
|
|
253
|
+
if (prev === null || prev <= 0) {
|
|
254
|
+
clearInterval(interval);
|
|
255
|
+
hangup();
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
return prev - 1;
|
|
259
|
+
});
|
|
260
|
+
}, 1e3);
|
|
261
|
+
} else {
|
|
262
|
+
setConnectingCountdown(null);
|
|
263
|
+
}
|
|
264
|
+
return () => clearInterval(interval);
|
|
265
|
+
}, [callStatus, isOpen, isIncoming, hangup]);
|
|
266
|
+
if (!isOpen && callStatus === "idle") return null;
|
|
267
|
+
return /* @__PURE__ */ jsxs(
|
|
268
|
+
"div",
|
|
269
|
+
{
|
|
270
|
+
role: "dialog",
|
|
271
|
+
"aria-label": "Call interface",
|
|
272
|
+
className: `fixed top-14 md:top-16 right-0 md:right-4 z-50 transform transition-all duration-300 ease-in-out bg-[#1E1E1E] shadow-xl ${isOpen ? "translate-x-0" : "translate-x-[110%]"} ${isMinimized ? "w-48 h-20 rounded-2xl p-4 flex items-center justify-between" : "w-full sm:w-96 md:max-w-sm max-h-[calc(100vh-3.5rem)] md:max-h-[calc(100vh-4rem)] rounded-none md:rounded-2xl"}`,
|
|
273
|
+
children: [
|
|
274
|
+
/* @__PURE__ */ jsx("audio", { ref: audioRef, autoPlay: true, className: "hidden" }),
|
|
275
|
+
/* @__PURE__ */ jsx(
|
|
276
|
+
"audio",
|
|
277
|
+
{
|
|
278
|
+
ref: ringtoneRef,
|
|
279
|
+
src: "https://assets.mixkit.co/active_storage/sfx/2358/2358-preview.mp3",
|
|
280
|
+
loop: true,
|
|
281
|
+
preload: "auto",
|
|
282
|
+
className: "hidden"
|
|
283
|
+
}
|
|
284
|
+
),
|
|
285
|
+
isMinimized ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between w-full h-full", children: [
|
|
286
|
+
/* @__PURE__ */ jsx(
|
|
287
|
+
"button",
|
|
288
|
+
{
|
|
289
|
+
onClick: () => setIsMinimized(false),
|
|
290
|
+
className: "w-10 h-10 rounded-full bg-[#2A2A2A] flex items-center justify-center text-slate-400 hover:text-white transition-colors",
|
|
291
|
+
"aria-label": "Maximize call interface",
|
|
292
|
+
children: maximizeIcon || /* @__PURE__ */ jsx(Maximize, { size: 18 })
|
|
293
|
+
}
|
|
294
|
+
),
|
|
295
|
+
/* @__PURE__ */ jsxs("div", { className: "relative flex items-center", children: [
|
|
296
|
+
/* @__PURE__ */ jsx("div", { className: "w-11 h-11 rounded-full bg-white flex items-center justify-center -mr-3", children: /* @__PURE__ */ jsx("span", { className: "text-xl text-slate-900 font-bold", children: displayName.charAt(0).toUpperCase() }) }),
|
|
297
|
+
/* @__PURE__ */ jsx(
|
|
298
|
+
"button",
|
|
299
|
+
{
|
|
300
|
+
onClick: hangup,
|
|
301
|
+
className: "w-11 h-11 rounded-full bg-red-500 text-white flex items-center justify-center shadow-lg hover:bg-red-600 transition-colors z-10",
|
|
302
|
+
"aria-label": "End call",
|
|
303
|
+
children: /* @__PURE__ */ jsx(Phone, { size: 18, className: "transform rotate-135" })
|
|
304
|
+
}
|
|
305
|
+
)
|
|
306
|
+
] })
|
|
307
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "flex flex-col py-4 md:py-6 gap-2.5 md:gap-3.5 px-3 md:px-5 relative", children: [
|
|
308
|
+
/* @__PURE__ */ jsx(
|
|
309
|
+
"button",
|
|
310
|
+
{
|
|
311
|
+
onClick: () => setIsMinimized(true),
|
|
312
|
+
className: "absolute top-2 right-2 text-slate-500 hover:text-white transition-colors",
|
|
313
|
+
"aria-label": "Minimize",
|
|
314
|
+
children: minimizeIcon || /* @__PURE__ */ jsx(Minimize2, { size: 16 })
|
|
315
|
+
}
|
|
316
|
+
),
|
|
317
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between gap-2 items-start md:items-center", children: [
|
|
318
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 md:gap-4 min-w-0", children: [
|
|
319
|
+
/* @__PURE__ */ jsx("div", { className: "w-9 md:w-11 h-9 md:h-11 rounded-full bg-white flex items-center justify-center shrink-0", children: /* @__PURE__ */ jsx("span", { className: "text-lg md:text-2xl text-slate-900 font-bold", children: displayName.charAt(0).toUpperCase() }) }),
|
|
320
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
321
|
+
/* @__PURE__ */ jsx("h2", { className: "text-base md:text-xl font-normal text-white truncate", children: isIncoming ? displayName : displayName === "Unknown" ? "Outgoing Call" : displayName }),
|
|
322
|
+
/* @__PURE__ */ jsxs("p", { className: "text-slate-400 text-xs md:text-sm truncate", children: [
|
|
323
|
+
remoteNumber,
|
|
324
|
+
" \u2022",
|
|
325
|
+
" ",
|
|
326
|
+
isMuted ? /* @__PURE__ */ jsx("span", { className: "text-xs text-[#F99578]", children: "Call mute" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
327
|
+
(callStatus === "connecting" || callStatus === "ringing") && /* @__PURE__ */ jsx("span", { children: isIncoming ? "Incoming Call..." : callStatus === "connecting" ? "Connecting..." : "Ringing..." }),
|
|
328
|
+
callStatus === "ongoing" && /* @__PURE__ */ jsx("span", { children: "On Call" })
|
|
329
|
+
] })
|
|
330
|
+
] })
|
|
331
|
+
] })
|
|
332
|
+
] }),
|
|
333
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-1.5 md:gap-3 shrink-0", children: [
|
|
334
|
+
isIncoming && callStatus === "ringing" && /* @__PURE__ */ jsx(
|
|
335
|
+
"button",
|
|
336
|
+
{
|
|
337
|
+
onClick: answerCall,
|
|
338
|
+
className: "rounded-full bg-green-500 text-white hover:bg-green-600 transition-colors w-8 md:w-9 h-8 md:h-9 flex items-center justify-center shrink-0",
|
|
339
|
+
"aria-label": "Answer call",
|
|
340
|
+
children: /* @__PURE__ */ jsx(Phone, { size: 18 })
|
|
341
|
+
}
|
|
342
|
+
),
|
|
343
|
+
(callStatus === "ongoing" || isIncoming && callStatus === "ringing") && /* @__PURE__ */ jsx(
|
|
344
|
+
"button",
|
|
345
|
+
{
|
|
346
|
+
onClick: () => {
|
|
347
|
+
toggleMute();
|
|
348
|
+
setLocalMuted((s) => !s);
|
|
349
|
+
},
|
|
350
|
+
className: `rounded-full transition-colors w-8 md:w-9 h-8 md:h-9 flex items-center justify-center shrink-0 bg-slate-700 hover:bg-slate-600 ${isMuted ? "text-white " : " text-slate-400 "}`,
|
|
351
|
+
"aria-label": "Toggle mute",
|
|
352
|
+
children: isMuted ? /* @__PURE__ */ jsx(MicOff, { size: 18 }) : /* @__PURE__ */ jsx(Mic, { size: 18 })
|
|
353
|
+
}
|
|
354
|
+
),
|
|
355
|
+
/* @__PURE__ */ jsx(
|
|
356
|
+
"button",
|
|
357
|
+
{
|
|
358
|
+
onClick: hangup,
|
|
359
|
+
className: "rounded-full bg-red-600 text-white hover:bg-red-700 transition-colors w-8 md:w-9 h-8 md:h-9 flex items-center justify-center shrink-0",
|
|
360
|
+
"aria-label": isIncoming && callStatus === "ringing" ? "Decline call" : "End call",
|
|
361
|
+
children: /* @__PURE__ */ jsx(Phone, { size: 18, className: "transform rotate-135" })
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
] })
|
|
365
|
+
] }),
|
|
366
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 border border-[#282828]" }),
|
|
367
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between items-center", children: [
|
|
368
|
+
/* @__PURE__ */ jsxs("div", { className: "text-slate-400 text-xs md:text-sm font-light tracking-wide flex items-center gap-1", children: [
|
|
369
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
370
|
+
(callStatus === "connecting" || callStatus === "ringing") && /* @__PURE__ */ jsx("span", { children: isIncoming ? "Incoming call" : "Outgoing call" }),
|
|
371
|
+
callStatus === "ongoing" && /* @__PURE__ */ jsx("span", { children: "Ongoing" }),
|
|
372
|
+
callStatus === "ended" && /* @__PURE__ */ jsx("span", { children: "Call ended" })
|
|
373
|
+
] }),
|
|
374
|
+
(callStatus === "connecting" || callStatus === "ringing" || callStatus === "ongoing") && (isIncoming ? /* @__PURE__ */ jsx(ArrowDownLeft, { className: "inline-block ml-1 text-green-400", size: 16 }) : /* @__PURE__ */ jsx(ArrowUpRight, { className: "inline-block ml-1 text-blue-400", size: 16 }))
|
|
375
|
+
] }),
|
|
376
|
+
callStatus === "ongoing" && /* @__PURE__ */ jsx("div", { className: "bg-[#272727] h-6 md:h-7.5 rounded-full px-3 md:px-4.5 text-white font-normal text-xs md:text-sm flex items-center justify-center", children: /* @__PURE__ */ jsxs("span", { children: [
|
|
377
|
+
" ",
|
|
378
|
+
callDuration,
|
|
379
|
+
" "
|
|
380
|
+
] }) }),
|
|
381
|
+
(connectingCountdown !== null || callStatus === "ringing") && !isIncoming && callStatus !== "ongoing" && /* @__PURE__ */ jsxs("div", { className: "bg-[#272727] h-8 rounded-full px-3 md:px-4 text-white font-normal text-xs md:text-sm flex items-center justify-center gap-2", children: [
|
|
382
|
+
/* @__PURE__ */ jsx("span", { className: "text-[#F99578] whitespace-nowrap", children: callStatus === "ringing" ? "Ringing..." : "Call connecting..." }),
|
|
383
|
+
callStatus === "connecting" && connectingCountdown && /* @__PURE__ */ jsxs("span", { className: "font-mono ml-1", children: [
|
|
384
|
+
Math.floor(connectingCountdown / 60),
|
|
385
|
+
":",
|
|
386
|
+
String(connectingCountdown % 60).padStart(2, "0")
|
|
387
|
+
] })
|
|
388
|
+
] })
|
|
389
|
+
] })
|
|
390
|
+
] })
|
|
391
|
+
]
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
function Dialpad({
|
|
396
|
+
onCallInitiated,
|
|
397
|
+
initialNumber = "",
|
|
398
|
+
showKeys = true,
|
|
399
|
+
className = "",
|
|
400
|
+
availableExtensions = [],
|
|
401
|
+
selectedExtension,
|
|
402
|
+
onExtensionChange
|
|
403
|
+
}) {
|
|
404
|
+
const { call, callStatus } = useCall();
|
|
405
|
+
const [number, setNumber] = useState(initialNumber);
|
|
406
|
+
const [keysVisible, setKeysVisible] = useState(showKeys);
|
|
407
|
+
const append = (digit) => setNumber((s) => s + digit);
|
|
408
|
+
const backspace = () => setNumber((s) => s.slice(0, -1));
|
|
409
|
+
const handleCall = async () => {
|
|
410
|
+
if (!number || number.trim() === "") return;
|
|
411
|
+
try {
|
|
412
|
+
await call(number, selectedExtension);
|
|
413
|
+
onCallInitiated?.(number);
|
|
414
|
+
} catch (e) {
|
|
415
|
+
console.error("Call failed", e);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
const isCallDisabled = !number || number.trim() === "" || callStatus === "ringing" || callStatus === "ongoing" || callStatus === "connecting";
|
|
419
|
+
return /* @__PURE__ */ jsxs("div", { className: `space-y-4 ${className}`, children: [
|
|
420
|
+
availableExtensions.length > 0 && /* @__PURE__ */ jsxs("div", { className: "gap-2 bg-[#F6F7F9] rounded-lg flex items-center justify-between p-2 md:p-2.5 mb-4", children: [
|
|
421
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs md:text-sm text-neutral-500 font-normal whitespace-nowrap", children: "Caller ID" }),
|
|
422
|
+
/* @__PURE__ */ jsx(
|
|
423
|
+
"select",
|
|
424
|
+
{
|
|
425
|
+
className: "bg-transparent text-sm font-medium outline-none",
|
|
426
|
+
value: selectedExtension,
|
|
427
|
+
onChange: (e) => onExtensionChange?.(e.target.value),
|
|
428
|
+
children: availableExtensions.map((ext) => /* @__PURE__ */ jsx("option", { value: ext.value, children: ext.label }, ext.value))
|
|
429
|
+
}
|
|
430
|
+
)
|
|
431
|
+
] }),
|
|
432
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 md:space-y-3 bg-[#F6F7F9] p-2 md:p-2.5 rounded-lg", children: [
|
|
433
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 md:gap-3", children: [
|
|
434
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 flex items-center bg-white rounded-md px-3 h-10 border border-transparent focus-within:border-green-500 transition-colors", children: [
|
|
435
|
+
/* @__PURE__ */ jsx(User, { className: "text-gray-400 mr-2", size: 18 }),
|
|
436
|
+
/* @__PURE__ */ jsx(
|
|
437
|
+
"input",
|
|
438
|
+
{
|
|
439
|
+
type: "tel",
|
|
440
|
+
placeholder: "Enter number",
|
|
441
|
+
value: number,
|
|
442
|
+
onChange: (e) => setNumber(e.target.value),
|
|
443
|
+
className: "bg-transparent border-none outline-none w-full text-base"
|
|
444
|
+
}
|
|
445
|
+
)
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx(
|
|
448
|
+
"button",
|
|
449
|
+
{
|
|
450
|
+
disabled: isCallDisabled,
|
|
451
|
+
className: "bg-green-500 text-white hover:bg-green-600 rounded-lg h-9 md:h-10 w-9 md:w-11 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed transition-colors",
|
|
452
|
+
onClick: handleCall,
|
|
453
|
+
title: "Place Call",
|
|
454
|
+
children: /* @__PURE__ */ jsx(Phone, { size: 16, fill: "currentColor" })
|
|
455
|
+
}
|
|
456
|
+
) })
|
|
457
|
+
] }),
|
|
458
|
+
/* @__PURE__ */ jsx("div", { children: keysVisible ? /* @__PURE__ */ jsxs("div", { className: "pt-3", children: [
|
|
459
|
+
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-3 mb-3", children: /* @__PURE__ */ jsxs(
|
|
460
|
+
"button",
|
|
461
|
+
{
|
|
462
|
+
onClick: () => setKeysVisible(false),
|
|
463
|
+
className: "px-2 md:px-2.5 h-8 bg-white rounded-full shadow text-neutral-500 flex items-center gap-1.5 md:gap-2 text-xs md:text-sm font-normal hover:bg-gray-50 transition-colors",
|
|
464
|
+
children: [
|
|
465
|
+
/* @__PURE__ */ jsx(X, { color: "red", size: 16 }),
|
|
466
|
+
" Close keypad"
|
|
467
|
+
]
|
|
468
|
+
}
|
|
469
|
+
) }),
|
|
470
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 md:gap-3", children: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "0", "del"].map(
|
|
471
|
+
(d) => /* @__PURE__ */ jsx(
|
|
472
|
+
"button",
|
|
473
|
+
{
|
|
474
|
+
onClick: () => d === "del" ? backspace() : append(d),
|
|
475
|
+
className: "h-10 md:h-12 bg-white rounded-xl shadow flex items-center justify-center text-base md:text-lg font-medium hover:bg-gray-50 active:bg-gray-100 transition-colors",
|
|
476
|
+
"aria-label": d === "del" ? "Delete" : `Dial ${d}`,
|
|
477
|
+
children: d === "del" ? /* @__PURE__ */ jsx(Delete, { size: 18 }) : d
|
|
478
|
+
},
|
|
479
|
+
d
|
|
480
|
+
)
|
|
481
|
+
) })
|
|
482
|
+
] }) : /* @__PURE__ */ jsx("div", { className: "pt-2 md:pt-3", children: /* @__PURE__ */ jsxs(
|
|
483
|
+
"button",
|
|
484
|
+
{
|
|
485
|
+
onClick: () => setKeysVisible(true),
|
|
486
|
+
className: "rounded-full bg-[#EDEEF1] h-9 md:h-10 flex justify-center items-center px-3 md:px-4 w-full text-xs md:text-sm hover:bg-[#E3E4E8] transition-colors",
|
|
487
|
+
children: [
|
|
488
|
+
/* @__PURE__ */ jsx("span", { className: "mr-2", children: "\u{1F522}" }),
|
|
489
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs md:text-sm text-neutral-500 font-normal ml-1.5", children: "Open dialer" })
|
|
490
|
+
]
|
|
491
|
+
}
|
|
492
|
+
) }) })
|
|
493
|
+
] })
|
|
494
|
+
] });
|
|
495
|
+
}
|
|
496
|
+
var DoraCellContext = createContext(void 0);
|
|
497
|
+
function DoraCellProvider({
|
|
498
|
+
config,
|
|
499
|
+
children,
|
|
500
|
+
autoInitialize = true
|
|
501
|
+
}) {
|
|
502
|
+
const [sdk] = useState(() => new DoraCell(config));
|
|
503
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
504
|
+
const [connectionStatus, setConnectionStatus] = useState("disconnected");
|
|
505
|
+
const [currentCall, setCurrentCall] = useState(null);
|
|
506
|
+
const [callStatus, setCallStatus] = useState("idle");
|
|
507
|
+
const [callDuration, setCallDuration] = useState("00:00");
|
|
508
|
+
const [isMuted, setIsMuted] = useState(false);
|
|
509
|
+
const [error, setError] = useState(null);
|
|
510
|
+
const durationIntervalRef = useRef(null);
|
|
511
|
+
useEffect(() => {
|
|
512
|
+
if (!autoInitialize) return;
|
|
513
|
+
const initializeSdk = async () => {
|
|
514
|
+
try {
|
|
515
|
+
await sdk.initialize();
|
|
516
|
+
setIsInitialized(true);
|
|
517
|
+
} catch (err) {
|
|
518
|
+
setError(err instanceof Error ? err : new Error("Failed to initialize SDK"));
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
initializeSdk();
|
|
522
|
+
return () => {
|
|
523
|
+
sdk.destroy();
|
|
524
|
+
};
|
|
525
|
+
}, [sdk, autoInitialize]);
|
|
526
|
+
useEffect(() => {
|
|
527
|
+
if (!sdk) return;
|
|
528
|
+
const handleConnectionStatus = (state) => {
|
|
529
|
+
setConnectionStatus(state.status);
|
|
530
|
+
if (state.error) {
|
|
531
|
+
setError(new Error(state.error));
|
|
532
|
+
}
|
|
533
|
+
};
|
|
534
|
+
const handleIncomingCall = (call2) => {
|
|
535
|
+
setCurrentCall(call2);
|
|
536
|
+
setCallStatus("ringing");
|
|
537
|
+
};
|
|
538
|
+
const handleOutgoingCall = (call2) => {
|
|
539
|
+
setCurrentCall(call2);
|
|
540
|
+
setCallStatus("connecting");
|
|
541
|
+
};
|
|
542
|
+
const handleCallRinging = (call2) => {
|
|
543
|
+
setCallStatus("ringing");
|
|
544
|
+
};
|
|
545
|
+
const handleCallConnected = (call2) => {
|
|
546
|
+
setCallStatus("ongoing");
|
|
547
|
+
};
|
|
548
|
+
const handleCallEnded = (call2) => {
|
|
549
|
+
setCallStatus("ended");
|
|
550
|
+
setCurrentCall(null);
|
|
551
|
+
setIsMuted(false);
|
|
552
|
+
setTimeout(() => {
|
|
553
|
+
setCallStatus("idle");
|
|
554
|
+
}, 3e3);
|
|
555
|
+
};
|
|
556
|
+
const handleCallFailed = (call2, errorMsg) => {
|
|
557
|
+
setError(new Error(errorMsg));
|
|
558
|
+
setCallStatus("ended");
|
|
559
|
+
setCurrentCall(null);
|
|
560
|
+
};
|
|
561
|
+
const handleError = (err) => {
|
|
562
|
+
setError(err);
|
|
563
|
+
};
|
|
564
|
+
sdk.on("connection:status", handleConnectionStatus);
|
|
565
|
+
sdk.on("call:incoming", handleIncomingCall);
|
|
566
|
+
sdk.on("call:outgoing", handleOutgoingCall);
|
|
567
|
+
sdk.on("call:ringing", handleCallRinging);
|
|
568
|
+
sdk.on("call:connected", handleCallConnected);
|
|
569
|
+
sdk.on("call:ended", handleCallEnded);
|
|
570
|
+
sdk.on("call:failed", handleCallFailed);
|
|
571
|
+
sdk.on("error", handleError);
|
|
572
|
+
return () => {
|
|
573
|
+
sdk.off("connection:status", handleConnectionStatus);
|
|
574
|
+
sdk.off("call:incoming", handleIncomingCall);
|
|
575
|
+
sdk.off("call:outgoing", handleOutgoingCall);
|
|
576
|
+
sdk.off("call:ringing", handleCallRinging);
|
|
577
|
+
sdk.off("call:connected", handleCallConnected);
|
|
578
|
+
sdk.off("call:ended", handleCallEnded);
|
|
579
|
+
sdk.off("call:failed", handleCallFailed);
|
|
580
|
+
sdk.off("error", handleError);
|
|
581
|
+
};
|
|
582
|
+
}, [sdk]);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
if (callStatus === "ongoing" && currentCall) {
|
|
585
|
+
durationIntervalRef.current = window.setInterval(() => {
|
|
586
|
+
const duration = currentCall.duration;
|
|
587
|
+
const mm = String(Math.floor(duration / 60)).padStart(2, "0");
|
|
588
|
+
const ss = String(duration % 60).padStart(2, "0");
|
|
589
|
+
setCallDuration(`${mm}:${ss}`);
|
|
590
|
+
}, 1e3);
|
|
591
|
+
} else {
|
|
592
|
+
setCallDuration("00:00");
|
|
593
|
+
if (durationIntervalRef.current) {
|
|
594
|
+
clearInterval(durationIntervalRef.current);
|
|
595
|
+
durationIntervalRef.current = null;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return () => {
|
|
599
|
+
if (durationIntervalRef.current) {
|
|
600
|
+
clearInterval(durationIntervalRef.current);
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}, [callStatus, currentCall]);
|
|
604
|
+
const call = async (phoneNumber, extension) => {
|
|
605
|
+
try {
|
|
606
|
+
setError(null);
|
|
607
|
+
await sdk.call(phoneNumber, { extension });
|
|
608
|
+
} catch (err) {
|
|
609
|
+
setError(err instanceof Error ? err : new Error("Failed to make call"));
|
|
610
|
+
throw err;
|
|
611
|
+
}
|
|
612
|
+
};
|
|
613
|
+
const hangup = () => {
|
|
614
|
+
try {
|
|
615
|
+
sdk.hangup();
|
|
616
|
+
} catch (err) {
|
|
617
|
+
setError(err instanceof Error ? err : new Error("Failed to hang up"));
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const toggleMute = () => {
|
|
621
|
+
if (!currentCall) return;
|
|
622
|
+
if (isMuted) {
|
|
623
|
+
currentCall.unmute();
|
|
624
|
+
setIsMuted(false);
|
|
625
|
+
} else {
|
|
626
|
+
currentCall.mute();
|
|
627
|
+
setIsMuted(true);
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
const answerCall = () => {
|
|
631
|
+
try {
|
|
632
|
+
sdk.answerCall();
|
|
633
|
+
} catch (err) {
|
|
634
|
+
setError(err instanceof Error ? err : new Error("Failed to answer call"));
|
|
635
|
+
}
|
|
636
|
+
};
|
|
637
|
+
const contextValue = {
|
|
638
|
+
sdk,
|
|
639
|
+
connectionStatus,
|
|
640
|
+
currentCall,
|
|
641
|
+
callStatus,
|
|
642
|
+
callDuration,
|
|
643
|
+
isMuted,
|
|
644
|
+
isInitialized,
|
|
645
|
+
error,
|
|
646
|
+
call,
|
|
647
|
+
hangup,
|
|
648
|
+
toggleMute,
|
|
649
|
+
answerCall
|
|
650
|
+
};
|
|
651
|
+
return /* @__PURE__ */ jsx(DoraCellContext.Provider, { value: contextValue, children });
|
|
652
|
+
}
|
|
653
|
+
function useDoraCell() {
|
|
654
|
+
const context = useContext(DoraCellContext);
|
|
655
|
+
if (!context) {
|
|
656
|
+
throw new Error("useDoraCell must be used within DoraCellProvider");
|
|
657
|
+
}
|
|
658
|
+
return context;
|
|
659
|
+
}
|
|
660
|
+
function useCall() {
|
|
661
|
+
const { call, hangup, toggleMute, answerCall, callStatus, callDuration, isMuted, currentCall } = useDoraCell();
|
|
662
|
+
return {
|
|
663
|
+
call,
|
|
664
|
+
hangup,
|
|
665
|
+
toggleMute,
|
|
666
|
+
answerCall,
|
|
667
|
+
callStatus,
|
|
668
|
+
callDuration,
|
|
669
|
+
isMuted,
|
|
670
|
+
currentCall
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
function useConnectionStatus() {
|
|
674
|
+
const { connectionStatus, isInitialized, error } = useDoraCell();
|
|
675
|
+
return {
|
|
676
|
+
connectionStatus,
|
|
677
|
+
isInitialized,
|
|
678
|
+
isConnected: connectionStatus === "registered",
|
|
679
|
+
error
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
/*! Bundled license information:
|
|
683
|
+
|
|
684
|
+
lucide-react/dist/esm/shared/src/utils.js:
|
|
685
|
+
lucide-react/dist/esm/defaultAttributes.js:
|
|
686
|
+
lucide-react/dist/esm/Icon.js:
|
|
687
|
+
lucide-react/dist/esm/createLucideIcon.js:
|
|
688
|
+
lucide-react/dist/esm/icons/arrow-down-left.js:
|
|
689
|
+
lucide-react/dist/esm/icons/arrow-up-right.js:
|
|
690
|
+
lucide-react/dist/esm/icons/delete.js:
|
|
691
|
+
lucide-react/dist/esm/icons/maximize.js:
|
|
692
|
+
lucide-react/dist/esm/icons/mic-off.js:
|
|
693
|
+
lucide-react/dist/esm/icons/mic.js:
|
|
694
|
+
lucide-react/dist/esm/icons/minimize-2.js:
|
|
695
|
+
lucide-react/dist/esm/icons/phone.js:
|
|
696
|
+
lucide-react/dist/esm/icons/user.js:
|
|
697
|
+
lucide-react/dist/esm/icons/x.js:
|
|
698
|
+
lucide-react/dist/esm/lucide-react.js:
|
|
699
|
+
(**
|
|
700
|
+
* @license lucide-react v0.555.0 - ISC
|
|
701
|
+
*
|
|
702
|
+
* This source code is licensed under the ISC license.
|
|
703
|
+
* See the LICENSE file in the root directory of this source tree.
|
|
704
|
+
*)
|
|
705
|
+
*/
|
|
706
|
+
|
|
707
|
+
export { CallInterface, Dialpad, DoraCellProvider, useCall, useConnectionStatus, useDoraCell };
|
|
708
|
+
//# sourceMappingURL=index.mjs.map
|
|
709
|
+
//# sourceMappingURL=index.mjs.map
|