@mobil80-dev/chatbot-widget 2.0.1 → 2.0.2
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 +31 -9
- package/index.js +69 -84
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A lightweight JavaScript chatbot widget powered by VaultChat, designed to be emb
|
|
|
11
11
|
- No iframe
|
|
12
12
|
- No dependencies
|
|
13
13
|
- Easy setup
|
|
14
|
+
- Optional page/container restriction via `attachToElement`
|
|
14
15
|
|
|
15
16
|
## 📦 Installation
|
|
16
17
|
|
|
@@ -112,20 +113,41 @@ VaultChat.init({
|
|
|
112
113
|
|
|
113
114
|
```
|
|
114
115
|
|
|
116
|
+
## 📌 attachToElement (Optional)
|
|
117
|
+
|
|
118
|
+
VaultChat allows you to specify a container where the chat widget should be rendered. By default, the chat button and card are appended to document.body.
|
|
119
|
+
|
|
120
|
+
You can restrict the widget to a specific element by passing a CSS selector or a DOM element using the attachToElement option:
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
// Using a CSS selector
|
|
124
|
+
VaultChat.init({
|
|
125
|
+
apiKey: 'YOUR_API_KEY',
|
|
126
|
+
attachToElement: '#dashboard-container'
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
// Using a DOM element
|
|
130
|
+
const container = document.getElementById('dashboard-container')
|
|
131
|
+
VaultChat.init({
|
|
132
|
+
apiKey: 'YOUR_API_KEY',
|
|
133
|
+
attachToElement: container
|
|
134
|
+
})
|
|
135
|
+
```
|
|
136
|
+
|
|
115
137
|
📌 Call this inside ngOnInit() of your root or layout component.
|
|
116
138
|
|
|
117
139
|
## ⚙️ Configuration Reference
|
|
118
140
|
|
|
119
141
|
```
|
|
120
|
-
| Option
|
|
121
|
-
|
|
|
122
|
-
| apiKey
|
|
123
|
-
| primaryColor
|
|
124
|
-
| theme
|
|
125
|
-
| buttonContent
|
|
126
|
-
| buttonType
|
|
127
|
-
| buttonShape
|
|
128
|
-
|
|
142
|
+
| Option | Type | Description |
|
|
143
|
+
| --------------- | ---------- | ----------------------------------------------- |
|
|
144
|
+
| apiKey | string | Your VaultChat API key |
|
|
145
|
+
| primaryColor | string | Primary UI color |
|
|
146
|
+
| theme | string | `light` or `dark` |
|
|
147
|
+
| buttonContent | string | Text, image URL, or emoji |
|
|
148
|
+
| buttonType | string | `text` or `image` |
|
|
149
|
+
| buttonShape | string | `circle`, `square`, or `pill` |
|
|
150
|
+
| attachToElement | string/DOM | CSS selector or DOM element to mount the widget |
|
|
129
151
|
|
|
130
152
|
```
|
|
131
153
|
|
package/index.js
CHANGED
|
@@ -10,7 +10,7 @@ let isOpen = false
|
|
|
10
10
|
/* =========================================================
|
|
11
11
|
STYLE INJECTION (ONCE)
|
|
12
12
|
========================================================= */
|
|
13
|
-
function injectStylesOnce
|
|
13
|
+
function injectStylesOnce() {
|
|
14
14
|
if (stylesInjected) return
|
|
15
15
|
stylesInjected = true
|
|
16
16
|
|
|
@@ -23,7 +23,6 @@ function injectStylesOnce () {
|
|
|
23
23
|
.vc-fab:hover {
|
|
24
24
|
box-shadow: 0 12px 28px rgba(0,0,0,.3);
|
|
25
25
|
}
|
|
26
|
-
|
|
27
26
|
.vc-loader {
|
|
28
27
|
display: inline-flex;
|
|
29
28
|
gap: 6px;
|
|
@@ -41,7 +40,6 @@ function injectStylesOnce () {
|
|
|
41
40
|
}
|
|
42
41
|
.vc-loader span:nth-child(1) { animation-delay: -0.32s; }
|
|
43
42
|
.vc-loader span:nth-child(2) { animation-delay: -0.16s; }
|
|
44
|
-
|
|
45
43
|
@keyframes vc-bounce {
|
|
46
44
|
0%, 80%, 100% { transform: scale(0); }
|
|
47
45
|
40% { transform: scale(1); }
|
|
@@ -50,30 +48,33 @@ function injectStylesOnce () {
|
|
|
50
48
|
document.head.appendChild(style)
|
|
51
49
|
}
|
|
52
50
|
|
|
51
|
+
/* =========================================================
|
|
52
|
+
HELPER TO RESOLVE CONTAINER
|
|
53
|
+
========================================================= */
|
|
54
|
+
function getAttachContainer(selector) {
|
|
55
|
+
if (!selector) return document.body
|
|
56
|
+
if (typeof selector === 'string') {
|
|
57
|
+
const el = document.querySelector(selector)
|
|
58
|
+
return el || document.body
|
|
59
|
+
}
|
|
60
|
+
return selector // accept DOM element directly
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
/* =========================================================
|
|
54
64
|
BUTTON
|
|
55
65
|
========================================================= */
|
|
56
|
-
function getButton
|
|
66
|
+
function getButton() {
|
|
57
67
|
if (vcButton) return vcButton
|
|
58
|
-
|
|
59
68
|
vcButton = document.createElement('div')
|
|
60
69
|
vcButton.className = 'vc-fab'
|
|
61
|
-
document.body.appendChild(vcButton)
|
|
62
|
-
|
|
63
70
|
return vcButton
|
|
64
71
|
}
|
|
65
72
|
|
|
66
|
-
function updateButton
|
|
73
|
+
function updateButton(config) {
|
|
67
74
|
const button = getButton()
|
|
68
75
|
button.innerHTML = ''
|
|
69
76
|
|
|
70
|
-
const {
|
|
71
|
-
buttonType = 'text',
|
|
72
|
-
buttonContent = 'Chat',
|
|
73
|
-
buttonShape = 'circle',
|
|
74
|
-
primaryColor = '#2563eb'
|
|
75
|
-
} = config
|
|
76
|
-
|
|
77
|
+
const { buttonType = 'text', buttonContent = 'Chat', buttonShape = 'circle', primaryColor = '#2563eb' } = config
|
|
77
78
|
const isText = buttonType === 'text'
|
|
78
79
|
|
|
79
80
|
if (buttonType === 'image') {
|
|
@@ -87,8 +88,7 @@ function updateButton (config) {
|
|
|
87
88
|
button.textContent = buttonContent
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
const borderRadius =
|
|
91
|
-
buttonShape === 'pill' ? '999px' : buttonShape === 'square' ? '8px' : '50%'
|
|
91
|
+
const borderRadius = buttonShape === 'pill' ? '999px' : buttonShape === 'square' ? '8px' : '50%'
|
|
92
92
|
|
|
93
93
|
Object.assign(button.style, {
|
|
94
94
|
position: 'fixed',
|
|
@@ -116,51 +116,22 @@ function updateButton (config) {
|
|
|
116
116
|
/* =========================================================
|
|
117
117
|
CARD
|
|
118
118
|
========================================================= */
|
|
119
|
-
function getCard
|
|
119
|
+
function getCard() {
|
|
120
120
|
if (vcCard) return vcCard
|
|
121
|
-
|
|
122
121
|
vcCard = document.createElement('div')
|
|
123
|
-
document.body.appendChild(vcCard)
|
|
124
122
|
return vcCard
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
function updateCard
|
|
125
|
+
function updateCard(config) {
|
|
128
126
|
const card = getCard()
|
|
129
127
|
|
|
130
|
-
const theme =
|
|
131
|
-
|
|
132
|
-
? 'dark'
|
|
133
|
-
: config.theme === 'light'
|
|
134
|
-
? 'light'
|
|
135
|
-
: window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
136
|
-
? 'dark'
|
|
137
|
-
: 'light'
|
|
138
|
-
|
|
128
|
+
const theme = config.theme === 'dark' ? 'dark' : config.theme === 'light' ? 'light' :
|
|
129
|
+
window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
139
130
|
const isDark = theme === 'dark'
|
|
140
131
|
|
|
141
132
|
const colors = isDark
|
|
142
|
-
? {
|
|
143
|
-
|
|
144
|
-
headerBorder: '#1e293b',
|
|
145
|
-
text: '#e5e7eb',
|
|
146
|
-
mutedText: '#94a3b8',
|
|
147
|
-
inputBg: '#020617',
|
|
148
|
-
inputBorder: '#334155',
|
|
149
|
-
botBubble: '#1e293b',
|
|
150
|
-
userText: '#ffffff',
|
|
151
|
-
messagesBg: '#020617'
|
|
152
|
-
}
|
|
153
|
-
: {
|
|
154
|
-
cardBg: '#ffffff',
|
|
155
|
-
headerBorder: '#e5e7eb',
|
|
156
|
-
text: '#0f172a',
|
|
157
|
-
mutedText: '#64748b',
|
|
158
|
-
inputBg: '#ffffff',
|
|
159
|
-
inputBorder: '#cbd5f5',
|
|
160
|
-
botBubble: '#e5e7eb',
|
|
161
|
-
userText: '#ffffff',
|
|
162
|
-
messagesBg: '#f8fafc'
|
|
163
|
-
}
|
|
133
|
+
? { cardBg: '#0f172a', headerBorder: '#1e293b', text: '#e5e7eb', mutedText: '#94a3b8', inputBg: '#020617', inputBorder: '#334155', botBubble: '#1e293b', userText: '#ffffff', messagesBg: '#020617' }
|
|
134
|
+
: { cardBg: '#ffffff', headerBorder: '#e5e7eb', text: '#0f172a', mutedText: '#64748b', inputBg: '#ffffff', inputBorder: '#cbd5f5', botBubble: '#e5e7eb', userText: '#ffffff', messagesBg: '#f8fafc' }
|
|
164
135
|
|
|
165
136
|
const primaryColor = config.primaryColor || '#2563eb'
|
|
166
137
|
|
|
@@ -208,20 +179,22 @@ function updateCard (config) {
|
|
|
208
179
|
const input = card.querySelector('#vc-input')
|
|
209
180
|
const sendBtn = card.querySelector('#vc-send')
|
|
210
181
|
|
|
211
|
-
|
|
182
|
+
// send message
|
|
183
|
+
async function sendMessage() {
|
|
212
184
|
const text = input.value.trim()
|
|
213
185
|
if (!text) return
|
|
214
186
|
|
|
215
187
|
const userMsg = document.createElement('div')
|
|
216
188
|
userMsg.style.cssText = `
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
189
|
+
background:${primaryColor};
|
|
190
|
+
color:${colors.userText};
|
|
191
|
+
padding:8px 12px;
|
|
192
|
+
border-radius:12px;
|
|
193
|
+
max-width:80%;
|
|
194
|
+
margin:8px 0 8px auto`
|
|
223
195
|
userMsg.textContent = text
|
|
224
196
|
messages.appendChild(userMsg)
|
|
197
|
+
|
|
225
198
|
input.value = ''
|
|
226
199
|
messages.scrollTop = messages.scrollHeight
|
|
227
200
|
|
|
@@ -232,29 +205,22 @@ function updateCard (config) {
|
|
|
232
205
|
|
|
233
206
|
showLoader(messages, sendBtn)
|
|
234
207
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
.then(res => res.json())
|
|
241
|
-
.then(data => {
|
|
242
|
-
hideLoader(sendBtn)
|
|
243
|
-
addBotMessage(
|
|
244
|
-
data?.data?.blocks?.map(b => b.text).join('\n') || 'No response'
|
|
245
|
-
)
|
|
246
|
-
})
|
|
247
|
-
.catch(() => {
|
|
248
|
-
hideLoader(sendBtn)
|
|
249
|
-
addBotMessage('⚠️ Something went wrong')
|
|
208
|
+
try {
|
|
209
|
+
const res = await fetch('https://api.vaultchat.io/askChatbot', {
|
|
210
|
+
method: 'POST',
|
|
211
|
+
headers: { 'Content-Type': 'application/json' },
|
|
212
|
+
body: JSON.stringify({ api_key: config.apiKey, question: text })
|
|
250
213
|
})
|
|
214
|
+
const data = await res.json()
|
|
215
|
+
hideLoader(sendBtn)
|
|
216
|
+
addBotMessage(data?.data?.blocks?.map(b => b.text).join('\n') || 'No response')
|
|
217
|
+
} catch {
|
|
218
|
+
hideLoader(sendBtn)
|
|
219
|
+
addBotMessage('⚠️ Something went wrong')
|
|
220
|
+
}
|
|
251
221
|
}
|
|
252
|
-
sendBtn.onclick = sendMessage
|
|
253
|
-
input.addEventListener('keydown', e => {
|
|
254
|
-
if (e.key === 'Enter') sendMessage()
|
|
255
|
-
})
|
|
256
222
|
|
|
257
|
-
function addBotMessage
|
|
223
|
+
function addBotMessage(text) {
|
|
258
224
|
const div = document.createElement('div')
|
|
259
225
|
div.style.cssText = `
|
|
260
226
|
background:${colors.botBubble};
|
|
@@ -267,12 +233,18 @@ function updateCard (config) {
|
|
|
267
233
|
messages.appendChild(div)
|
|
268
234
|
messages.scrollTop = messages.scrollHeight
|
|
269
235
|
}
|
|
236
|
+
|
|
237
|
+
// events
|
|
238
|
+
sendBtn.onclick = sendMessage
|
|
239
|
+
input.addEventListener('keydown', e => { if (e.key === 'Enter') sendMessage() })
|
|
240
|
+
|
|
241
|
+
return { messages, input, sendBtn }
|
|
270
242
|
}
|
|
271
243
|
|
|
272
244
|
/* =========================================================
|
|
273
245
|
LOADER
|
|
274
246
|
========================================================= */
|
|
275
|
-
function showLoader
|
|
247
|
+
function showLoader(container, sendBtn) {
|
|
276
248
|
loaderEl = document.createElement('div')
|
|
277
249
|
loaderEl.className = 'vc-loader'
|
|
278
250
|
loaderEl.innerHTML = '<span></span><span></span><span></span>'
|
|
@@ -285,7 +257,7 @@ function showLoader (container, sendBtn) {
|
|
|
285
257
|
}
|
|
286
258
|
}
|
|
287
259
|
|
|
288
|
-
function hideLoader
|
|
260
|
+
function hideLoader(sendBtn) {
|
|
289
261
|
loaderEl?.remove()
|
|
290
262
|
loaderEl = null
|
|
291
263
|
|
|
@@ -300,16 +272,29 @@ function hideLoader (sendBtn) {
|
|
|
300
272
|
PUBLIC API
|
|
301
273
|
========================================================= */
|
|
302
274
|
const VaultChat = {
|
|
303
|
-
init
|
|
275
|
+
init(config = {}) {
|
|
304
276
|
injectStylesOnce()
|
|
305
277
|
updateButton(config)
|
|
278
|
+
|
|
279
|
+
const mountEl = getAttachContainer(config.attachToElement)
|
|
280
|
+
mountEl.appendChild(vcButton)
|
|
281
|
+
|
|
306
282
|
updateCard(config)
|
|
283
|
+
mountEl.appendChild(vcCard)
|
|
307
284
|
|
|
308
|
-
|
|
309
|
-
button.onclick = () => {
|
|
285
|
+
vcButton.onclick = () => {
|
|
310
286
|
isOpen = !isOpen
|
|
311
287
|
vcCard.style.display = isOpen ? 'flex' : 'none'
|
|
312
288
|
}
|
|
289
|
+
},
|
|
290
|
+
|
|
291
|
+
destroy() {
|
|
292
|
+
vcButton?.remove()
|
|
293
|
+
vcCard?.remove()
|
|
294
|
+
vcButton = null
|
|
295
|
+
vcCard = null
|
|
296
|
+
loaderEl = null
|
|
297
|
+
isOpen = false
|
|
313
298
|
}
|
|
314
299
|
}
|
|
315
300
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mobil80-dev/chatbot-widget",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
4
4
|
"description": "Drop-in JavaScript chat widget for websites (no iframe, no framework)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"javascript",
|
|
12
12
|
"vaultchat"
|
|
13
13
|
],
|
|
14
|
-
"author": "
|
|
14
|
+
"author": "NanthaGopal",
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@mobil80-dev/chatbot-widget": "^1.0.8"
|