kasookoo-click-to-call-widget 0.1.0-beta.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 +549 -0
- package/dist/index.cjs +1859 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +253 -0
- package/dist/index.d.ts +253 -0
- package/dist/index.js +1818 -0
- package/dist/index.js.map +1 -0
- package/dist/kasookoo-dialer.js +118 -0
- package/dist/kasookoo-dialer.js.map +1 -0
- package/package.json +83 -0
package/README.md
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
# kasookoo-click-to-call-widget
|
|
2
|
+
|
|
3
|
+
A drop-in **click-to-call widget** for your website: a floating call button,
|
|
4
|
+
dial pad, and in-call window, delivered as a single custom HTML element —
|
|
5
|
+
`<kasookoo-dialer>`. Visitors can dial a real phone number and talk to you
|
|
6
|
+
over the browser, no app or plugin required.
|
|
7
|
+
|
|
8
|
+
## Why a custom element
|
|
9
|
+
|
|
10
|
+
`<kasookoo-dialer>` is a [web component](https://developer.mozilla.org/en-US/docs/Web/API/Web_components) —
|
|
11
|
+
a real HTML element the browser understands natively, the same way it
|
|
12
|
+
understands `<video>` or `<select>`. That means it works identically
|
|
13
|
+
everywhere:
|
|
14
|
+
|
|
15
|
+
- Drop it into a plain HTML page with one `<script>` tag, no build step.
|
|
16
|
+
- Drop it into a React, Angular, Vue, or Svelte app — every framework already
|
|
17
|
+
has its own normal way of placing a DOM element on the page and reading
|
|
18
|
+
values off it (a `ref`, a template binding, etc.). Nothing framework-specific
|
|
19
|
+
needs to be installed or maintained.
|
|
20
|
+
- Use as many of them as you want, anywhere in your app — one shared
|
|
21
|
+
connection backs every tag (see [Connecting once](#connecting-once) below).
|
|
22
|
+
|
|
23
|
+
Each tag is entirely self-contained: its own look, its own dial pad, its own
|
|
24
|
+
in-call window. It does not use or expose any of your page's CSS, and your
|
|
25
|
+
page's CSS cannot accidentally break it either.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
**Via npm** (bundler-based apps — React, Angular, Vue, Svelte, etc.):
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install kasookoo-click-to-call-widget
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
```js
|
|
36
|
+
import "kasookoo-click-to-call-widget" // registers <kasookoo-dialer>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Via a plain `<script>` tag** (no build step, no npm):
|
|
40
|
+
|
|
41
|
+
```html
|
|
42
|
+
<script type="module" src="https://unpkg.com/kasookoo-click-to-call-widget/dist/kasookoo-dialer.js"></script>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Connecting once
|
|
46
|
+
|
|
47
|
+
Call `KasookooClickToCall.init()` **once**, anywhere early in your app —
|
|
48
|
+
before or after your `<kasookoo-dialer>` tags render, it doesn't matter which
|
|
49
|
+
comes first. Every tag on the page automatically shares the one connection
|
|
50
|
+
this creates, so tags themselves don't take a publishable key or a caller
|
|
51
|
+
name:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
import { KasookooClickToCall } from "kasookoo-click-to-call-widget"
|
|
55
|
+
|
|
56
|
+
await KasookooClickToCall.init({
|
|
57
|
+
publishableKey: "pk_live_...",
|
|
58
|
+
subject: "Website visitor", // a display name for whoever is placing calls — see below
|
|
59
|
+
})
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- `publishableKey` — the key you were given for your website/app.
|
|
63
|
+
- `subject` — a display name for the caller. Any name is fine (e.g.
|
|
64
|
+
`"Website visitor"`, or a logged-in user's name) — it does not need to be
|
|
65
|
+
an email address or match an account.
|
|
66
|
+
|
|
67
|
+
Calling `init()` more than once is harmless — later calls just resolve to the
|
|
68
|
+
same connection the first call set up, so it's safe to call it from a
|
|
69
|
+
component that might mount more than once.
|
|
70
|
+
|
|
71
|
+
## Quick start
|
|
72
|
+
|
|
73
|
+
```html
|
|
74
|
+
<kasookoo-dialer></kasookoo-dialer>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
That's it — this renders a small floating call button. Clicking it opens a
|
|
78
|
+
popup with the full dial pad; when a visitor enters a number and presses
|
|
79
|
+
call, the widget places the call and shows a floating in-call card with
|
|
80
|
+
mute/hang-up controls, automatically.
|
|
81
|
+
|
|
82
|
+
## Static "call this number" buttons
|
|
83
|
+
|
|
84
|
+
Set `fixed-number` on a tag to turn it into a button for one specific number
|
|
85
|
+
instead of a dial pad. Instead of a dots icon, it shows the number itself as
|
|
86
|
+
its label, calls that number directly on click (behind a confirmation modal
|
|
87
|
+
by default), and shows a small call icon beside the number on hover:
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
<!-- Dynamic (default): visitor dials any number -->
|
|
91
|
+
<kasookoo-dialer></kasookoo-dialer>
|
|
92
|
+
|
|
93
|
+
<!-- Static: shows "+1 800 555 1234", confirms, then calls it -->
|
|
94
|
+
<kasookoo-dialer fixed-number="+1 800 555 1234"></kasookoo-dialer>
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Two more attributes only apply to a static tag — both default to on:
|
|
98
|
+
|
|
99
|
+
```html
|
|
100
|
+
<!-- No confirmation modal, no hover icon: one click, dials immediately -->
|
|
101
|
+
<kasookoo-dialer
|
|
102
|
+
fixed-number="+18005551234"
|
|
103
|
+
confirm-before-call="false"
|
|
104
|
+
show-call-icon-on-hover="false"
|
|
105
|
+
></kasookoo-dialer>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The number is dialed exactly as you write it in `fixed-number` (spaces and
|
|
109
|
+
formatting are stripped automatically before it's actually dialed) — so
|
|
110
|
+
format it however reads best to a visitor.
|
|
111
|
+
|
|
112
|
+
## One call at a time
|
|
113
|
+
|
|
114
|
+
Every `<kasookoo-dialer>` tag on the page — dynamic or static — shares the
|
|
115
|
+
same active-call state. If a visitor is already on a call and tries to start
|
|
116
|
+
another one from any tag, the widget doesn't place a second call: it fires a
|
|
117
|
+
`call-error` event with `code: "call_in_progress"` on the tag they just
|
|
118
|
+
clicked, and every other tag's button disables itself for the duration of
|
|
119
|
+
the active call.
|
|
120
|
+
|
|
121
|
+
## Placing a call programmatically
|
|
122
|
+
|
|
123
|
+
A static tag covers the common case — a button that always calls the same
|
|
124
|
+
number, no JavaScript required. For a number that isn't known until runtime
|
|
125
|
+
(looked up from an API, chosen per click, etc.), call the element's `dial`
|
|
126
|
+
method directly instead:
|
|
127
|
+
|
|
128
|
+
```html
|
|
129
|
+
<kasookoo-dialer id="ctc"></kasookoo-dialer>
|
|
130
|
+
<button id="call-support">Call support for my region</button>
|
|
131
|
+
|
|
132
|
+
<script type="module">
|
|
133
|
+
document.getElementById("call-support").addEventListener("click", async () => {
|
|
134
|
+
const number = await resolveSupportNumberForVisitor() // your own logic
|
|
135
|
+
document.getElementById("ctc").dial(number)
|
|
136
|
+
})
|
|
137
|
+
</script>
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
On a tag that has `fixed-number` set, `dial()` ignores whatever you pass it
|
|
141
|
+
and always calls that configured number instead — a static tag's whole point
|
|
142
|
+
is to only ever call the one number it's configured for.
|
|
143
|
+
|
|
144
|
+
The in-call window renders the same way regardless of how the call started —
|
|
145
|
+
`dial()`, a static button, and a number typed into the dial pad all go
|
|
146
|
+
through the same code path.
|
|
147
|
+
|
|
148
|
+
## The widget instance
|
|
149
|
+
|
|
150
|
+
`init()` resolves to a `KasookooClickToCall` instance. It does **not** offer
|
|
151
|
+
any way to place a call from code — a real user clicking a rendered
|
|
152
|
+
`<kasookoo-dialer>` tag is the only way a call gets placed. This is
|
|
153
|
+
deliberate: if placing a call were just a normal method call, any script on
|
|
154
|
+
the page (a compromised dependency, an injected ad script) could silently
|
|
155
|
+
dial numbers with no user interaction and no confirmation. What the instance
|
|
156
|
+
does offer is read-only inspection and teardown:
|
|
157
|
+
|
|
158
|
+
```js
|
|
159
|
+
const widget = await KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" })
|
|
160
|
+
|
|
161
|
+
widget.session // SessionInfo | null — sessionId, subject, organizationId, expiresAt
|
|
162
|
+
widget.activeCall // Call | null — the call currently connecting/connected, if any (see below)
|
|
163
|
+
widget.close() // tears the widget down: ends any active call, removes every mounted
|
|
164
|
+
// <kasookoo-dialer> trigger button, clears the session. Safe to call more
|
|
165
|
+
// than once; the instance can't be reused afterward — a later init() call
|
|
166
|
+
// creates a fresh one.
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### The `Call` object
|
|
170
|
+
|
|
171
|
+
`activeCall` returns a `Call` — the same object that drives every
|
|
172
|
+
`<kasookoo-dialer>`'s built-in call window internally:
|
|
173
|
+
|
|
174
|
+
| Member | Type | Description |
|
|
175
|
+
|---|---|---|
|
|
176
|
+
| `roomName` | `string` | Call/room identifier. |
|
|
177
|
+
| `callTraceId` | `string` | Stable id for this call's lifecycle — useful for correlating logs/support requests. |
|
|
178
|
+
| `remote` | `{ name: string, type?: string, id?: string }` | The other side of the call — `name` is the dialed phone number. |
|
|
179
|
+
| `state` | `CallState` | `"connecting" \| "connected" \| "ended"`. |
|
|
180
|
+
| `muted` | `boolean` | Whether the local microphone is muted. |
|
|
181
|
+
| `setMuted(muted)` | `(muted: boolean) => Promise<void>` | Mutes/unmutes the local microphone. |
|
|
182
|
+
| `end()` | `() => Promise<void>` | Hangs up, whether or not the phone number has answered yet. |
|
|
183
|
+
| `on("state", cb)` | `(state: CallState) => void` | Fires on every state transition. |
|
|
184
|
+
| `on("error", cb)` | `(err: KasookooError) => void` | Fires if something goes wrong on this specific call. |
|
|
185
|
+
|
|
186
|
+
## Configuration
|
|
187
|
+
|
|
188
|
+
Two kinds of settings:
|
|
189
|
+
|
|
190
|
+
- **Simple values** go on the element as plain HTML attributes (kebab-case,
|
|
191
|
+
since HTML attributes can only hold text):
|
|
192
|
+
- `fixed-number` — optional. See [Static "call this number" buttons](#static-call-this-number-buttons).
|
|
193
|
+
- `confirm-before-call` — static tags only, defaults to on. Set to `"false"` to skip the confirmation modal.
|
|
194
|
+
- `show-call-icon-on-hover` — static tags only, defaults to on. Set to `"false"` to hide the hover icon.
|
|
195
|
+
|
|
196
|
+
- **Everything else** (an object) goes on the element's `config` **property**
|
|
197
|
+
in JavaScript, since HTML attributes can't hold objects:
|
|
198
|
+
|
|
199
|
+
```js
|
|
200
|
+
const dialer = document.querySelector("kasookoo-dialer")
|
|
201
|
+
dialer.config = {
|
|
202
|
+
participantName: "Jane (Sales Inquiry)", // shown as the caller's name on the call, defaults to the `subject` given to init()
|
|
203
|
+
defaultCountryCode: "+1", // prefilled in the dial pad's number field (dynamic tags only)
|
|
204
|
+
placeholder: "Enter phone number", // dial pad input placeholder (dynamic tags only)
|
|
205
|
+
customCss: ".kasookoo-ctc-trigger--static { background: #7c3aed; }", // see Styling below
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## Styling
|
|
210
|
+
|
|
211
|
+
Set `config.customCss` to a raw CSS string to restyle a tag — it's injected
|
|
212
|
+
into that tag's own shadow root, so it only ever affects that one tag's
|
|
213
|
+
trigger button, dial pad, and (static tags) confirmation modal. It does not
|
|
214
|
+
reach the floating in-call window, since that's shared, page-level UI
|
|
215
|
+
independent of any single tag.
|
|
216
|
+
|
|
217
|
+
If you use Tailwind or another CSS build step, compile it and pass the
|
|
218
|
+
resulting CSS text here — this widget's shadow root isn't part of your page's
|
|
219
|
+
normal build output, so utility classes from your app's stylesheet can't
|
|
220
|
+
reach in on their own.
|
|
221
|
+
|
|
222
|
+
```js
|
|
223
|
+
dialer.config = {
|
|
224
|
+
customCss: `
|
|
225
|
+
.kasookoo-ctc-trigger--static { background: #7c3aed; border-radius: 8px; }
|
|
226
|
+
.kasookoo-ctc-trigger--static:hover { background: #6d28d9; }
|
|
227
|
+
`,
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Listening for call activity
|
|
232
|
+
|
|
233
|
+
The element reports what's happening through standard browser
|
|
234
|
+
[`CustomEvent`s](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) —
|
|
235
|
+
listen with `addEventListener`, same as any other DOM event:
|
|
236
|
+
|
|
237
|
+
```js
|
|
238
|
+
const dialer = document.querySelector("kasookoo-dialer")
|
|
239
|
+
|
|
240
|
+
dialer.addEventListener("call-state", (e) => {
|
|
241
|
+
// e.detail = { state: "connecting" | "connected" | "ended", phoneNumber: string }
|
|
242
|
+
console.log("call is now", e.detail.state)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
dialer.addEventListener("call-error", (e) => {
|
|
246
|
+
// e.detail = { code: string, message: string }
|
|
247
|
+
console.warn("something went wrong:", e.detail.message)
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
dialer.addEventListener("session-expired", () => {
|
|
251
|
+
console.warn("the widget's session expired and needs a page refresh")
|
|
252
|
+
})
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
`call-state` fires for every step of a call's life: `"connecting"` while it's
|
|
256
|
+
dialing, `"connected"` once answered, `"ended"` when it's over. Every call
|
|
257
|
+
placed by this widget is outbound — there is no "incoming call" state.
|
|
258
|
+
|
|
259
|
+
## Framework usage
|
|
260
|
+
|
|
261
|
+
### Plain HTML / vanilla JS
|
|
262
|
+
|
|
263
|
+
```html
|
|
264
|
+
<script type="module" src="https://unpkg.com/kasookoo-click-to-call-widget/dist/kasookoo-dialer.js"></script>
|
|
265
|
+
|
|
266
|
+
<!-- Dynamic: dots icon, opens the dial pad -->
|
|
267
|
+
<kasookoo-dialer id="ctc"></kasookoo-dialer>
|
|
268
|
+
|
|
269
|
+
<!-- Static: shows the number, calls it on click -->
|
|
270
|
+
<kasookoo-dialer fixed-number="+18005551234"></kasookoo-dialer>
|
|
271
|
+
|
|
272
|
+
<script type="module">
|
|
273
|
+
import { KasookooClickToCall } from "https://unpkg.com/kasookoo-click-to-call-widget/dist/kasookoo-dialer.js"
|
|
274
|
+
|
|
275
|
+
await KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" })
|
|
276
|
+
|
|
277
|
+
const dialer = document.getElementById("ctc")
|
|
278
|
+
dialer.config = { defaultCountryCode: "+1" }
|
|
279
|
+
dialer.addEventListener("call-state", (e) => console.log("call state:", e.detail.state))
|
|
280
|
+
</script>
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### React
|
|
284
|
+
|
|
285
|
+
```tsx
|
|
286
|
+
import { useEffect, useState } from "react"
|
|
287
|
+
import { KasookooClickToCall } from "kasookoo-click-to-call-widget" // registers <kasookoo-dialer> once
|
|
288
|
+
|
|
289
|
+
// Call init() once, high in your app (e.g. a top-level effect) — every
|
|
290
|
+
// <kasookoo-dialer> tag rendered anywhere afterward shares that connection.
|
|
291
|
+
function App() {
|
|
292
|
+
const [ready, setReady] = useState(false)
|
|
293
|
+
|
|
294
|
+
useEffect(() => {
|
|
295
|
+
KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" }).then(() =>
|
|
296
|
+
setReady(true)
|
|
297
|
+
)
|
|
298
|
+
}, [])
|
|
299
|
+
|
|
300
|
+
if (!ready) return null
|
|
301
|
+
return (
|
|
302
|
+
<>
|
|
303
|
+
{/* @ts-expect-error -- custom element, not a typed JSX component */}
|
|
304
|
+
<kasookoo-dialer />
|
|
305
|
+
{/* Static button needs no ref or click handler — the tag is the whole button: */}
|
|
306
|
+
{/* @ts-expect-error -- custom element, not a typed JSX component */}
|
|
307
|
+
<kasookoo-dialer fixed-number="+18005551234" />
|
|
308
|
+
</>
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Vue
|
|
314
|
+
|
|
315
|
+
```vue
|
|
316
|
+
<template>
|
|
317
|
+
<!-- Dynamic -->
|
|
318
|
+
<kasookoo-dialer ref="dialerEl" @call-state="onCallState" />
|
|
319
|
+
<!-- Static: shows the number, calls it on click -->
|
|
320
|
+
<kasookoo-dialer fixed-number="+18005551234" />
|
|
321
|
+
</template>
|
|
322
|
+
|
|
323
|
+
<script setup>
|
|
324
|
+
import { ref, onMounted } from "vue"
|
|
325
|
+
import { KasookooClickToCall } from "kasookoo-click-to-call-widget"
|
|
326
|
+
|
|
327
|
+
const dialerEl = ref(null)
|
|
328
|
+
const onCallState = (e) => console.log("call state:", e.detail.state)
|
|
329
|
+
|
|
330
|
+
onMounted(async () => {
|
|
331
|
+
await KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" })
|
|
332
|
+
dialerEl.value.config = { defaultCountryCode: "+1" }
|
|
333
|
+
})
|
|
334
|
+
</script>
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Vue treats unrecognized tags with a dash in the name as custom elements
|
|
338
|
+
automatically — no extra configuration needed.
|
|
339
|
+
|
|
340
|
+
### Angular
|
|
341
|
+
|
|
342
|
+
```html
|
|
343
|
+
<!-- Dynamic -->
|
|
344
|
+
<kasookoo-dialer #ctc (call-state)="onCallState($event)"></kasookoo-dialer>
|
|
345
|
+
<!-- Static: shows the number, calls it on click -->
|
|
346
|
+
<kasookoo-dialer fixed-number="+18005551234"></kasookoo-dialer>
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
```ts
|
|
350
|
+
// In the module bootstrapping this component:
|
|
351
|
+
// schemas: [CUSTOM_ELEMENTS_SCHEMA]
|
|
352
|
+
import { KasookooClickToCall } from "kasookoo-click-to-call-widget"
|
|
353
|
+
|
|
354
|
+
@Component({ /* ... */ })
|
|
355
|
+
export class ClickToCallComponent implements OnInit {
|
|
356
|
+
async ngOnInit() {
|
|
357
|
+
await KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" })
|
|
358
|
+
}
|
|
359
|
+
onCallState(e: CustomEvent) {
|
|
360
|
+
console.log("call state:", e.detail.state)
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### Svelte
|
|
366
|
+
|
|
367
|
+
```svelte
|
|
368
|
+
<script>
|
|
369
|
+
import { onMount } from "svelte"
|
|
370
|
+
import { KasookooClickToCall } from "kasookoo-click-to-call-widget"
|
|
371
|
+
|
|
372
|
+
onMount(() => {
|
|
373
|
+
KasookooClickToCall.init({ publishableKey: "pk_live_...", subject: "Website visitor" })
|
|
374
|
+
})
|
|
375
|
+
</script>
|
|
376
|
+
|
|
377
|
+
<!-- Dynamic -->
|
|
378
|
+
<kasookoo-dialer on:call-state={(e) => console.log("call state:", e.detail.state)} />
|
|
379
|
+
<!-- Static: shows the number, calls it on click -->
|
|
380
|
+
<kasookoo-dialer fixed-number="+18005551234" />
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
The pattern is the same everywhere: `KasookooClickToCall.init()` is called
|
|
384
|
+
once per app, plain text values are HTML attributes, anything more complex
|
|
385
|
+
is a JS property set on the element (via `ref`, `:prop`, `[prop]`, or plain
|
|
386
|
+
binding, depending on your framework), and anything the widget needs to tell
|
|
387
|
+
you comes back as a `CustomEvent`.
|
|
388
|
+
|
|
389
|
+
## Requirements
|
|
390
|
+
|
|
391
|
+
- A modern browser (Chrome, Edge, Firefox, Safari — recent versions).
|
|
392
|
+
- The page must be served over **HTTPS** (or `localhost` during development)
|
|
393
|
+
— browsers only allow microphone access on secure origins.
|
|
394
|
+
- The visitor will be prompted to allow microphone access the first time
|
|
395
|
+
`init()` runs; a call cannot be placed without it.
|
|
396
|
+
|
|
397
|
+
## What this widget does not do
|
|
398
|
+
|
|
399
|
+
- It does not receive incoming calls — it only places outbound calls to
|
|
400
|
+
phone numbers.
|
|
401
|
+
- It does not offer a way to swap in your own dial pad or call window design
|
|
402
|
+
— both are fixed, built-in UI (though a static tag's trigger button and
|
|
403
|
+
dial pad can be restyled — see [Styling](#styling)).
|
|
404
|
+
- It does not support call hold, DTMF tones (in-call keypad input), or
|
|
405
|
+
choosing a specific microphone device.
|
|
406
|
+
|
|
407
|
+
## API Reference
|
|
408
|
+
|
|
409
|
+
### `KasookooClickToCall.init(config)`
|
|
410
|
+
|
|
411
|
+
Called once per app, before or after your `<kasookoo-dialer>` tags render.
|
|
412
|
+
|
|
413
|
+
| Option | Type | Default | Description |
|
|
414
|
+
|---|---|---|---|
|
|
415
|
+
| `publishableKey` | `string` | *required* | The key you were given for your website/app. |
|
|
416
|
+
| `subject` | `string` | *required* | Display name for whoever is placing calls. Any name works — not required to be an email or match an account. |
|
|
417
|
+
| `callRingTimeoutMs` | `number \| false` | `60000` | How long an unanswered call rings before it's cancelled automatically. Pass `false` to disable — the call then rings until answered or manually ended. |
|
|
418
|
+
| `logging` | `KasookooLoggingConfig` | `-` | Structured JSON console logging. See below. |
|
|
419
|
+
| `telemetry` | `KasookooTelemetryConfig` | `-` | **Optional** OpenTelemetry trace/log export — off unless you turn it on. See below. |
|
|
420
|
+
|
|
421
|
+
`logging` options:
|
|
422
|
+
|
|
423
|
+
| Option | Type | Default | Description |
|
|
424
|
+
|---|---|---|---|
|
|
425
|
+
| `enabled` | `boolean` | `true` | Set to `false` to silence all console logging. |
|
|
426
|
+
| `level` | `"debug" \| "info" \| "warn" \| "error"` | `"info"` | Minimum level emitted. |
|
|
427
|
+
| `service` | `string` | `"kasookoo-click-to-call-widget"` | Logical service name attached to each log line. |
|
|
428
|
+
| `host` | `string` | `window.location.hostname` | Host label attached to each log line. |
|
|
429
|
+
|
|
430
|
+
Log lines are structured JSON emitted via `console.debug`/`info`/`warn`/`error`. Sensitive fields (tokens, passwords, secrets, etc.) are automatically redacted before logging.
|
|
431
|
+
|
|
432
|
+
```js
|
|
433
|
+
await KasookooClickToCall.init({
|
|
434
|
+
publishableKey: "pk_live_...",
|
|
435
|
+
subject: "Website visitor",
|
|
436
|
+
logging: { enabled: false }, // or e.g. { level: "warn" }
|
|
437
|
+
})
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
`telemetry` — **entirely optional, off by default.** The widget works
|
|
441
|
+
exactly the same with or without it; nothing here affects calling. Turning
|
|
442
|
+
it on sends OpenTelemetry traces (every `fetch()` the widget makes, tagged
|
|
443
|
+
with the same correlation ids as `logging`) and, optionally, the same
|
|
444
|
+
structured logs, to a collector you control.
|
|
445
|
+
|
|
446
|
+
| Option | Type | Default | Description |
|
|
447
|
+
|---|---|---|---|
|
|
448
|
+
| `enabled` | `boolean` | `false` | Turns telemetry on. Everything below only matters if this is `true`. |
|
|
449
|
+
| `endpoint` | `string` | `"https://monitoring-test.kasookoo.ai/v1/traces"` | OTLP/HTTP traces endpoint. Defaults to a shared test/demo collector — good enough to `{ enabled: true }` and immediately see traces, but point this at your own collector for anything real. |
|
|
450
|
+
| `logsEndpoint` | `string` | derived from `endpoint` | OTLP/HTTP logs endpoint. Defaults to `endpoint` with a trailing `/v1/traces` swapped for `/v1/logs`; set explicitly if your collector doesn't follow that convention. |
|
|
451
|
+
| `serviceName` | `string` | `"kasookoo-click-to-call-widget"` | OTel `service.name`. |
|
|
452
|
+
| `environment` | `string` | `"production"` | OTel `deployment.environment`. |
|
|
453
|
+
|
|
454
|
+
Two things make this safe to leave off, and safe to turn on without it ever
|
|
455
|
+
breaking the widget:
|
|
456
|
+
|
|
457
|
+
- **It requires extra packages you install yourself.** Traces need
|
|
458
|
+
`@opentelemetry/sdk-trace-web`, `@opentelemetry/sdk-trace-base`,
|
|
459
|
+
`@opentelemetry/exporter-trace-otlp-http`, `@opentelemetry/instrumentation`,
|
|
460
|
+
`@opentelemetry/instrumentation-fetch`, `@opentelemetry/resources`,
|
|
461
|
+
`@opentelemetry/semantic-conventions`, and `@opentelemetry/api`. Log export
|
|
462
|
+
additionally needs `@opentelemetry/sdk-logs` and
|
|
463
|
+
`@opentelemetry/exporter-logs-otlp-http`. They're declared as optional
|
|
464
|
+
`peerDependencies` — npm won't install them for you, and won't complain if
|
|
465
|
+
you don't:
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-web @opentelemetry/sdk-trace-base \
|
|
469
|
+
@opentelemetry/exporter-trace-otlp-http @opentelemetry/instrumentation \
|
|
470
|
+
@opentelemetry/instrumentation-fetch @opentelemetry/resources \
|
|
471
|
+
@opentelemetry/semantic-conventions @opentelemetry/sdk-logs \
|
|
472
|
+
@opentelemetry/exporter-logs-otlp-http
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
If `telemetry.enabled` is `true` but these aren't installed, the widget
|
|
476
|
+
logs a console warning and continues working with console logging only —
|
|
477
|
+
it never throws or blocks a call.
|
|
478
|
+
|
|
479
|
+
- **It's bundler-only** — available when you `npm install` the widget, not
|
|
480
|
+
via the plain `<script type="module">`/CDN build. A script tag has no way
|
|
481
|
+
to resolve npm package names, so `dist/kasookoo-dialer.js` doesn't include
|
|
482
|
+
telemetry at all, on or off.
|
|
483
|
+
|
|
484
|
+
```js
|
|
485
|
+
// Demo — traces go to the shared test collector, no setup required:
|
|
486
|
+
await KasookooClickToCall.init({
|
|
487
|
+
publishableKey: "pk_live_...",
|
|
488
|
+
subject: "Website visitor",
|
|
489
|
+
telemetry: { enabled: true },
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
// Production — point it at your own collector:
|
|
493
|
+
await KasookooClickToCall.init({
|
|
494
|
+
publishableKey: "pk_live_...",
|
|
495
|
+
subject: "Website visitor",
|
|
496
|
+
telemetry: {
|
|
497
|
+
enabled: true,
|
|
498
|
+
endpoint: "https://your-collector.example.com/v1/traces",
|
|
499
|
+
},
|
|
500
|
+
})
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### `KasookooClickToCall` instance
|
|
504
|
+
|
|
505
|
+
The object `init()` resolves to. See [The widget instance](#the-widget-instance). There is deliberately no way to place a call from this object — only a real user clicking a rendered `<kasookoo-dialer>` tag can do that.
|
|
506
|
+
|
|
507
|
+
| Member | Type | Description |
|
|
508
|
+
|---|---|---|
|
|
509
|
+
| `session` | `SessionInfo \| null` | Current session info — `sessionId`, `subject`, `organizationId`, `expiresAt`. |
|
|
510
|
+
| `activeCall` | `Call \| null` | The call currently connecting/connected, if any. |
|
|
511
|
+
| `close()` | `() => void` | Tears the widget down: ends any active call, removes every mounted `<kasookoo-dialer>` trigger button, clears the session. Safe to call more than once. |
|
|
512
|
+
|
|
513
|
+
### `<kasookoo-dialer>` attributes
|
|
514
|
+
|
|
515
|
+
Plain HTML attributes — values are always strings.
|
|
516
|
+
|
|
517
|
+
| Attribute | Type | Default | Description |
|
|
518
|
+
|---|---|---|---|
|
|
519
|
+
| `fixed-number` | `string` | `-` | Presence turns this tag into a static button showing and calling this one number instead of a dial pad. |
|
|
520
|
+
| `confirm-before-call` | `"false"` to disable | on | Static tags only. Shows a confirmation modal before dialing `fixed-number`. |
|
|
521
|
+
| `show-call-icon-on-hover` | `"false"` to disable | on | Static tags only. Shows a call icon beside the number on hover. |
|
|
522
|
+
|
|
523
|
+
### `config` property
|
|
524
|
+
|
|
525
|
+
Set as a JS property (`element.config = {...}`), not an attribute — this is
|
|
526
|
+
where anything beyond a plain string goes.
|
|
527
|
+
|
|
528
|
+
| Prop | Type | Default | Description |
|
|
529
|
+
|---|---|---|---|
|
|
530
|
+
| `participantName` | `string` | value of `subject` given to `init()` | Caller name shown on the call itself. |
|
|
531
|
+
| `defaultCountryCode` | `string` | `-` | Dynamic tags only. Prefilled dialing prefix in the dial pad's number field, e.g. `"+1"`. Purely a starting value — the visitor can edit or clear it. |
|
|
532
|
+
| `placeholder` | `string` | `"Enter phone number"` | Dynamic tags only. Placeholder text for the dial pad's number input. |
|
|
533
|
+
| `customCss` | `string` | `-` | Raw CSS injected into this tag's own shadow root. See [Styling](#styling). |
|
|
534
|
+
|
|
535
|
+
### Events
|
|
536
|
+
|
|
537
|
+
Standard `CustomEvent`s — listen with `element.addEventListener(name, handler)`.
|
|
538
|
+
|
|
539
|
+
| Event | Detail (`event.detail`) | Description |
|
|
540
|
+
|---|---|---|
|
|
541
|
+
| `call-state` | `{ state: "connecting" \| "connected" \| "ended", phoneNumber: string }` | Fires on every step of a call's life, from dialing to hang-up. |
|
|
542
|
+
| `call-error` | `{ code: string, message: string }` | Fires when something goes wrong — a failed call, a denied microphone permission, a call already in progress (`code: "call_in_progress"`), etc. If `code` is `"missing_scope"`, your publishable key isn't provisioned for calling — contact your key provider/Kasookoo. |
|
|
543
|
+
| `session-expired` | `{ reason: string }` | Fires if the widget's session expires mid-use; reload the page to recover. |
|
|
544
|
+
|
|
545
|
+
### Methods
|
|
546
|
+
|
|
547
|
+
| Method | Signature | Description |
|
|
548
|
+
|---|---|---|
|
|
549
|
+
| `dial` | `(phoneNumber?: string) => void` | Places a call directly, bypassing the trigger button and dial pad. On a tag with `fixed-number` set, the argument is ignored and that number is always dialed instead. |
|