@bravophone/webphone 0.1.0
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 +479 -0
- package/dist/bravophone.js +917 -0
- package/dist/bravophone.umd.cjs +340 -0
- package/package.json +65 -0
- package/types/index.d.ts +153 -0
|
@@ -0,0 +1,917 @@
|
|
|
1
|
+
const K = 'stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"', be = `<svg viewBox="0 0 24 24" fill="none" ${K} aria-hidden="true">
|
|
2
|
+
<path class="bp-i-body" d="M15.6 12.8a11 11 0 0 1-4.4-4.4l1-1a1.6 1.6 0 0 0 .3-1.7c-.2-.7-.4-1.4-.5-2.2A1.6 1.6 0 0 0 10.4 2H7.9a1.6 1.6 0 0 0-1.6 1.8 15.8 15.8 0 0 0 2.4 6.9 15.6 15.6 0 0 0 4.8 4.8 15.8 15.8 0 0 0 6.9 2.4 1.6 1.6 0 0 0 1.7-1.6v-2.5a1.6 1.6 0 0 0-1.4-1.6c-.8-.1-1.5-.3-2.2-.5a1.6 1.6 0 0 0-1.7.4z"/>
|
|
3
|
+
<path class="bp-i-wave bp-i-wave-1" d="M14.5 6.2a4 4 0 0 1 3.3 3.3"/>
|
|
4
|
+
<path class="bp-i-wave bp-i-wave-2" d="M14.1 2.6a7.6 7.6 0 0 1 7.3 7.3"/>
|
|
5
|
+
</svg>`, ue = `<svg viewBox="0 0 24 24" fill="none" ${K} aria-hidden="true">
|
|
6
|
+
<path class="bp-i-bar bp-i-bar-1" d="M4 10v4"/>
|
|
7
|
+
<path class="bp-i-bar bp-i-bar-2" d="M8.7 6.5v11"/>
|
|
8
|
+
<path class="bp-i-bar bp-i-bar-3" d="M12 3.5v17"/>
|
|
9
|
+
<path class="bp-i-bar bp-i-bar-4" d="M15.3 6.5v11"/>
|
|
10
|
+
<path class="bp-i-bar bp-i-bar-5" d="M20 10v4"/>
|
|
11
|
+
</svg>`, ge = `<svg viewBox="0 0 24 24" fill="none" ${K} aria-hidden="true">
|
|
12
|
+
<path class="bp-i-body" d="M4 13v-1a8 8 0 0 1 16 0v1"/>
|
|
13
|
+
<path class="bp-i-body" d="M20 14.5v2a3.5 3.5 0 0 1-3.5 3.5H13"/>
|
|
14
|
+
<rect class="bp-i-cup" x="1.6" y="12.6" width="4.4" height="6.4" rx="1.8"/>
|
|
15
|
+
<rect class="bp-i-cup" x="18" y="12.6" width="4.4" height="6.4" rx="1.8"/>
|
|
16
|
+
</svg>`, me = `<svg viewBox="0 0 24 24" fill="none" ${K} aria-hidden="true">
|
|
17
|
+
<path class="bp-i-body" d="M21 11.5a8.4 8.4 0 0 1-9 8.4 9 9 0 0 1-2.9-.5L3 21.5l1.5-4.6A8.4 8.4 0 0 1 12 3.1a8.4 8.4 0 0 1 9 8.4z"/>
|
|
18
|
+
<path class="bp-i-hand" d="M14.6 14.4a6.5 6.5 0 0 1-3.2-3.2l.7-.7a1 1 0 0 0 .2-1.1 6 6 0 0 1-.3-1.2 1 1 0 0 0-1-.8H9.6a1 1 0 0 0-1 1.1 9.6 9.6 0 0 0 4.8 7.2 1 1 0 0 0 1.4-.9v-1.2a1 1 0 0 0-.8-1z"/>
|
|
19
|
+
</svg>`, fe = `<svg viewBox="0 0 10 16" width="10" height="16" fill="currentColor" aria-hidden="true">
|
|
20
|
+
<circle cx="2.6" cy="2.6" r="1.35"/><circle cx="7.4" cy="2.6" r="1.35"/>
|
|
21
|
+
<circle cx="2.6" cy="8" r="1.35"/><circle cx="7.4" cy="8" r="1.35"/>
|
|
22
|
+
<circle cx="2.6" cy="13.4" r="1.35"/><circle cx="7.4" cy="13.4" r="1.35"/>
|
|
23
|
+
</svg>`, j = {
|
|
24
|
+
"phone-waves": be,
|
|
25
|
+
waveform: ue,
|
|
26
|
+
headset: ge,
|
|
27
|
+
"chat-phone": me
|
|
28
|
+
}, Q = "phone-waves", xe = `
|
|
29
|
+
.bp-launcher-icon svg { width: 100%; height: 100%; display: block; overflow: visible; }
|
|
30
|
+
|
|
31
|
+
/* Ponto de status no canto do ícone — verde registrado, cinza sem linha. */
|
|
32
|
+
.bp-launcher-icon::after {
|
|
33
|
+
content: '';
|
|
34
|
+
position: absolute;
|
|
35
|
+
right: -1px; bottom: -1px;
|
|
36
|
+
width: 7px; height: 7px;
|
|
37
|
+
border-radius: 50%;
|
|
38
|
+
background: #94a3b8;
|
|
39
|
+
box-shadow: 0 0 0 2px #6c5ce7;
|
|
40
|
+
transition: background .2s ease;
|
|
41
|
+
}
|
|
42
|
+
.bp-launcher[data-state="ready"] .bp-launcher-icon::after { background: #22c55e; }
|
|
43
|
+
.bp-launcher[data-state="error"] .bp-launcher-icon::after { background: #ef4444; }
|
|
44
|
+
/* Tocando ou em chamada, o ponto sai: a animacao ja comunica, e ele sujaria. */
|
|
45
|
+
.bp-launcher[data-state="ringing"] .bp-launcher-icon::after,
|
|
46
|
+
.bp-launcher[data-state="incall"] .bp-launcher-icon::after { display: none; }
|
|
47
|
+
|
|
48
|
+
/* --- ondas do handset: crescem de dentro para fora --- */
|
|
49
|
+
.bp-i-wave { opacity: .45; transform-origin: 12px 12px; }
|
|
50
|
+
.bp-launcher[data-state="ready"] .bp-i-wave { opacity: .75; }
|
|
51
|
+
@keyframes bp-i-emit {
|
|
52
|
+
0% { opacity: 0; transform: scale(.6); }
|
|
53
|
+
40% { opacity: 1; }
|
|
54
|
+
100% { opacity: 0; transform: scale(1.15); }
|
|
55
|
+
}
|
|
56
|
+
.bp-launcher[data-state="ringing"] .bp-i-wave,
|
|
57
|
+
.bp-launcher[data-state="incall"] .bp-i-wave {
|
|
58
|
+
animation: bp-i-emit 1.4s ease-out infinite;
|
|
59
|
+
}
|
|
60
|
+
.bp-launcher[data-state="ringing"] .bp-i-wave-2,
|
|
61
|
+
.bp-launcher[data-state="incall"] .bp-i-wave-2 { animation-delay: .35s; }
|
|
62
|
+
|
|
63
|
+
/* --- handset balanca quando toca, como um telefone de verdade --- */
|
|
64
|
+
@keyframes bp-i-shake {
|
|
65
|
+
0%, 100% { transform: rotate(0deg); }
|
|
66
|
+
15%, 45% { transform: rotate(-13deg); }
|
|
67
|
+
30%, 60% { transform: rotate(13deg); }
|
|
68
|
+
75% { transform: rotate(0deg); }
|
|
69
|
+
}
|
|
70
|
+
.bp-launcher[data-state="ringing"] .bp-i-body,
|
|
71
|
+
.bp-launcher[data-state="ringing"] .bp-i-hand {
|
|
72
|
+
transform-origin: 12px 12px;
|
|
73
|
+
animation: bp-i-shake 1s ease-in-out infinite;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/* --- equalizador: barras dancam so em chamada --- */
|
|
77
|
+
.bp-i-bar { transform-origin: center; transition: transform .25s ease; }
|
|
78
|
+
.bp-launcher[data-state="incall"] .bp-i-bar {
|
|
79
|
+
animation: bp-i-dance .9s ease-in-out infinite;
|
|
80
|
+
}
|
|
81
|
+
@keyframes bp-i-dance {
|
|
82
|
+
0%, 100% { transform: scaleY(.45); }
|
|
83
|
+
50% { transform: scaleY(1); }
|
|
84
|
+
}
|
|
85
|
+
.bp-launcher[data-state="incall"] .bp-i-bar-2 { animation-delay: .12s }
|
|
86
|
+
.bp-launcher[data-state="incall"] .bp-i-bar-3 { animation-delay: .24s }
|
|
87
|
+
.bp-launcher[data-state="incall"] .bp-i-bar-4 { animation-delay: .36s }
|
|
88
|
+
.bp-launcher[data-state="incall"] .bp-i-bar-5 { animation-delay: .48s }
|
|
89
|
+
|
|
90
|
+
/* Hover: o icone da uma leve subida, so para o botao parecer vivo. */
|
|
91
|
+
.bp-launcher:hover .bp-launcher-icon { transform: translateY(-1px); }
|
|
92
|
+
.bp-launcher-icon { transition: transform .16s ease; }
|
|
93
|
+
|
|
94
|
+
@media (prefers-reduced-motion: reduce) {
|
|
95
|
+
.bp-i-wave, .bp-i-body, .bp-i-hand, .bp-i-bar { animation: none !important; }
|
|
96
|
+
.bp-launcher-icon { transition: none; }
|
|
97
|
+
}
|
|
98
|
+
`, we = `
|
|
99
|
+
:host { all: initial; }
|
|
100
|
+
* { box-sizing: border-box; }
|
|
101
|
+
|
|
102
|
+
.bp-root {
|
|
103
|
+
position: fixed;
|
|
104
|
+
z-index: 2147483000;
|
|
105
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
106
|
+
display: flex;
|
|
107
|
+
flex-direction: column;
|
|
108
|
+
background: #1e1e2d;
|
|
109
|
+
border-radius: 12px;
|
|
110
|
+
box-shadow: 0 12px 40px rgba(0,0,0,.45), 0 0 0 1px rgba(255,255,255,.06);
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
will-change: transform;
|
|
113
|
+
}
|
|
114
|
+
.bp-root[hidden] { display: none !important; }
|
|
115
|
+
.bp-root { transition: left .16s ease, top .16s ease, width .16s ease, height .16s ease; }
|
|
116
|
+
.bp-root.bp-dragging,
|
|
117
|
+
.bp-root.bp-resizing { transition: none; user-select: none; }
|
|
118
|
+
.bp-root.bp-minimized .bp-body,
|
|
119
|
+
.bp-root.bp-minimized /* ---- modo sem moldura (frame:'none', padrao) ----
|
|
120
|
+
A UI do webphone ocupa 100% da janela. Os controles ficam sobrepostos,
|
|
121
|
+
invisiveis ate o hover, e nunca empurram nem redimensionam o conteudo. */
|
|
122
|
+
.bp-root.bp-chromeless .bp-header { display: none; }
|
|
123
|
+
.bp-root.bp-chromeless .bp-overlay { display: flex; }
|
|
124
|
+
|
|
125
|
+
.bp-overlay {
|
|
126
|
+
position: absolute;
|
|
127
|
+
top: 6px; right: 6px;
|
|
128
|
+
display: none;
|
|
129
|
+
gap: 2px;
|
|
130
|
+
padding: 3px;
|
|
131
|
+
border-radius: 8px;
|
|
132
|
+
background: rgba(10,10,16,.72);
|
|
133
|
+
backdrop-filter: blur(6px);
|
|
134
|
+
opacity: 0;
|
|
135
|
+
transition: opacity .14s ease;
|
|
136
|
+
z-index: 3;
|
|
137
|
+
pointer-events: none;
|
|
138
|
+
}
|
|
139
|
+
.bp-root.bp-chromeless:hover .bp-overlay,
|
|
140
|
+
.bp-root.bp-chromeless:focus-within .bp-overlay { opacity: 1; pointer-events: auto; }
|
|
141
|
+
.bp-root.bp-dragging .bp-overlay { opacity: 0; pointer-events: none; }
|
|
142
|
+
.bp-overlay .bp-btn { width: 22px; height: 22px; font-size: 13px; color: #cbd5e1; }
|
|
143
|
+
|
|
144
|
+
/* Sem barra de titulo o arraste vem de dentro do iframe: o cursor tambem. */
|
|
145
|
+
.bp-root.bp-chromeless.bp-dragging { cursor: grabbing; }
|
|
146
|
+
|
|
147
|
+
.bp-resize { display: none; }
|
|
148
|
+
.bp-root.bp-minimized { height: auto !important; }
|
|
149
|
+
|
|
150
|
+
.bp-header {
|
|
151
|
+
display: flex;
|
|
152
|
+
align-items: center;
|
|
153
|
+
gap: 8px;
|
|
154
|
+
height: 36px;
|
|
155
|
+
padding: 0 6px 0 12px;
|
|
156
|
+
background: #15151f;
|
|
157
|
+
color: #e6e6f0;
|
|
158
|
+
cursor: grab;
|
|
159
|
+
flex: 0 0 auto;
|
|
160
|
+
touch-action: none;
|
|
161
|
+
}
|
|
162
|
+
.bp-header:active { cursor: grabbing; }
|
|
163
|
+
.bp-title {
|
|
164
|
+
flex: 1 1 auto;
|
|
165
|
+
font-size: 12px;
|
|
166
|
+
font-weight: 600;
|
|
167
|
+
letter-spacing: .3px;
|
|
168
|
+
white-space: nowrap;
|
|
169
|
+
overflow: hidden;
|
|
170
|
+
text-overflow: ellipsis;
|
|
171
|
+
}
|
|
172
|
+
.bp-status {
|
|
173
|
+
width: 8px; height: 8px; border-radius: 50%;
|
|
174
|
+
background: #6b7280; flex: 0 0 auto;
|
|
175
|
+
}
|
|
176
|
+
.bp-status[data-state="ready"] { background: #22c55e; }
|
|
177
|
+
.bp-status[data-state="ringing"] { background: #f59e0b; animation: bp-pulse 1s infinite; }
|
|
178
|
+
.bp-status[data-state="incall"] { background: #22c55e; animation: bp-pulse 2s infinite; }
|
|
179
|
+
.bp-status[data-state="error"] { background: #ef4444; }
|
|
180
|
+
@keyframes bp-pulse { 0%,100% { opacity: 1 } 50% { opacity: .35 } }
|
|
181
|
+
|
|
182
|
+
.bp-btn {
|
|
183
|
+
width: 24px; height: 24px;
|
|
184
|
+
display: grid; place-items: center;
|
|
185
|
+
border: 0; border-radius: 6px;
|
|
186
|
+
background: transparent; color: #9ca3af;
|
|
187
|
+
cursor: pointer; font-size: 15px; line-height: 1;
|
|
188
|
+
flex: 0 0 auto; padding: 0;
|
|
189
|
+
}
|
|
190
|
+
.bp-btn:hover { background: rgba(255,255,255,.08); color: #fff; }
|
|
191
|
+
.bp-btn-close:hover { background: #dc2626; color: #fff; }
|
|
192
|
+
|
|
193
|
+
.bp-body { flex: 1 1 auto; position: relative; min-height: 0; }
|
|
194
|
+
.bp-frame {
|
|
195
|
+
width: 100%; height: 100%;
|
|
196
|
+
border: 0; display: block; background: #1e1e2d;
|
|
197
|
+
}
|
|
198
|
+
/* Durante o arraste o iframe precisa parar de capturar o ponteiro,
|
|
199
|
+
senão o mousemove é engolido pelo documento interno e o drag "trava". */
|
|
200
|
+
.bp-root.bp-dragging .bp-frame,
|
|
201
|
+
.bp-root.bp-resizing .bp-frame { pointer-events: none; }
|
|
202
|
+
|
|
203
|
+
/* ---- alcas de redimensionamento (8 bordas/cantos) ----
|
|
204
|
+
Faixas invisiveis por fora da borda: nao cobrem a UI do webphone e ainda
|
|
205
|
+
assim dao uma area de clique confortavel. */
|
|
206
|
+
.bp-h {
|
|
207
|
+
position: absolute;
|
|
208
|
+
z-index: 4;
|
|
209
|
+
touch-action: none;
|
|
210
|
+
}
|
|
211
|
+
.bp-h-n { top: -4px; left: 10px; right: 10px; height: 8px; cursor: ns-resize; }
|
|
212
|
+
.bp-h-s { bottom: -4px; left: 10px; right: 10px; height: 8px; cursor: ns-resize; }
|
|
213
|
+
.bp-h-w { left: -4px; top: 10px; bottom: 10px; width: 8px; cursor: ew-resize; }
|
|
214
|
+
.bp-h-e { right: -4px; top: 10px; bottom: 10px; width: 8px; cursor: ew-resize; }
|
|
215
|
+
.bp-h-nw { top: -4px; left: -4px; width: 14px; height: 14px; cursor: nwse-resize; }
|
|
216
|
+
.bp-h-ne { top: -4px; right: -4px; width: 14px; height: 14px; cursor: nesw-resize; }
|
|
217
|
+
.bp-h-sw { bottom: -4px; left: -4px; width: 14px; height: 14px; cursor: nesw-resize; }
|
|
218
|
+
.bp-h-se { bottom: -4px; right: -4px; width: 14px; height: 14px; cursor: nwse-resize; }
|
|
219
|
+
|
|
220
|
+
/* Marcador visivel so no canto inferior direito, como affordance. */
|
|
221
|
+
.bp-h-se::after {
|
|
222
|
+
content: ''; position: absolute; right: 3px; bottom: 3px;
|
|
223
|
+
width: 9px; height: 9px;
|
|
224
|
+
background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,.3) 50%);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/* Uma janela docada nao se redimensiona pelas bordas coladas na viewport. */
|
|
228
|
+
.bp-root.bp-docked { border-radius: 0; }
|
|
229
|
+
|
|
230
|
+
/* ---- previa de encaixe (docking) ----
|
|
231
|
+
Fantasma que mostra onde a janela vai parar antes de soltar. */
|
|
232
|
+
.bp-preview {
|
|
233
|
+
position: fixed;
|
|
234
|
+
z-index: 2147482998;
|
|
235
|
+
border-radius: 10px;
|
|
236
|
+
background: rgba(37,99,235,.18);
|
|
237
|
+
border: 2px solid rgba(96,165,250,.85);
|
|
238
|
+
box-shadow: 0 0 0 1px rgba(0,0,0,.25), inset 0 0 40px rgba(96,165,250,.15);
|
|
239
|
+
pointer-events: none;
|
|
240
|
+
transition: left .12s ease, top .12s ease, width .12s ease, height .12s ease;
|
|
241
|
+
}
|
|
242
|
+
.bp-preview[hidden] { display: none !important; }
|
|
243
|
+
|
|
244
|
+
.bp-launcher {
|
|
245
|
+
position: fixed;
|
|
246
|
+
top: 50%;
|
|
247
|
+
z-index: 2147482999;
|
|
248
|
+
display: flex;
|
|
249
|
+
align-items: center;
|
|
250
|
+
gap: 0;
|
|
251
|
+
height: 48px;
|
|
252
|
+
padding: 0 11px;
|
|
253
|
+
background: #6c5ce7;
|
|
254
|
+
color: #fff;
|
|
255
|
+
border: 0;
|
|
256
|
+
cursor: pointer;
|
|
257
|
+
box-shadow: 0 4px 16px rgba(76,58,190,.42), 0 0 0 1px rgba(255,255,255,.08) inset;
|
|
258
|
+
transition: padding .16s ease, background .16s ease, box-shadow .16s ease;
|
|
259
|
+
touch-action: none;
|
|
260
|
+
user-select: none;
|
|
261
|
+
-webkit-user-select: none;
|
|
262
|
+
}
|
|
263
|
+
.bp-launcher[hidden] { display: none !important; }
|
|
264
|
+
|
|
265
|
+
/* Colada na borda: os cantos arredondados sao so os que ficam para dentro. */
|
|
266
|
+
.bp-launcher[data-side="right"] { border-radius: 14px 0 0 14px; }
|
|
267
|
+
.bp-launcher[data-side="left"] { border-radius: 0 14px 14px 0; }
|
|
268
|
+
|
|
269
|
+
.bp-launcher:hover,
|
|
270
|
+
.bp-launcher:focus-visible {
|
|
271
|
+
background: #5b4bd6;
|
|
272
|
+
box-shadow: 0 6px 22px rgba(76,58,190,.55), 0 0 0 1px rgba(255,255,255,.12) inset;
|
|
273
|
+
}
|
|
274
|
+
.bp-launcher:focus-visible { outline: 2px solid #c7d2fe; outline-offset: 2px; }
|
|
275
|
+
.bp-launcher.bp-launcher-dragging { cursor: grabbing; transition: none; }
|
|
276
|
+
|
|
277
|
+
/* Alca de arraste: os pontos ficam sempre com largura fixa e somem por
|
|
278
|
+
opacidade + margem negativa. Animar a largura cortaria o SVG no meio. */
|
|
279
|
+
.bp-launcher-grip {
|
|
280
|
+
width: 10px;
|
|
281
|
+
height: 16px;
|
|
282
|
+
flex: 0 0 auto;
|
|
283
|
+
opacity: 0;
|
|
284
|
+
color: rgba(255,255,255,.9);
|
|
285
|
+
cursor: grab;
|
|
286
|
+
transition: opacity .16s ease, margin .16s ease;
|
|
287
|
+
}
|
|
288
|
+
.bp-launcher-grip svg { display: block; }
|
|
289
|
+
.bp-launcher[data-side="right"] .bp-launcher-grip { order: 0; margin-right: -10px; }
|
|
290
|
+
.bp-launcher[data-side="left"] .bp-launcher-grip { order: 2; margin-left: -10px; }
|
|
291
|
+
|
|
292
|
+
.bp-launcher:hover .bp-launcher-grip,
|
|
293
|
+
.bp-launcher:focus-visible .bp-launcher-grip,
|
|
294
|
+
.bp-launcher.bp-launcher-dragging .bp-launcher-grip { opacity: 1; }
|
|
295
|
+
.bp-launcher[data-side="right"]:hover .bp-launcher-grip,
|
|
296
|
+
.bp-launcher[data-side="right"]:focus-visible .bp-launcher-grip,
|
|
297
|
+
.bp-launcher[data-side="right"].bp-launcher-dragging .bp-launcher-grip { margin-right: 7px; }
|
|
298
|
+
.bp-launcher[data-side="left"]:hover .bp-launcher-grip,
|
|
299
|
+
.bp-launcher[data-side="left"]:focus-visible .bp-launcher-grip,
|
|
300
|
+
.bp-launcher[data-side="left"].bp-launcher-dragging .bp-launcher-grip { margin-left: 7px; }
|
|
301
|
+
|
|
302
|
+
.bp-launcher-icon {
|
|
303
|
+
position: relative;
|
|
304
|
+
order: 1;
|
|
305
|
+
display: grid;
|
|
306
|
+
place-items: center;
|
|
307
|
+
width: 22px;
|
|
308
|
+
height: 22px;
|
|
309
|
+
flex: 0 0 auto;
|
|
310
|
+
pointer-events: none;
|
|
311
|
+
}
|
|
312
|
+
.bp-launcher-icon svg { width: 100%; height: 100%; display: block; }
|
|
313
|
+
|
|
314
|
+
.bp-launcher .bp-badge {
|
|
315
|
+
position: absolute;
|
|
316
|
+
top: -5px;
|
|
317
|
+
min-width: 18px;
|
|
318
|
+
height: 18px;
|
|
319
|
+
padding: 0 5px;
|
|
320
|
+
border-radius: 9px;
|
|
321
|
+
background: #ef4444;
|
|
322
|
+
color: #fff;
|
|
323
|
+
font: 700 11px/18px -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
|
324
|
+
text-align: center;
|
|
325
|
+
display: none;
|
|
326
|
+
box-shadow: 0 0 0 2px #6c5ce7;
|
|
327
|
+
}
|
|
328
|
+
.bp-launcher[data-side="right"] .bp-badge { left: -6px; }
|
|
329
|
+
.bp-launcher[data-side="left"] .bp-badge { right: -6px; }
|
|
330
|
+
.bp-launcher[data-badge]:not([data-badge=""]) .bp-badge { display: block; }
|
|
331
|
+
|
|
332
|
+
/* Chamada entrante: a aba pulsa para chamar atencao mesmo fechada. */
|
|
333
|
+
@keyframes bp-launcher-ring {
|
|
334
|
+
0%, 100% { box-shadow: 0 4px 16px rgba(76,58,190,.42); }
|
|
335
|
+
50% { box-shadow: 0 4px 16px rgba(239,68,68,.75), 0 0 0 4px rgba(239,68,68,.25); }
|
|
336
|
+
}
|
|
337
|
+
.bp-launcher[data-badge]:not([data-badge=""]) { animation: bp-launcher-ring 1.1s infinite; }
|
|
338
|
+
|
|
339
|
+
@media (prefers-reduced-motion: reduce) {
|
|
340
|
+
.bp-launcher, .bp-launcher-grip { transition: none; }
|
|
341
|
+
.bp-launcher[data-badge]:not([data-badge=""]) { animation: none; }
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
${xe}
|
|
345
|
+
`, pe = "bravophone:widget:geometry", q = 28, J = 32;
|
|
346
|
+
function ye(e) {
|
|
347
|
+
try {
|
|
348
|
+
const a = localStorage.getItem(pe);
|
|
349
|
+
if (!a) return { ...e };
|
|
350
|
+
const i = JSON.parse(a);
|
|
351
|
+
return i.dock ? { ...e, ...i, ...F(i.dock, i) } : { ...e, ...i };
|
|
352
|
+
} catch (a) {
|
|
353
|
+
return { ...e };
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
function X(e) {
|
|
357
|
+
try {
|
|
358
|
+
localStorage.setItem(pe, JSON.stringify(e));
|
|
359
|
+
} catch (a) {
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
function se(e) {
|
|
363
|
+
return {
|
|
364
|
+
minWidth: e.minWidth,
|
|
365
|
+
minHeight: e.minHeight,
|
|
366
|
+
// Teto relativo à viewport: numa tela larga o histórico de chamadas pode
|
|
367
|
+
// usar o espaço que precisa, sem um limite fixo apertando.
|
|
368
|
+
maxWidth: Math.max(e.minWidth, Math.min(e.maxWidth, window.innerWidth)),
|
|
369
|
+
maxHeight: Math.max(e.minHeight, Math.min(e.maxHeight, window.innerHeight))
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
function R(e) {
|
|
373
|
+
const a = Math.max(0, window.innerWidth - e.width), i = Math.max(0, window.innerHeight - 40);
|
|
374
|
+
return {
|
|
375
|
+
...e,
|
|
376
|
+
x: Math.min(Math.max(0, e.x), a),
|
|
377
|
+
y: Math.min(Math.max(0, e.y), i)
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function F(e, a) {
|
|
381
|
+
const i = window.innerWidth, r = window.innerHeight, o = Math.round(i / 2), s = Math.round(r / 2), u = Math.min(a.width, i);
|
|
382
|
+
switch (e) {
|
|
383
|
+
case "left":
|
|
384
|
+
return { x: 0, y: 0, width: u, height: r };
|
|
385
|
+
case "right":
|
|
386
|
+
return { x: i - u, y: 0, width: u, height: r };
|
|
387
|
+
case "left-half":
|
|
388
|
+
return { x: 0, y: 0, width: o, height: r };
|
|
389
|
+
case "right-half":
|
|
390
|
+
return { x: i - o, y: 0, width: o, height: r };
|
|
391
|
+
case "top":
|
|
392
|
+
return { x: a.x, y: 0, width: u, height: s };
|
|
393
|
+
case "bottom":
|
|
394
|
+
return { x: a.x, y: r - s, width: u, height: s };
|
|
395
|
+
case "top-half":
|
|
396
|
+
return { x: 0, y: 0, width: i, height: s };
|
|
397
|
+
case "bottom-half":
|
|
398
|
+
return { x: 0, y: r - s, width: i, height: s };
|
|
399
|
+
case "max":
|
|
400
|
+
return { x: 0, y: 0, width: i, height: r };
|
|
401
|
+
default:
|
|
402
|
+
return { x: a.x, y: a.y, width: a.width, height: a.height };
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
function ve(e, a, i) {
|
|
406
|
+
const r = window.innerWidth, o = window.innerHeight, s = e <= q, u = e >= r - q, f = a <= q, t = a >= o - q;
|
|
407
|
+
return (f || t) && s ? "left-half" : (f || t) && u ? "right-half" : f ? i || "max" : t ? "bottom-half" : s ? "left" : u ? "right" : null;
|
|
408
|
+
}
|
|
409
|
+
function ke({ root: e, handle: a, handles: i, preview: r, geometry: o, limits: s, topDock: u, onChange: f }) {
|
|
410
|
+
let t = { ...o }, g = null, p = "", d = null, h = null, w = null, y = null;
|
|
411
|
+
const z = () => {
|
|
412
|
+
e.style.left = `${t.x}px`, e.style.top = `${t.y}px`, e.style.width = `${t.width}px`, e.style.height = `${t.height}px`, e.classList.toggle("bp-docked", !!t.dock), f == null || f(t);
|
|
413
|
+
}, C = (n) => {
|
|
414
|
+
if (r) {
|
|
415
|
+
if (!n) {
|
|
416
|
+
r.hidden = !0;
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
r.hidden = !1, r.style.left = `${n.x}px`, r.style.top = `${n.y}px`, r.style.width = `${n.width}px`, r.style.height = `${n.height}px`;
|
|
420
|
+
}
|
|
421
|
+
}, D = (n, b) => {
|
|
422
|
+
const l = se(s);
|
|
423
|
+
let { x: S, y: m, width: k, height: H } = d;
|
|
424
|
+
p.includes("e") && (k = d.width + n), p.includes("w") && (k = d.width - n), p.includes("s") && (H = d.height + b), p.includes("n") && (H = d.height - b), k = Math.min(l.maxWidth, Math.max(l.minWidth, k)), H = Math.min(l.maxHeight, Math.max(l.minHeight, H)), p.includes("w") && (S = d.x + (d.width - k)), p.includes("n") && (m = d.y + (d.height - H)), p.includes("e") && Math.abs(S + k - window.innerWidth) <= J && (k = window.innerWidth - S), p.includes("w") && Math.abs(S) <= J && (k += S, S = 0), p.includes("s") && Math.abs(m + H - window.innerHeight) <= J && (H = window.innerHeight - m), p.includes("n") && Math.abs(m) <= J && (H += m, m = 0), t = { ...t, x: S, y: m, width: k, height: H, dock: null }, z();
|
|
425
|
+
}, $ = (n, b, l, S) => {
|
|
426
|
+
if (t.dock) {
|
|
427
|
+
const m = y || { width: s.width, height: s.height }, k = h.x / Math.max(1, t.width);
|
|
428
|
+
t = { ...t, ...m, dock: null }, h = { x: Math.round(m.width * k), y: Math.min(h.y, 32) }, d = {
|
|
429
|
+
...d,
|
|
430
|
+
x: l - h.x,
|
|
431
|
+
y: S - h.y,
|
|
432
|
+
px: d.px + n,
|
|
433
|
+
py: d.py + b
|
|
434
|
+
}, n = 0, b = 0;
|
|
435
|
+
}
|
|
436
|
+
t = R({ ...t, x: d.x + n, y: d.y + b }), z(), w = ve(l, S, u), C(w ? F(w, t) : null);
|
|
437
|
+
}, O = () => {
|
|
438
|
+
w && (y = y || { width: t.width, height: t.height }, t = { ...t, ...F(w, t), dock: w }, z());
|
|
439
|
+
}, P = (n, b) => (l) => {
|
|
440
|
+
l.button === 0 && (n === "drag" && l.target.closest(".bp-btn") || (g = n, p = b || "", d = { px: l.clientX, py: l.clientY, ...t }, h = { x: l.clientX - t.x, y: l.clientY - t.y }, t.dock || (y = { width: t.width, height: t.height }), e.classList.add(n === "drag" ? "bp-dragging" : "bp-resizing"), l.currentTarget.setPointerCapture(l.pointerId), l.preventDefault()));
|
|
441
|
+
}, U = (n) => {
|
|
442
|
+
if (!g || !d) return;
|
|
443
|
+
const b = n.clientX - d.px, l = n.clientY - d.py;
|
|
444
|
+
g === "drag" ? $(b, l, n.clientX, n.clientY) : D(b, l);
|
|
445
|
+
}, B = (n) => {
|
|
446
|
+
if (g) {
|
|
447
|
+
e.classList.remove("bp-dragging", "bp-resizing"), C(null), g === "drag" && O(), g = null, p = "", d = null, w = null;
|
|
448
|
+
try {
|
|
449
|
+
n.currentTarget.releasePointerCapture(n.pointerId);
|
|
450
|
+
} catch (b) {
|
|
451
|
+
}
|
|
452
|
+
X(t);
|
|
453
|
+
}
|
|
454
|
+
};
|
|
455
|
+
a.addEventListener("pointerdown", P("drag")), a.addEventListener("pointermove", U), a.addEventListener("pointerup", B), a.addEventListener("pointercancel", B), (i || []).forEach(({ el: n, dir: b }) => {
|
|
456
|
+
n.addEventListener("pointerdown", P("resize", b)), n.addEventListener("pointermove", U), n.addEventListener("pointerup", B), n.addEventListener("pointercancel", B);
|
|
457
|
+
});
|
|
458
|
+
const V = () => {
|
|
459
|
+
t = t.dock ? { ...t, ...F(t.dock, t) } : R(t), z();
|
|
460
|
+
};
|
|
461
|
+
window.addEventListener("resize", V);
|
|
462
|
+
const G = {
|
|
463
|
+
start(n) {
|
|
464
|
+
var b, l;
|
|
465
|
+
g = "drag", d = { px: n.x, py: n.y, ...t }, h = { x: (b = n.cx) != null ? b : t.width / 2, y: (l = n.cy) != null ? l : 20 }, t.dock || (y = { width: t.width, height: t.height }), e.classList.add("bp-dragging");
|
|
466
|
+
},
|
|
467
|
+
move(n) {
|
|
468
|
+
if (g !== "drag" || !d) return;
|
|
469
|
+
const b = n.x - d.px, l = n.y - d.py;
|
|
470
|
+
$(b, l, d.x + b + h.x, d.y + l + h.y);
|
|
471
|
+
},
|
|
472
|
+
end() {
|
|
473
|
+
g === "drag" && (e.classList.remove("bp-dragging"), C(null), O(), g = null, d = null, w = null, X(t));
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
return z(), {
|
|
477
|
+
external: G,
|
|
478
|
+
get geometry() {
|
|
479
|
+
return { ...t };
|
|
480
|
+
},
|
|
481
|
+
set(n) {
|
|
482
|
+
const b = se(s), l = { ...t, ...n, dock: null };
|
|
483
|
+
l.width = Math.min(b.maxWidth, Math.max(b.minWidth, l.width)), l.height = Math.min(b.maxHeight, Math.max(b.minHeight, l.height)), t = R(l), z(), X(t);
|
|
484
|
+
},
|
|
485
|
+
/** 'left' | 'right' | 'left-half' | 'right-half' | 'max' | 'float' */
|
|
486
|
+
dock(n) {
|
|
487
|
+
if (!n || n === "float") {
|
|
488
|
+
const b = y || { width: s.width, height: s.height };
|
|
489
|
+
t = R({ ...t, ...b, dock: null });
|
|
490
|
+
} else
|
|
491
|
+
t.dock || (y = { width: t.width, height: t.height }), t = { ...t, ...F(n, t), dock: n };
|
|
492
|
+
z(), X(t);
|
|
493
|
+
},
|
|
494
|
+
destroy() {
|
|
495
|
+
window.removeEventListener("resize", V);
|
|
496
|
+
}
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
const de = "bravophone/v1";
|
|
500
|
+
function Me({ frame: e, origin: a, onEvent: i }) {
|
|
501
|
+
const r = /* @__PURE__ */ new Map();
|
|
502
|
+
let o = 0, s = !1;
|
|
503
|
+
const u = [], f = (g) => {
|
|
504
|
+
var d;
|
|
505
|
+
if (g.origin !== a || g.source !== e.contentWindow) return;
|
|
506
|
+
const p = g.data;
|
|
507
|
+
if (!(!p || p.protocol !== de)) {
|
|
508
|
+
if (p.type === "ready") {
|
|
509
|
+
for (s = !0; u.length; ) u.shift()();
|
|
510
|
+
i == null || i("ready", (d = p.payload) != null ? d : {});
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
if (p.type === "reply") {
|
|
514
|
+
const h = r.get(p.id);
|
|
515
|
+
if (!h) return;
|
|
516
|
+
r.delete(p.id), clearTimeout(h.timer), p.error ? h.reject(new Error(p.error)) : h.resolve(p.payload);
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
p.type === "event" && (i == null || i(p.name, p.payload));
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
window.addEventListener("message", f);
|
|
523
|
+
function t(g, p, { timeout: d = 15e3 } = {}) {
|
|
524
|
+
return new Promise((h, w) => {
|
|
525
|
+
const y = `${Date.now()}-${++o}`, z = () => {
|
|
526
|
+
var D;
|
|
527
|
+
const C = setTimeout(() => {
|
|
528
|
+
r.delete(y), w(new Error(`Bravophone: timeout no comando "${g}"`));
|
|
529
|
+
}, d);
|
|
530
|
+
r.set(y, { resolve: h, reject: w, timer: C }), (D = e.contentWindow) == null || D.postMessage(
|
|
531
|
+
{ protocol: de, type: "command", id: y, command: g, payload: p },
|
|
532
|
+
a
|
|
533
|
+
);
|
|
534
|
+
};
|
|
535
|
+
s ? z() : u.push(z);
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
call: t,
|
|
540
|
+
get ready() {
|
|
541
|
+
return s;
|
|
542
|
+
},
|
|
543
|
+
destroy() {
|
|
544
|
+
window.removeEventListener("message", f), r.forEach(({ reject: g, timer: p }) => {
|
|
545
|
+
clearTimeout(p), g(new Error("Bravophone: widget destruído"));
|
|
546
|
+
}), r.clear();
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
const he = "bravophone:launcher", ze = 4, ee = 8;
|
|
551
|
+
function Le(e) {
|
|
552
|
+
try {
|
|
553
|
+
const a = localStorage.getItem(he);
|
|
554
|
+
return a ? { ...e, ...JSON.parse(a) } : { ...e };
|
|
555
|
+
} catch (a) {
|
|
556
|
+
return { ...e };
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function te(e) {
|
|
560
|
+
try {
|
|
561
|
+
localStorage.setItem(he, JSON.stringify(e));
|
|
562
|
+
} catch (a) {
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
function Y(e, a) {
|
|
566
|
+
const i = Math.max(ee, window.innerHeight - a - ee);
|
|
567
|
+
return Math.min(Math.max(ee, e), i);
|
|
568
|
+
}
|
|
569
|
+
function Ee({ el: e, side: a, y: i, onOpen: r }) {
|
|
570
|
+
let o = { side: a, y: Y(i, e.offsetHeight || 48) }, s = null, u = !1;
|
|
571
|
+
const f = () => {
|
|
572
|
+
e.dataset.side = o.side, e.style.top = `${o.y}px`, e.style.right = o.side === "right" ? "0px" : "auto", e.style.left = o.side === "left" ? "0px" : "auto";
|
|
573
|
+
}, t = (h) => {
|
|
574
|
+
h.button === 0 && (s = { py: h.clientY, y: o.y }, u = !1, e.setPointerCapture(h.pointerId));
|
|
575
|
+
}, g = (h) => {
|
|
576
|
+
if (!s) return;
|
|
577
|
+
const w = h.clientY - s.py;
|
|
578
|
+
!u && Math.abs(w) < ze || (u || (u = !0, e.classList.add("bp-launcher-dragging")), o.y = Y(s.y + w, e.offsetHeight), f());
|
|
579
|
+
}, p = (h) => {
|
|
580
|
+
if (!s) return;
|
|
581
|
+
const w = u;
|
|
582
|
+
s = null, u = !1, e.classList.remove("bp-launcher-dragging");
|
|
583
|
+
try {
|
|
584
|
+
e.releasePointerCapture(h.pointerId);
|
|
585
|
+
} catch (y) {
|
|
586
|
+
}
|
|
587
|
+
w ? te(o) : r == null || r();
|
|
588
|
+
};
|
|
589
|
+
e.addEventListener("pointerdown", t), e.addEventListener("pointermove", g), e.addEventListener("pointerup", p), e.addEventListener("pointercancel", p), e.addEventListener("keydown", (h) => {
|
|
590
|
+
(h.key === "Enter" || h.key === " ") && (h.preventDefault(), r == null || r());
|
|
591
|
+
});
|
|
592
|
+
const d = () => {
|
|
593
|
+
o.y = Y(o.y, e.offsetHeight), f();
|
|
594
|
+
};
|
|
595
|
+
return window.addEventListener("resize", d), f(), {
|
|
596
|
+
get state() {
|
|
597
|
+
return { ...o };
|
|
598
|
+
},
|
|
599
|
+
setSide(h) {
|
|
600
|
+
o.side = h, f(), te(o);
|
|
601
|
+
},
|
|
602
|
+
moveTo(h) {
|
|
603
|
+
o.y = Y(h, e.offsetHeight), f(), te(o);
|
|
604
|
+
},
|
|
605
|
+
destroy() {
|
|
606
|
+
window.removeEventListener("resize", d);
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
const ce = {
|
|
611
|
+
// 380x640 é a geometria da janela nativa da extensão — o layout do
|
|
612
|
+
// webphone é calibrado para ela, então é o ponto de partida aqui também.
|
|
613
|
+
width: 380,
|
|
614
|
+
height: 640,
|
|
615
|
+
minWidth: 320,
|
|
616
|
+
minHeight: 420,
|
|
617
|
+
// Tetos generosos: o limite real é a viewport (ver limitsFor). O histórico
|
|
618
|
+
// de chamadas fica ilegível espremido em 380px, então dá para alargar muito.
|
|
619
|
+
maxWidth: 1600,
|
|
620
|
+
maxHeight: 1400,
|
|
621
|
+
margin: 24
|
|
622
|
+
};
|
|
623
|
+
function v(e, a = {}, i = []) {
|
|
624
|
+
const r = document.createElement(e);
|
|
625
|
+
for (const [o, s] of Object.entries(a))
|
|
626
|
+
o === "class" ? r.className = s : o === "text" ? r.textContent = s : s != null && r.setAttribute(o, s);
|
|
627
|
+
return i.forEach((o) => r.appendChild(o)), r;
|
|
628
|
+
}
|
|
629
|
+
function Se(e) {
|
|
630
|
+
const {
|
|
631
|
+
hostUrl: a,
|
|
632
|
+
token: i,
|
|
633
|
+
position: r = "bottom-right",
|
|
634
|
+
open: o = !1,
|
|
635
|
+
launcher: s = !0,
|
|
636
|
+
title: u = "BRAVOPHONE",
|
|
637
|
+
// 'none' preserva 100% da UI do webphone: sem barra de titulo, o arraste
|
|
638
|
+
// vem de dentro do iframe. 'bar' adiciona a barra com titulo e controles.
|
|
639
|
+
frame: f = "none",
|
|
640
|
+
// O que arrastar até a borda de cima faz. 'max' é o gesto universal;
|
|
641
|
+
// troque para 'top-half' se preferir as metades verticais no topo.
|
|
642
|
+
dockTop: t = "max",
|
|
643
|
+
// Lado da viewport em que a aba de abertura fica colada.
|
|
644
|
+
launcherSide: g = "right",
|
|
645
|
+
// Desenho da aba: 'phone-waves' | 'waveform' | 'headset' | 'chat-phone'
|
|
646
|
+
launcherIcon: p = Q,
|
|
647
|
+
emit: d
|
|
648
|
+
} = e, h = new URL(a), w = h.origin, y = v("div", { "data-bravophone": "root" });
|
|
649
|
+
y.style.cssText = "all:initial;position:static";
|
|
650
|
+
const z = y.attachShadow({ mode: "open" });
|
|
651
|
+
z.appendChild(v("style", { text: we }));
|
|
652
|
+
const C = (() => {
|
|
653
|
+
var oe;
|
|
654
|
+
const { width: c, height: E, margin: I } = ce, N = window.innerWidth - c - I, ie = window.innerHeight - E - I, re = {
|
|
655
|
+
"bottom-right": { x: N, y: ie },
|
|
656
|
+
"bottom-left": { x: I, y: ie },
|
|
657
|
+
"top-right": { x: N, y: I },
|
|
658
|
+
"top-left": { x: I, y: I }
|
|
659
|
+
};
|
|
660
|
+
return R({ ...(oe = re[r]) != null ? oe : re["bottom-right"], width: c, height: E });
|
|
661
|
+
})(), D = R(ye(C)), $ = v("span", { class: "bp-status", "data-state": "connecting" }), O = v("button", { class: "bp-btn bp-btn-min", title: "Minimizar", text: "—" }), P = v("button", { class: "bp-btn bp-btn-close", title: "Fechar", text: "×" }), U = v("div", { class: "bp-header" }, [
|
|
662
|
+
$,
|
|
663
|
+
v("span", { class: "bp-title", text: u }),
|
|
664
|
+
O,
|
|
665
|
+
P
|
|
666
|
+
]), B = v("iframe", {
|
|
667
|
+
class: "bp-frame",
|
|
668
|
+
src: He(h, { token: i, embed: "1", parent: location.origin }),
|
|
669
|
+
allow: "microphone; autoplay; clipboard-write; speaker-selection",
|
|
670
|
+
title: "Webphone BRAVOPHONE"
|
|
671
|
+
}), G = ["n", "s", "w", "e", "nw", "ne", "sw", "se"].map((c) => ({
|
|
672
|
+
el: v("div", { class: `bp-h bp-h-${c}` }),
|
|
673
|
+
dir: c
|
|
674
|
+
})), n = v("div", { class: "bp-preview" });
|
|
675
|
+
n.hidden = !0;
|
|
676
|
+
const b = v("button", { class: "bp-btn bp-btn-close", title: "Fechar", text: "×" }), l = v("div", { class: "bp-overlay" }, [b]), S = v("div", { class: "bp-body" }, [B, l]), m = v("div", { class: "bp-root" }, [U, S, ...G.map((c) => c.el)]);
|
|
677
|
+
f !== "bar" && m.classList.add("bp-chromeless"), m.hidden = !o;
|
|
678
|
+
const k = v("span", { class: "bp-launcher-icon" });
|
|
679
|
+
k.innerHTML = j[p] || j[Q];
|
|
680
|
+
const H = v("span", { class: "bp-launcher-grip", "aria-hidden": "true" });
|
|
681
|
+
H.innerHTML = fe;
|
|
682
|
+
const L = v("div", {
|
|
683
|
+
class: "bp-launcher",
|
|
684
|
+
role: "button",
|
|
685
|
+
tabindex: "0",
|
|
686
|
+
title: "Abrir webphone",
|
|
687
|
+
"aria-label": "Abrir webphone"
|
|
688
|
+
}, [
|
|
689
|
+
H,
|
|
690
|
+
k,
|
|
691
|
+
v("span", { class: "bp-badge" })
|
|
692
|
+
]);
|
|
693
|
+
L.dataset.state = "connecting", L.hidden = !s || o, z.append(n, m, L), document.body.appendChild(y);
|
|
694
|
+
const ae = Le({
|
|
695
|
+
side: g,
|
|
696
|
+
y: Math.round(window.innerHeight / 2 - 24)
|
|
697
|
+
}), ne = Ee({
|
|
698
|
+
el: L,
|
|
699
|
+
side: ae.side,
|
|
700
|
+
y: Y(ae.y, 48),
|
|
701
|
+
onOpen: () => T.show()
|
|
702
|
+
}), W = ke({
|
|
703
|
+
root: m,
|
|
704
|
+
handle: U,
|
|
705
|
+
handles: G,
|
|
706
|
+
preview: n,
|
|
707
|
+
geometry: D,
|
|
708
|
+
limits: ce,
|
|
709
|
+
topDock: t,
|
|
710
|
+
onChange: (c) => d("resize", { width: c.width, height: c.height, dock: c.dock || null })
|
|
711
|
+
}), Z = Me({
|
|
712
|
+
frame: B,
|
|
713
|
+
origin: w,
|
|
714
|
+
onEvent: (c, E) => {
|
|
715
|
+
var I;
|
|
716
|
+
if (c === "ready" && ($.dataset.state = "ready", L.dataset.state = "ready", i && Z.call("auth", { token: i }).catch(() => {
|
|
717
|
+
})), c === "state") {
|
|
718
|
+
const N = (I = E == null ? void 0 : E.state) != null ? I : "ready";
|
|
719
|
+
$.dataset.state = N, L.dataset.state = N;
|
|
720
|
+
}
|
|
721
|
+
if (c === "call:incoming" && (T.show(), L.dataset.badge = "1", L.dataset.state = "ringing"), c === "call:answered" && (L.dataset.state = "incall"), (c === "call:ended" || c === "call:failed") && (delete L.dataset.badge, L.dataset.state = "ready"), c === "drag:start") {
|
|
722
|
+
W.external.start(E);
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (c === "drag:move") {
|
|
726
|
+
W.external.move(E);
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (c === "drag:end") {
|
|
730
|
+
W.external.end();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
d(c, E);
|
|
734
|
+
}
|
|
735
|
+
});
|
|
736
|
+
let _ = !1;
|
|
737
|
+
const T = {
|
|
738
|
+
el: y,
|
|
739
|
+
bridge: Z,
|
|
740
|
+
show() {
|
|
741
|
+
m.hidden = !1, L.hidden = !0, d("open");
|
|
742
|
+
},
|
|
743
|
+
hide() {
|
|
744
|
+
m.hidden = !0, s && (L.hidden = !1), d("close");
|
|
745
|
+
},
|
|
746
|
+
toggle() {
|
|
747
|
+
m.hidden ? T.show() : T.hide();
|
|
748
|
+
},
|
|
749
|
+
get isOpen() {
|
|
750
|
+
return !m.hidden;
|
|
751
|
+
},
|
|
752
|
+
minimize(c) {
|
|
753
|
+
if (f !== "bar") {
|
|
754
|
+
T.hide();
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
_ = c != null ? c : !_, m.classList.toggle("bp-minimized", _), O.textContent = _ ? "▢" : "—";
|
|
758
|
+
},
|
|
759
|
+
move(c, E) {
|
|
760
|
+
W.set({ x: c, y: E });
|
|
761
|
+
},
|
|
762
|
+
resize(c, E) {
|
|
763
|
+
W.set({ width: c, height: E });
|
|
764
|
+
},
|
|
765
|
+
dock(c) {
|
|
766
|
+
W.dock(c);
|
|
767
|
+
},
|
|
768
|
+
setLauncherSide(c) {
|
|
769
|
+
ne.setSide(c);
|
|
770
|
+
},
|
|
771
|
+
setLauncherIcon(c) {
|
|
772
|
+
k.innerHTML = j[c] || j[Q];
|
|
773
|
+
},
|
|
774
|
+
get geometry() {
|
|
775
|
+
return W.geometry;
|
|
776
|
+
},
|
|
777
|
+
destroy() {
|
|
778
|
+
Z.destroy(), W.destroy(), ne.destroy(), n.remove(), y.remove();
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
return P.addEventListener("click", () => T.hide()), O.addEventListener("click", () => T.minimize()), b.addEventListener("click", () => T.hide()), T;
|
|
782
|
+
}
|
|
783
|
+
function He(e, a) {
|
|
784
|
+
const i = new URL(e.href);
|
|
785
|
+
for (const [r, o] of Object.entries(a))
|
|
786
|
+
o != null && o !== "" && i.searchParams.set(r, o);
|
|
787
|
+
return a.token && (i.searchParams.delete("token"), i.hash = `token=${encodeURIComponent(a.token)}`), i.href;
|
|
788
|
+
}
|
|
789
|
+
const Te = "https://webphone.bravophone.com/embed/";
|
|
790
|
+
let M = null;
|
|
791
|
+
const A = /* @__PURE__ */ new Map();
|
|
792
|
+
function Ie(e, a) {
|
|
793
|
+
var i, r;
|
|
794
|
+
(i = A.get(e)) == null || i.forEach((o) => {
|
|
795
|
+
try {
|
|
796
|
+
o(a);
|
|
797
|
+
} catch (s) {
|
|
798
|
+
console.error("[Bravophone] listener falhou:", s);
|
|
799
|
+
}
|
|
800
|
+
}), (r = A.get("*")) == null || r.forEach((o) => {
|
|
801
|
+
try {
|
|
802
|
+
o({ event: e, payload: a });
|
|
803
|
+
} catch (s) {
|
|
804
|
+
console.error("[Bravophone] listener falhou:", s);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
function x() {
|
|
809
|
+
if (!M) throw new Error("Bravophone: chame Bravophone.init() antes.");
|
|
810
|
+
return M;
|
|
811
|
+
}
|
|
812
|
+
const le = {
|
|
813
|
+
/**
|
|
814
|
+
* Monta o webphone na página.
|
|
815
|
+
* @param {object} opts
|
|
816
|
+
* @param {string} [opts.hostUrl] Origem do webphone hospedado.
|
|
817
|
+
* @param {string} [opts.token] Token de sessão do usuário (SSO do integrador).
|
|
818
|
+
* @param {'bottom-right'|'bottom-left'|'top-right'|'top-left'} [opts.position]
|
|
819
|
+
* @param {boolean} [opts.open] Abrir já visível (padrão: false, só o launcher).
|
|
820
|
+
* @param {boolean} [opts.launcher] Exibir o botão flutuante (padrão: true).
|
|
821
|
+
*/
|
|
822
|
+
init(e = {}) {
|
|
823
|
+
if (M) return M;
|
|
824
|
+
if (typeof window == "undefined" || !document.body)
|
|
825
|
+
throw new Error("Bravophone: init() precisa rodar no browser, após o <body> existir.");
|
|
826
|
+
return location.protocol !== "https:" && location.hostname !== "localhost" && console.warn("[Bravophone] Contexto inseguro: o microfone exige HTTPS ou localhost."), M = Se({ hostUrl: Te, ...e, emit: Ie }), M;
|
|
827
|
+
},
|
|
828
|
+
// ---- Controle da janela ----
|
|
829
|
+
show() {
|
|
830
|
+
x().show();
|
|
831
|
+
},
|
|
832
|
+
hide() {
|
|
833
|
+
x().hide();
|
|
834
|
+
},
|
|
835
|
+
toggle() {
|
|
836
|
+
x().toggle();
|
|
837
|
+
},
|
|
838
|
+
minimize(e) {
|
|
839
|
+
x().minimize(e);
|
|
840
|
+
},
|
|
841
|
+
move(e, a) {
|
|
842
|
+
x().move(e, a);
|
|
843
|
+
},
|
|
844
|
+
resize(e, a) {
|
|
845
|
+
x().resize(e, a);
|
|
846
|
+
},
|
|
847
|
+
/** 'left' | 'right' | 'left-half' | 'right-half' | 'max' | 'float' */
|
|
848
|
+
dock(e) {
|
|
849
|
+
x().dock(e);
|
|
850
|
+
},
|
|
851
|
+
/** Move a aba de abertura para o outro lado da viewport. */
|
|
852
|
+
setLauncherSide(e) {
|
|
853
|
+
x().setLauncherSide(e);
|
|
854
|
+
},
|
|
855
|
+
/** 'phone-waves' | 'waveform' | 'headset' | 'chat-phone' */
|
|
856
|
+
setLauncherIcon(e) {
|
|
857
|
+
x().setLauncherIcon(e);
|
|
858
|
+
},
|
|
859
|
+
get isOpen() {
|
|
860
|
+
return M ? M.isOpen : !1;
|
|
861
|
+
},
|
|
862
|
+
get geometry() {
|
|
863
|
+
return M ? M.geometry : null;
|
|
864
|
+
},
|
|
865
|
+
// ---- Telefonia ----
|
|
866
|
+
/** Disca um número. Aceita formato livre; a normalização é a mesma da extensão. */
|
|
867
|
+
call(e, a) {
|
|
868
|
+
return x().bridge.call("call", { number: e, meta: a });
|
|
869
|
+
},
|
|
870
|
+
hangup() {
|
|
871
|
+
return x().bridge.call("hangup");
|
|
872
|
+
},
|
|
873
|
+
answer() {
|
|
874
|
+
return x().bridge.call("answer");
|
|
875
|
+
},
|
|
876
|
+
/** Alterna o mudo — o bundle só expõe toggle, não aceita estado alvo. */
|
|
877
|
+
mute() {
|
|
878
|
+
return x().bridge.call("mute");
|
|
879
|
+
},
|
|
880
|
+
/** Alterna a espera. Também toggle. */
|
|
881
|
+
hold() {
|
|
882
|
+
return x().bridge.call("hold");
|
|
883
|
+
},
|
|
884
|
+
sendDTMF(e) {
|
|
885
|
+
return x().bridge.call("dtmf", { tone: e });
|
|
886
|
+
},
|
|
887
|
+
transfer(e) {
|
|
888
|
+
return x().bridge.call("transfer", { to: e });
|
|
889
|
+
},
|
|
890
|
+
getStatus() {
|
|
891
|
+
return x().bridge.call("status");
|
|
892
|
+
},
|
|
893
|
+
setAuth(e) {
|
|
894
|
+
return x().bridge.call("auth", { token: e });
|
|
895
|
+
},
|
|
896
|
+
logout() {
|
|
897
|
+
return x().bridge.call("logout");
|
|
898
|
+
},
|
|
899
|
+
// ---- Eventos ----
|
|
900
|
+
/** Eventos: ready, state, call:dialing, call:incoming, call:answered,
|
|
901
|
+
* call:ended, call:failed, resize, open, close, error. */
|
|
902
|
+
on(e, a) {
|
|
903
|
+
return A.has(e) || A.set(e, /* @__PURE__ */ new Set()), A.get(e).add(a), () => le.off(e, a);
|
|
904
|
+
},
|
|
905
|
+
off(e, a) {
|
|
906
|
+
var i;
|
|
907
|
+
(i = A.get(e)) == null || i.delete(a);
|
|
908
|
+
},
|
|
909
|
+
destroy() {
|
|
910
|
+
M == null || M.destroy(), M = null, A.clear();
|
|
911
|
+
},
|
|
912
|
+
version: "0.1.0"
|
|
913
|
+
};
|
|
914
|
+
typeof window != "undefined" && (window.Bravophone = le);
|
|
915
|
+
export {
|
|
916
|
+
le as default
|
|
917
|
+
};
|