@bubblav/ai-chatbot-angular 1.0.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
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @bubblav/ai-chatbot-angular
|
|
2
|
+
|
|
3
|
+
Angular component for embedding the BubblaV AI chatbot widget in your Angular application.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bubblav/ai-chatbot-angular
|
|
9
|
+
# or
|
|
10
|
+
yarn add @bubblav/ai-chatbot-angular
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @bubblav/ai-chatbot-angular
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Import the Module (Angular 14+ - Standalone)
|
|
18
|
+
|
|
19
|
+
Since this is a standalone component, you can import it directly in your component:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Component } from '@angular/core';
|
|
23
|
+
import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';
|
|
24
|
+
|
|
25
|
+
@Component({
|
|
26
|
+
selector: 'app-root',
|
|
27
|
+
standalone: true,
|
|
28
|
+
imports: [BubblaVWidgetComponent],
|
|
29
|
+
template: `
|
|
30
|
+
<bubblav-widget
|
|
31
|
+
websiteId="your-website-id"
|
|
32
|
+
/>
|
|
33
|
+
`
|
|
34
|
+
})
|
|
35
|
+
export class AppComponent {}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Basic Usage
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<bubblav-widget
|
|
42
|
+
websiteId="your-website-id">
|
|
43
|
+
</bubblav-widget>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### With Custom Styling
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<bubblav-widget
|
|
50
|
+
websiteId="your-website-id"
|
|
51
|
+
bubbleColor="#3b82f6"
|
|
52
|
+
bubbleIconColor="#ffffff"
|
|
53
|
+
desktopPosition="bottom-right"
|
|
54
|
+
mobilePosition="bottom-right">
|
|
55
|
+
</bubblav-widget>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Dynamic Configuration
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { Component } from '@angular/core';
|
|
62
|
+
import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';
|
|
63
|
+
|
|
64
|
+
@Component({
|
|
65
|
+
selector: 'app-root',
|
|
66
|
+
standalone: true,
|
|
67
|
+
imports: [BubblaVWidgetComponent],
|
|
68
|
+
template: `
|
|
69
|
+
<bubblav-widget
|
|
70
|
+
[websiteId]="websiteId"
|
|
71
|
+
[bubbleColor]="bubbleColor">
|
|
72
|
+
</bubblav-widget>
|
|
73
|
+
`
|
|
74
|
+
})
|
|
75
|
+
export class AppComponent {
|
|
76
|
+
websiteId = 'your-website-id';
|
|
77
|
+
bubbleColor = '#3b82f6';
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Using the Service
|
|
82
|
+
|
|
83
|
+
For programmatic access to the widget SDK, inject the service:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { Component, inject } from '@angular/core';
|
|
87
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
88
|
+
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-support',
|
|
91
|
+
template: `
|
|
92
|
+
<button (click)="openChat()">Open Chat</button>
|
|
93
|
+
<button (click)="sendMessage()">Send Help Message</button>
|
|
94
|
+
`
|
|
95
|
+
})
|
|
96
|
+
export class SupportComponent {
|
|
97
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
98
|
+
|
|
99
|
+
openChat() {
|
|
100
|
+
this.bubblav.open();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
sendMessage() {
|
|
104
|
+
this.bubblav.sendMessage('I need help with my order');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With Service Initialization
|
|
110
|
+
|
|
111
|
+
If you want more control, you can initialize the widget manually through the service:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { Component, inject, OnInit } from '@angular/core';
|
|
115
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
116
|
+
|
|
117
|
+
@Component({
|
|
118
|
+
selector: 'app-root',
|
|
119
|
+
template: `
|
|
120
|
+
<h1>My App</h1>
|
|
121
|
+
`
|
|
122
|
+
})
|
|
123
|
+
export class AppComponent implements OnInit {
|
|
124
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
125
|
+
|
|
126
|
+
ngOnInit() {
|
|
127
|
+
this.bubblav.initialize({
|
|
128
|
+
websiteId: 'your-website-id',
|
|
129
|
+
bubbleColor: '#3b82f6',
|
|
130
|
+
desktopPosition: 'bottom-right'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
openChat() {
|
|
135
|
+
this.bubblav.open();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Observable State
|
|
141
|
+
|
|
142
|
+
Subscribe to widget state changes:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { Component, inject } from '@angular/core';
|
|
146
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
147
|
+
|
|
148
|
+
@Component({
|
|
149
|
+
selector: 'app-widget-status',
|
|
150
|
+
template: `
|
|
151
|
+
<div>Widget is {{ isOpen$ | async ? 'open' : 'closed' }}</div>
|
|
152
|
+
`
|
|
153
|
+
})
|
|
154
|
+
export class WidgetStatusComponent {
|
|
155
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
156
|
+
isOpen$ = this.bubblav.isOpen$;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Inputs
|
|
161
|
+
|
|
162
|
+
| Input | Type | Required | Default | Description |
|
|
163
|
+
|-------|------|----------|---------|-------------|
|
|
164
|
+
| `websiteId` | `string` | Yes | - | Your website ID from the BubblaV dashboard |
|
|
165
|
+
| `apiUrl` | `string` | No | Production API | Custom API URL (for self-hosted deployments) |
|
|
166
|
+
| `bubbleColor` | `string` | No | - | Bubble button color (hex) |
|
|
167
|
+
| `bubbleIconColor` | `string` | No | - | Bubble icon color (hex) |
|
|
168
|
+
| `desktopPosition` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Desktop position |
|
|
169
|
+
| `mobilePosition` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Mobile position |
|
|
170
|
+
| `poweredByVisible` | `boolean` | No | `true` | Show/hide powered by branding |
|
|
171
|
+
| `botName` | `string` | No | `'Bot'` | Custom bot name |
|
|
172
|
+
| `greetingMessage` | `string` | No | - | Greeting message when widget opens |
|
|
173
|
+
| `textboxPlaceholder` | `string` | No | - | Input placeholder text |
|
|
174
|
+
| `showActionButtons` | `boolean` | No | `true` | Show/hide action buttons |
|
|
175
|
+
|
|
176
|
+
## Service Methods
|
|
177
|
+
|
|
178
|
+
| Method | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `initialize(config)` | Initialize the widget with configuration |
|
|
181
|
+
| `open()` | Open the widget |
|
|
182
|
+
| `close()` | Close the widget |
|
|
183
|
+
| `toggle()` | Toggle widget open/close |
|
|
184
|
+
| `isOpen()` | Check if widget is open |
|
|
185
|
+
| `sendMessage(text, conversationId?)` | Send a message programmatically |
|
|
186
|
+
| `showGreeting(message?)` | Show greeting message |
|
|
187
|
+
| `hideGreeting()` | Hide greeting message |
|
|
188
|
+
| `getConfig()` | Get current widget configuration |
|
|
189
|
+
| `setDebug(enabled)` | Enable/disable debug mode |
|
|
190
|
+
| `on(event, callback)` | Register event listener |
|
|
191
|
+
| `off(event, callback)` | Unregister event listener |
|
|
192
|
+
| `emit(event, data?)` | Emit event |
|
|
193
|
+
| `ready(callback)` | Ready callback for widget loaded |
|
|
194
|
+
| `track(eventName, properties?)` | Track analytics event |
|
|
195
|
+
|
|
196
|
+
## Service Observables
|
|
197
|
+
|
|
198
|
+
| Observable | Description |
|
|
199
|
+
|------------|-------------|
|
|
200
|
+
| `isOpen$` | Emits `true` when widget opens, `false` when closes |
|
|
201
|
+
|
|
202
|
+
## Getting Your Website ID
|
|
203
|
+
|
|
204
|
+
1. Go to [bubblav.com/dashboard](https://www.bubblav.com/dashboard)
|
|
205
|
+
2. Select your website
|
|
206
|
+
3. Go to **Installation**
|
|
207
|
+
4. Copy your website ID
|
|
208
|
+
|
|
209
|
+
## Server-Side Rendering (SSR)
|
|
210
|
+
|
|
211
|
+
This component is SSR-safe. The widget script only loads in the browser.
|
|
212
|
+
|
|
213
|
+
## Angular Versions
|
|
214
|
+
|
|
215
|
+
Compatible with Angular 14+ (supports standalone components).
|
|
216
|
+
|
|
217
|
+
## TypeScript
|
|
218
|
+
|
|
219
|
+
This package is written in TypeScript and includes full type definitions.
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
|
224
|
+
|
|
225
|
+
## Support
|
|
226
|
+
|
|
227
|
+
- Documentation: [docs.bubblav.com](https://docs.bubblav.com)
|
|
228
|
+
- Issues: [GitHub Issues](https://github.com/tonnguyen/botcanchat/issues)
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @bubblav/ai-chatbot-angular
|
|
2
|
+
|
|
3
|
+
Angular component for embedding the BubblaV AI chatbot widget in your Angular application.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @bubblav/ai-chatbot-angular
|
|
9
|
+
# or
|
|
10
|
+
yarn add @bubblav/ai-chatbot-angular
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @bubblav/ai-chatbot-angular
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
### Import the Module (Angular 14+ - Standalone)
|
|
18
|
+
|
|
19
|
+
Since this is a standalone component, you can import it directly in your component:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { Component } from '@angular/core';
|
|
23
|
+
import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';
|
|
24
|
+
|
|
25
|
+
@Component({
|
|
26
|
+
selector: 'app-root',
|
|
27
|
+
standalone: true,
|
|
28
|
+
imports: [BubblaVWidgetComponent],
|
|
29
|
+
template: `
|
|
30
|
+
<bubblav-widget
|
|
31
|
+
websiteId="your-website-id"
|
|
32
|
+
/>
|
|
33
|
+
`
|
|
34
|
+
})
|
|
35
|
+
export class AppComponent {}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Basic Usage
|
|
39
|
+
|
|
40
|
+
```html
|
|
41
|
+
<bubblav-widget
|
|
42
|
+
websiteId="your-website-id">
|
|
43
|
+
</bubblav-widget>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### With Custom Styling
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<bubblav-widget
|
|
50
|
+
websiteId="your-website-id"
|
|
51
|
+
bubbleColor="#3b82f6"
|
|
52
|
+
bubbleIconColor="#ffffff"
|
|
53
|
+
desktopPosition="bottom-right"
|
|
54
|
+
mobilePosition="bottom-right">
|
|
55
|
+
</bubblav-widget>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Dynamic Configuration
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { Component } from '@angular/core';
|
|
62
|
+
import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';
|
|
63
|
+
|
|
64
|
+
@Component({
|
|
65
|
+
selector: 'app-root',
|
|
66
|
+
standalone: true,
|
|
67
|
+
imports: [BubblaVWidgetComponent],
|
|
68
|
+
template: `
|
|
69
|
+
<bubblav-widget
|
|
70
|
+
[websiteId]="websiteId"
|
|
71
|
+
[bubbleColor]="bubbleColor">
|
|
72
|
+
</bubblav-widget>
|
|
73
|
+
`
|
|
74
|
+
})
|
|
75
|
+
export class AppComponent {
|
|
76
|
+
websiteId = 'your-website-id';
|
|
77
|
+
bubbleColor = '#3b82f6';
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Using the Service
|
|
82
|
+
|
|
83
|
+
For programmatic access to the widget SDK, inject the service:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
import { Component, inject } from '@angular/core';
|
|
87
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
88
|
+
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-support',
|
|
91
|
+
template: `
|
|
92
|
+
<button (click)="openChat()">Open Chat</button>
|
|
93
|
+
<button (click)="sendMessage()">Send Help Message</button>
|
|
94
|
+
`
|
|
95
|
+
})
|
|
96
|
+
export class SupportComponent {
|
|
97
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
98
|
+
|
|
99
|
+
openChat() {
|
|
100
|
+
this.bubblav.open();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
sendMessage() {
|
|
104
|
+
this.bubblav.sendMessage('I need help with my order');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### With Service Initialization
|
|
110
|
+
|
|
111
|
+
If you want more control, you can initialize the widget manually through the service:
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { Component, inject, OnInit } from '@angular/core';
|
|
115
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
116
|
+
|
|
117
|
+
@Component({
|
|
118
|
+
selector: 'app-root',
|
|
119
|
+
template: `
|
|
120
|
+
<h1>My App</h1>
|
|
121
|
+
`
|
|
122
|
+
})
|
|
123
|
+
export class AppComponent implements OnInit {
|
|
124
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
125
|
+
|
|
126
|
+
ngOnInit() {
|
|
127
|
+
this.bubblav.initialize({
|
|
128
|
+
websiteId: 'your-website-id',
|
|
129
|
+
bubbleColor: '#3b82f6',
|
|
130
|
+
desktopPosition: 'bottom-right'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
openChat() {
|
|
135
|
+
this.bubblav.open();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Observable State
|
|
141
|
+
|
|
142
|
+
Subscribe to widget state changes:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { Component, inject } from '@angular/core';
|
|
146
|
+
import { BubblaVWidgetService } from '@bubblav/ai-chatbot-angular';
|
|
147
|
+
|
|
148
|
+
@Component({
|
|
149
|
+
selector: 'app-widget-status',
|
|
150
|
+
template: `
|
|
151
|
+
<div>Widget is {{ isOpen$ | async ? 'open' : 'closed' }}</div>
|
|
152
|
+
`
|
|
153
|
+
})
|
|
154
|
+
export class WidgetStatusComponent {
|
|
155
|
+
private bubblav = inject(BubblaVWidgetService);
|
|
156
|
+
isOpen$ = this.bubblav.isOpen$;
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Inputs
|
|
161
|
+
|
|
162
|
+
| Input | Type | Required | Default | Description |
|
|
163
|
+
|-------|------|----------|---------|-------------|
|
|
164
|
+
| `websiteId` | `string` | Yes | - | Your website ID from the BubblaV dashboard |
|
|
165
|
+
| `apiUrl` | `string` | No | Production API | Custom API URL (for self-hosted deployments) |
|
|
166
|
+
| `bubbleColor` | `string` | No | - | Bubble button color (hex) |
|
|
167
|
+
| `bubbleIconColor` | `string` | No | - | Bubble icon color (hex) |
|
|
168
|
+
| `desktopPosition` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Desktop position |
|
|
169
|
+
| `mobilePosition` | `'bottom-left' \| 'bottom-right'` | No | `'bottom-right'` | Mobile position |
|
|
170
|
+
| `poweredByVisible` | `boolean` | No | `true` | Show/hide powered by branding |
|
|
171
|
+
| `botName` | `string` | No | `'Bot'` | Custom bot name |
|
|
172
|
+
| `greetingMessage` | `string` | No | - | Greeting message when widget opens |
|
|
173
|
+
| `textboxPlaceholder` | `string` | No | - | Input placeholder text |
|
|
174
|
+
| `showActionButtons` | `boolean` | No | `true` | Show/hide action buttons |
|
|
175
|
+
|
|
176
|
+
## Service Methods
|
|
177
|
+
|
|
178
|
+
| Method | Description |
|
|
179
|
+
|--------|-------------|
|
|
180
|
+
| `initialize(config)` | Initialize the widget with configuration |
|
|
181
|
+
| `open()` | Open the widget |
|
|
182
|
+
| `close()` | Close the widget |
|
|
183
|
+
| `toggle()` | Toggle widget open/close |
|
|
184
|
+
| `isOpen()` | Check if widget is open |
|
|
185
|
+
| `sendMessage(text, conversationId?)` | Send a message programmatically |
|
|
186
|
+
| `showGreeting(message?)` | Show greeting message |
|
|
187
|
+
| `hideGreeting()` | Hide greeting message |
|
|
188
|
+
| `getConfig()` | Get current widget configuration |
|
|
189
|
+
| `setDebug(enabled)` | Enable/disable debug mode |
|
|
190
|
+
| `on(event, callback)` | Register event listener |
|
|
191
|
+
| `off(event, callback)` | Unregister event listener |
|
|
192
|
+
| `emit(event, data?)` | Emit event |
|
|
193
|
+
| `ready(callback)` | Ready callback for widget loaded |
|
|
194
|
+
| `track(eventName, properties?)` | Track analytics event |
|
|
195
|
+
|
|
196
|
+
## Service Observables
|
|
197
|
+
|
|
198
|
+
| Observable | Description |
|
|
199
|
+
|------------|-------------|
|
|
200
|
+
| `isOpen$` | Emits `true` when widget opens, `false` when closes |
|
|
201
|
+
|
|
202
|
+
## Getting Your Website ID
|
|
203
|
+
|
|
204
|
+
1. Go to [bubblav.com/dashboard](https://www.bubblav.com/dashboard)
|
|
205
|
+
2. Select your website
|
|
206
|
+
3. Go to **Installation**
|
|
207
|
+
4. Copy your website ID
|
|
208
|
+
|
|
209
|
+
## Server-Side Rendering (SSR)
|
|
210
|
+
|
|
211
|
+
This component is SSR-safe. The widget script only loads in the browser.
|
|
212
|
+
|
|
213
|
+
## Angular Versions
|
|
214
|
+
|
|
215
|
+
Compatible with Angular 14+ (supports standalone components).
|
|
216
|
+
|
|
217
|
+
## TypeScript
|
|
218
|
+
|
|
219
|
+
This package is written in TypeScript and includes full type definitions.
|
|
220
|
+
|
|
221
|
+
## License
|
|
222
|
+
|
|
223
|
+
MIT
|
|
224
|
+
|
|
225
|
+
## Support
|
|
226
|
+
|
|
227
|
+
- Documentation: [docs.bubblav.com](https://docs.bubblav.com)
|
|
228
|
+
- Issues: [GitHub Issues](https://github.com/tonnguyen/botcanchat/issues)
|
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, NgZone, DestroyRef, Injectable, ElementRef, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { BehaviorSubject } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Utility functions for @bubblav/ai-chatbot-angular
|
|
7
|
+
*/
|
|
8
|
+
const WIDGET_SCRIPT_URL = 'https://www.bubblav.com/widget.js';
|
|
9
|
+
const DEFAULT_API_URL = 'https://www.bubblav.com/api/chat';
|
|
10
|
+
/**
|
|
11
|
+
* Convert camelCase props to data-* attribute names
|
|
12
|
+
*/
|
|
13
|
+
function propsToDataAttributes(props) {
|
|
14
|
+
const dataAttrs = {};
|
|
15
|
+
for (const [key, value] of Object.entries(props)) {
|
|
16
|
+
if (value === undefined || value === null)
|
|
17
|
+
continue;
|
|
18
|
+
// Convert camelCase to kebab-case for data attributes
|
|
19
|
+
const dataKey = 'data-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
20
|
+
dataAttrs[dataKey] = String(value);
|
|
21
|
+
}
|
|
22
|
+
return dataAttrs;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the widget script URL
|
|
26
|
+
*/
|
|
27
|
+
function getWidgetScriptUrl(apiUrl) {
|
|
28
|
+
if (typeof window === 'undefined') {
|
|
29
|
+
return WIDGET_SCRIPT_URL;
|
|
30
|
+
}
|
|
31
|
+
// If custom API URL provided, derive widget URL from it
|
|
32
|
+
if (apiUrl) {
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(apiUrl, window.location.origin);
|
|
35
|
+
// Replace /api/chat with /widget.js
|
|
36
|
+
return url.origin + '/widget.js';
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return WIDGET_SCRIPT_URL;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return WIDGET_SCRIPT_URL;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Validate website ID format
|
|
46
|
+
*/
|
|
47
|
+
function validateWebsiteId(websiteId) {
|
|
48
|
+
// Basic validation: should be a non-empty string
|
|
49
|
+
// Format varies, so we just check it's a reasonable string
|
|
50
|
+
return typeof websiteId === 'string' && websiteId.length > 0 && websiteId.length < 100;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Filter config to only include widget config (exclude websiteId)
|
|
54
|
+
*/
|
|
55
|
+
function getConfigProps(config) {
|
|
56
|
+
const { websiteId: _websiteId, ...configProps } = config;
|
|
57
|
+
return configProps;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Check if we're in a browser environment
|
|
61
|
+
*/
|
|
62
|
+
function isBrowser() {
|
|
63
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if a widget script is already loaded
|
|
67
|
+
*/
|
|
68
|
+
function isScriptLoaded(url) {
|
|
69
|
+
if (!isBrowser())
|
|
70
|
+
return false;
|
|
71
|
+
const scripts = document.getElementsByTagName('script');
|
|
72
|
+
for (let i = 0; i < scripts.length; i++) {
|
|
73
|
+
if (scripts[i].src === url) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* BubblaVWidget Service
|
|
82
|
+
*
|
|
83
|
+
* Injectable service for programmatic access to the BubblaV SDK.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```ts
|
|
87
|
+
* constructor(private bubblav: BubblaVWidgetService) {
|
|
88
|
+
* bubblav.open();
|
|
89
|
+
* bubblav.sendMessage('Hello');
|
|
90
|
+
* }
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
class BubblaVWidgetService {
|
|
94
|
+
ngZone = inject(NgZone);
|
|
95
|
+
destroyRef = inject(DestroyRef);
|
|
96
|
+
sdk = null;
|
|
97
|
+
scriptElement = null;
|
|
98
|
+
isInitialized = false;
|
|
99
|
+
// Track widget state
|
|
100
|
+
isOpenSubject = new BehaviorSubject(false);
|
|
101
|
+
isOpen$ = this.isOpenSubject.asObservable();
|
|
102
|
+
constructor() {
|
|
103
|
+
// Initialize SDK if already available
|
|
104
|
+
if (isBrowser() && window.BubblaV) {
|
|
105
|
+
this.sdk = window.BubblaV;
|
|
106
|
+
this.setupEventListeners();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Initialize the widget with configuration
|
|
111
|
+
*/
|
|
112
|
+
initialize(config) {
|
|
113
|
+
if (!isBrowser()) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (this.isInitialized) {
|
|
117
|
+
console.warn('[BubblaV] Widget already initialized');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Validate website ID
|
|
121
|
+
if (!validateWebsiteId(config.websiteId)) {
|
|
122
|
+
console.warn(`[BubblaV] Invalid website ID format: "${config.websiteId}". ` +
|
|
123
|
+
`Please check your website ID in the BubblaV dashboard.`);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Get script URL
|
|
127
|
+
const scriptUrl = getWidgetScriptUrl(config.apiUrl);
|
|
128
|
+
// Check if script is already loaded
|
|
129
|
+
if (isScriptLoaded(scriptUrl)) {
|
|
130
|
+
console.warn(`[BubblaV] Widget script already loaded. ` +
|
|
131
|
+
`Only one widget instance should be active.`);
|
|
132
|
+
if (window.BubblaV) {
|
|
133
|
+
this.sdk = window.BubblaV;
|
|
134
|
+
this.setupEventListeners();
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
// Mark as initialized
|
|
139
|
+
this.isInitialized = true;
|
|
140
|
+
// Create script element
|
|
141
|
+
const script = document.createElement('script');
|
|
142
|
+
script.src = scriptUrl;
|
|
143
|
+
script.async = true;
|
|
144
|
+
script.defer = true;
|
|
145
|
+
// Set data attributes
|
|
146
|
+
script.setAttribute('data-site-id', config.websiteId);
|
|
147
|
+
// Set optional config as data attributes
|
|
148
|
+
const configProps = getConfigProps(config);
|
|
149
|
+
const dataAttrs = propsToDataAttributes(configProps);
|
|
150
|
+
for (const [key, value] of Object.entries(dataAttrs)) {
|
|
151
|
+
script.setAttribute(key, value);
|
|
152
|
+
}
|
|
153
|
+
// Handle load event
|
|
154
|
+
script.onload = () => {
|
|
155
|
+
this.ngZone.run(() => {
|
|
156
|
+
if (window.BubblaV) {
|
|
157
|
+
this.sdk = window.BubblaV;
|
|
158
|
+
this.setupEventListeners();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
};
|
|
162
|
+
// Handle error event
|
|
163
|
+
script.onerror = () => {
|
|
164
|
+
console.error(`[BubblaV] Failed to load widget script from ${scriptUrl}. ` +
|
|
165
|
+
`Please check your network connection and ensure the URL is correct.`);
|
|
166
|
+
this.isInitialized = false;
|
|
167
|
+
};
|
|
168
|
+
// Add script to document
|
|
169
|
+
document.body.appendChild(script);
|
|
170
|
+
this.scriptElement = script;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Setup event listeners for widget state changes
|
|
174
|
+
*/
|
|
175
|
+
setupEventListeners() {
|
|
176
|
+
if (!this.sdk)
|
|
177
|
+
return;
|
|
178
|
+
this.sdk.on('widget_opened', () => {
|
|
179
|
+
this.ngZone.run(() => {
|
|
180
|
+
this.isOpenSubject.next(true);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
this.sdk.on('widget_closed', () => {
|
|
184
|
+
this.ngZone.run(() => {
|
|
185
|
+
this.isOpenSubject.next(false);
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Cleanup widget
|
|
191
|
+
*/
|
|
192
|
+
destroy() {
|
|
193
|
+
if (this.scriptElement && this.scriptElement.parentNode) {
|
|
194
|
+
this.scriptElement.parentNode.removeChild(this.scriptElement);
|
|
195
|
+
}
|
|
196
|
+
this.scriptElement = null;
|
|
197
|
+
this.sdk = null;
|
|
198
|
+
this.isInitialized = false;
|
|
199
|
+
}
|
|
200
|
+
// SDK Methods
|
|
201
|
+
open() {
|
|
202
|
+
this.sdk?.open();
|
|
203
|
+
}
|
|
204
|
+
close() {
|
|
205
|
+
this.sdk?.close();
|
|
206
|
+
}
|
|
207
|
+
toggle() {
|
|
208
|
+
this.sdk?.toggle();
|
|
209
|
+
}
|
|
210
|
+
isOpen() {
|
|
211
|
+
return this.sdk?.isOpen() ?? false;
|
|
212
|
+
}
|
|
213
|
+
sendMessage(text, conversationId) {
|
|
214
|
+
this.sdk?.sendMessage(text, conversationId);
|
|
215
|
+
}
|
|
216
|
+
showGreeting(message) {
|
|
217
|
+
this.sdk?.showGreeting(message);
|
|
218
|
+
}
|
|
219
|
+
hideGreeting() {
|
|
220
|
+
this.sdk?.hideGreeting();
|
|
221
|
+
}
|
|
222
|
+
getConfig() {
|
|
223
|
+
return this.sdk?.getConfig() ?? {};
|
|
224
|
+
}
|
|
225
|
+
setDebug(enabled) {
|
|
226
|
+
this.sdk?.setDebug(enabled);
|
|
227
|
+
}
|
|
228
|
+
// Event methods
|
|
229
|
+
/**
|
|
230
|
+
* Listen to widget events
|
|
231
|
+
*/
|
|
232
|
+
on(event, callback) {
|
|
233
|
+
this.sdk?.on(event, callback);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Unregister event listener
|
|
237
|
+
*/
|
|
238
|
+
off(event, callback) {
|
|
239
|
+
this.sdk?.off(event, callback);
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Emit event
|
|
243
|
+
*/
|
|
244
|
+
emit(event, data) {
|
|
245
|
+
this.sdk?.emit(event, data);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Ready callback for widget loaded
|
|
249
|
+
*/
|
|
250
|
+
ready(callback) {
|
|
251
|
+
this.sdk?.ready(callback);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Track analytics event
|
|
255
|
+
*/
|
|
256
|
+
track(eventName, properties) {
|
|
257
|
+
this.sdk?.track(eventName, properties);
|
|
258
|
+
}
|
|
259
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BubblaVWidgetService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
260
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BubblaVWidgetService, providedIn: 'root' });
|
|
261
|
+
}
|
|
262
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BubblaVWidgetService, decorators: [{
|
|
263
|
+
type: Injectable,
|
|
264
|
+
args: [{
|
|
265
|
+
providedIn: 'root',
|
|
266
|
+
}]
|
|
267
|
+
}], ctorParameters: () => [] });
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* BubblaVWidget Component
|
|
271
|
+
*
|
|
272
|
+
* Standalone Angular component for embedding the BubblaV chat widget.
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* ```html
|
|
276
|
+
* <bubblav-widget
|
|
277
|
+
* websiteId="your-website-id"
|
|
278
|
+
* bubbleColor="#3b82f6"
|
|
279
|
+
* desktopPosition="bottom-right">
|
|
280
|
+
* </bubblav-widget>
|
|
281
|
+
* ```
|
|
282
|
+
*/
|
|
283
|
+
class BubblaVWidgetComponent {
|
|
284
|
+
service = inject(BubblaVWidgetService);
|
|
285
|
+
elementRef = inject(ElementRef);
|
|
286
|
+
// Required input
|
|
287
|
+
websiteId;
|
|
288
|
+
// Optional styling inputs
|
|
289
|
+
apiUrl;
|
|
290
|
+
bubbleColor;
|
|
291
|
+
bubbleIconColor;
|
|
292
|
+
desktopPosition;
|
|
293
|
+
mobilePosition;
|
|
294
|
+
poweredByVisible;
|
|
295
|
+
botName;
|
|
296
|
+
greetingMessage;
|
|
297
|
+
textboxPlaceholder;
|
|
298
|
+
showActionButtons;
|
|
299
|
+
config = {
|
|
300
|
+
websiteId: '',
|
|
301
|
+
};
|
|
302
|
+
ngOnInit() {
|
|
303
|
+
// Validate website ID
|
|
304
|
+
if (!validateWebsiteId(this.websiteId)) {
|
|
305
|
+
console.warn(`[BubblaV] Invalid website ID format: "${this.websiteId}". ` +
|
|
306
|
+
`Please check your website ID in the BubblaV dashboard.`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
// Build config object
|
|
310
|
+
this.config = {
|
|
311
|
+
websiteId: this.websiteId,
|
|
312
|
+
...(this.apiUrl !== undefined && { apiUrl: this.apiUrl }),
|
|
313
|
+
...(this.bubbleColor !== undefined && { bubbleColor: this.bubbleColor }),
|
|
314
|
+
...(this.bubbleIconColor !== undefined && { bubbleIconColor: this.bubbleIconColor }),
|
|
315
|
+
...(this.desktopPosition !== undefined && { desktopPosition: this.desktopPosition }),
|
|
316
|
+
...(this.mobilePosition !== undefined && { mobilePosition: this.mobilePosition }),
|
|
317
|
+
...(this.poweredByVisible !== undefined && { poweredByVisible: this.poweredByVisible }),
|
|
318
|
+
...(this.botName !== undefined && { botName: this.botName }),
|
|
319
|
+
...(this.greetingMessage !== undefined && { greetingMessage: this.greetingMessage }),
|
|
320
|
+
...(this.textboxPlaceholder !== undefined && { textboxPlaceholder: this.textboxPlaceholder }),
|
|
321
|
+
...(this.showActionButtons !== undefined && { showActionButtons: this.showActionButtons }),
|
|
322
|
+
};
|
|
323
|
+
// Initialize widget
|
|
324
|
+
this.service.initialize(this.config);
|
|
325
|
+
}
|
|
326
|
+
ngOnDestroy() {
|
|
327
|
+
// Cleanup widget
|
|
328
|
+
this.service.destroy();
|
|
329
|
+
}
|
|
330
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BubblaVWidgetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
331
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.5", type: BubblaVWidgetComponent, isStandalone: true, selector: "bubblav-widget", inputs: { websiteId: "websiteId", apiUrl: "apiUrl", bubbleColor: "bubbleColor", bubbleIconColor: "bubbleIconColor", desktopPosition: "desktopPosition", mobilePosition: "mobilePosition", poweredByVisible: "poweredByVisible", botName: "botName", greetingMessage: "greetingMessage", textboxPlaceholder: "textboxPlaceholder", showActionButtons: "showActionButtons" }, ngImport: i0, template: '', isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
332
|
+
}
|
|
333
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.5", ngImport: i0, type: BubblaVWidgetComponent, decorators: [{
|
|
334
|
+
type: Component,
|
|
335
|
+
args: [{
|
|
336
|
+
selector: 'bubblav-widget',
|
|
337
|
+
template: '',
|
|
338
|
+
standalone: true,
|
|
339
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
340
|
+
}]
|
|
341
|
+
}], propDecorators: { websiteId: [{
|
|
342
|
+
type: Input,
|
|
343
|
+
args: [{ required: true }]
|
|
344
|
+
}], apiUrl: [{
|
|
345
|
+
type: Input
|
|
346
|
+
}], bubbleColor: [{
|
|
347
|
+
type: Input
|
|
348
|
+
}], bubbleIconColor: [{
|
|
349
|
+
type: Input
|
|
350
|
+
}], desktopPosition: [{
|
|
351
|
+
type: Input
|
|
352
|
+
}], mobilePosition: [{
|
|
353
|
+
type: Input
|
|
354
|
+
}], poweredByVisible: [{
|
|
355
|
+
type: Input
|
|
356
|
+
}], botName: [{
|
|
357
|
+
type: Input
|
|
358
|
+
}], greetingMessage: [{
|
|
359
|
+
type: Input
|
|
360
|
+
}], textboxPlaceholder: [{
|
|
361
|
+
type: Input
|
|
362
|
+
}], showActionButtons: [{
|
|
363
|
+
type: Input
|
|
364
|
+
}] } });
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* @bubblav/ai-chatbot-angular
|
|
368
|
+
*
|
|
369
|
+
* Angular component and service for embedding the BubblaV AI chatbot widget.
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```ts
|
|
373
|
+
* import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';
|
|
374
|
+
* ```
|
|
375
|
+
*/
|
|
376
|
+
// Component export
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Generated bundle index. Do not edit.
|
|
380
|
+
*/
|
|
381
|
+
|
|
382
|
+
export { BubblaVWidgetComponent, BubblaVWidgetService, getConfigProps, getWidgetScriptUrl, isBrowser, isScriptLoaded, propsToDataAttributes, validateWebsiteId };
|
|
383
|
+
//# sourceMappingURL=bubblav-ai-chatbot-angular.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bubblav-ai-chatbot-angular.mjs","sources":["../../src/lib/utils.ts","../../src/lib/bubblav-widget.service.ts","../../src/lib/bubblav-widget.component.ts","../../src/index.ts","../../src/bubblav-ai-chatbot-angular.ts"],"sourcesContent":["/**\n * Utility functions for @bubblav/ai-chatbot-angular\n */\n\nimport { BubblaVWidgetConfig } from './types';\n\nconst WIDGET_SCRIPT_URL = 'https://www.bubblav.com/widget.js';\nconst DEFAULT_API_URL = 'https://www.bubblav.com/api/chat';\n\n/**\n * Convert camelCase props to data-* attribute names\n */\nexport function propsToDataAttributes(props: Record<string, unknown>): Record<string, string> {\n const dataAttrs: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(props)) {\n if (value === undefined || value === null) continue;\n\n // Convert camelCase to kebab-case for data attributes\n const dataKey = 'data-' + key.replace(/([A-Z])/g, '-$1').toLowerCase();\n dataAttrs[dataKey] = String(value);\n }\n\n return dataAttrs;\n}\n\n/**\n * Get the widget script URL\n */\nexport function getWidgetScriptUrl(apiUrl?: string): string {\n if (typeof window === 'undefined') {\n return WIDGET_SCRIPT_URL;\n }\n\n // If custom API URL provided, derive widget URL from it\n if (apiUrl) {\n try {\n const url = new URL(apiUrl, window.location.origin);\n // Replace /api/chat with /widget.js\n return url.origin + '/widget.js';\n } catch {\n return WIDGET_SCRIPT_URL;\n }\n }\n\n return WIDGET_SCRIPT_URL;\n}\n\n/**\n * Validate website ID format\n */\nexport function validateWebsiteId(websiteId: string): boolean {\n // Basic validation: should be a non-empty string\n // Format varies, so we just check it's a reasonable string\n return typeof websiteId === 'string' && websiteId.length > 0 && websiteId.length < 100;\n}\n\n/**\n * Filter config to only include widget config (exclude websiteId)\n */\nexport function getConfigProps(config: BubblaVWidgetConfig): Omit<BubblaVWidgetConfig, 'websiteId'> {\n const { websiteId: _websiteId, ...configProps } = config;\n return configProps;\n}\n\n/**\n * Check if we're in a browser environment\n */\nexport function isBrowser(): boolean {\n return typeof window !== 'undefined' && typeof document !== 'undefined';\n}\n\n/**\n * Check if a widget script is already loaded\n */\nexport function isScriptLoaded(url: string): boolean {\n if (!isBrowser()) return false;\n\n const scripts = document.getElementsByTagName('script');\n for (let i = 0; i < scripts.length; i++) {\n if (scripts[i].src === url) {\n return true;\n }\n }\n return false;\n}\n","/**\n * BubblaVWidget Service\n *\n * Injectable service for programmatic access to the BubblaV SDK.\n *\n * @example\n * ```ts\n * constructor(private bubblav: BubblaVWidgetService) {\n * bubblav.open();\n * bubblav.sendMessage('Hello');\n * }\n * ```\n */\n\nimport { Injectable, NgZone, inject, DestroyRef } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { BehaviorSubject, Observable } from 'rxjs';\nimport { BubblaVWidgetSDK, BubblaVWidgetConfig, BubblaVSDK } from './types';\nimport {\n getWidgetScriptUrl,\n validateWebsiteId,\n propsToDataAttributes,\n getConfigProps,\n isBrowser,\n isScriptLoaded,\n} from './utils';\n\n@Injectable({\n providedIn: 'root',\n})\nexport class BubblaVWidgetService implements BubblaVWidgetSDK {\n private ngZone = inject(NgZone);\n private destroyRef = inject(DestroyRef);\n\n private sdk: BubblaVSDK | null = null;\n private scriptElement: HTMLScriptElement | null = null;\n private isInitialized = false;\n\n // Track widget state\n private isOpenSubject = new BehaviorSubject<boolean>(false);\n public readonly isOpen$ = this.isOpenSubject.asObservable();\n\n constructor() {\n // Initialize SDK if already available\n if (isBrowser() && window.BubblaV) {\n this.sdk = window.BubblaV;\n this.setupEventListeners();\n }\n }\n\n /**\n * Initialize the widget with configuration\n */\n initialize(config: BubblaVWidgetConfig): void {\n if (!isBrowser()) {\n return;\n }\n\n if (this.isInitialized) {\n console.warn('[BubblaV] Widget already initialized');\n return;\n }\n\n // Validate website ID\n if (!validateWebsiteId(config.websiteId)) {\n console.warn(\n `[BubblaV] Invalid website ID format: \"${config.websiteId}\". ` +\n `Please check your website ID in the BubblaV dashboard.`\n );\n return;\n }\n\n // Get script URL\n const scriptUrl = getWidgetScriptUrl(config.apiUrl);\n\n // Check if script is already loaded\n if (isScriptLoaded(scriptUrl)) {\n console.warn(\n `[BubblaV] Widget script already loaded. ` +\n `Only one widget instance should be active.`\n );\n if (window.BubblaV) {\n this.sdk = window.BubblaV;\n this.setupEventListeners();\n }\n return;\n }\n\n // Mark as initialized\n this.isInitialized = true;\n\n // Create script element\n const script = document.createElement('script');\n script.src = scriptUrl;\n script.async = true;\n script.defer = true;\n\n // Set data attributes\n script.setAttribute('data-site-id', config.websiteId);\n\n // Set optional config as data attributes\n const configProps = getConfigProps(config);\n const dataAttrs = propsToDataAttributes(configProps);\n for (const [key, value] of Object.entries(dataAttrs)) {\n script.setAttribute(key, value);\n }\n\n // Handle load event\n script.onload = () => {\n this.ngZone.run(() => {\n if (window.BubblaV) {\n this.sdk = window.BubblaV;\n this.setupEventListeners();\n }\n });\n };\n\n // Handle error event\n script.onerror = () => {\n console.error(\n `[BubblaV] Failed to load widget script from ${scriptUrl}. ` +\n `Please check your network connection and ensure the URL is correct.`\n );\n this.isInitialized = false;\n };\n\n // Add script to document\n document.body.appendChild(script);\n this.scriptElement = script;\n }\n\n /**\n * Setup event listeners for widget state changes\n */\n private setupEventListeners(): void {\n if (!this.sdk) return;\n\n this.sdk.on('widget_opened', () => {\n this.ngZone.run(() => {\n this.isOpenSubject.next(true);\n });\n });\n\n this.sdk.on('widget_closed', () => {\n this.ngZone.run(() => {\n this.isOpenSubject.next(false);\n });\n });\n }\n\n /**\n * Cleanup widget\n */\n destroy(): void {\n if (this.scriptElement && this.scriptElement.parentNode) {\n this.scriptElement.parentNode.removeChild(this.scriptElement);\n }\n this.scriptElement = null;\n this.sdk = null;\n this.isInitialized = false;\n }\n\n // SDK Methods\n\n open(): void {\n this.sdk?.open();\n }\n\n close(): void {\n this.sdk?.close();\n }\n\n toggle(): void {\n this.sdk?.toggle();\n }\n\n isOpen(): boolean {\n return this.sdk?.isOpen() ?? false;\n }\n\n sendMessage(text: string, conversationId?: string): void {\n this.sdk?.sendMessage(text, conversationId);\n }\n\n showGreeting(message?: string): void {\n this.sdk?.showGreeting(message);\n }\n\n hideGreeting(): void {\n this.sdk?.hideGreeting();\n }\n\n getConfig(): Record<string, unknown> {\n return this.sdk?.getConfig() ?? {};\n }\n\n setDebug(enabled: boolean): void {\n this.sdk?.setDebug(enabled);\n }\n\n // Event methods\n\n /**\n * Listen to widget events\n */\n on(event: string, callback: (...args: unknown[]) => void): void {\n this.sdk?.on(event, callback);\n }\n\n /**\n * Unregister event listener\n */\n off(event: string, callback: (...args: unknown[]) => void): void {\n this.sdk?.off(event, callback);\n }\n\n /**\n * Emit event\n */\n emit(event: string, data?: unknown): void {\n this.sdk?.emit(event, data);\n }\n\n /**\n * Ready callback for widget loaded\n */\n ready(callback: () => void): void {\n this.sdk?.ready(callback);\n }\n\n /**\n * Track analytics event\n */\n track(eventName: string, properties?: Record<string, unknown>): void {\n this.sdk?.track(eventName, properties);\n }\n}\n","/**\n * BubblaVWidget Component\n *\n * Standalone Angular component for embedding the BubblaV chat widget.\n *\n * @example\n * ```html\n * <bubblav-widget\n * websiteId=\"your-website-id\"\n * bubbleColor=\"#3b82f6\"\n * desktopPosition=\"bottom-right\">\n * </bubblav-widget>\n * ```\n */\n\nimport {\n Component,\n Input,\n OnDestroy,\n OnInit,\n inject,\n ElementRef,\n ChangeDetectionStrategy,\n} from '@angular/core';\nimport { BubblaVWidgetService } from './bubblav-widget.service';\nimport { BubblaVWidgetConfig } from './types';\nimport { validateWebsiteId } from './utils';\n\n@Component({\n selector: 'bubblav-widget',\n template: '',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class BubblaVWidgetComponent implements OnInit, OnDestroy {\n private service = inject(BubblaVWidgetService);\n private elementRef = inject(ElementRef);\n\n // Required input\n @Input({ required: true }) websiteId!: string;\n\n // Optional styling inputs\n @Input() apiUrl?: string;\n @Input() bubbleColor?: string;\n @Input() bubbleIconColor?: string;\n @Input() desktopPosition?: 'bottom-left' | 'bottom-right';\n @Input() mobilePosition?: 'bottom-left' | 'bottom-right';\n @Input() poweredByVisible?: boolean;\n @Input() botName?: string;\n @Input() greetingMessage?: string;\n @Input() textboxPlaceholder?: string;\n @Input() showActionButtons?: boolean;\n\n private config: BubblaVWidgetConfig = {\n websiteId: '',\n };\n\n ngOnInit(): void {\n // Validate website ID\n if (!validateWebsiteId(this.websiteId)) {\n console.warn(\n `[BubblaV] Invalid website ID format: \"${this.websiteId}\". ` +\n `Please check your website ID in the BubblaV dashboard.`\n );\n return;\n }\n\n // Build config object\n this.config = {\n websiteId: this.websiteId,\n ...(this.apiUrl !== undefined && { apiUrl: this.apiUrl }),\n ...(this.bubbleColor !== undefined && { bubbleColor: this.bubbleColor }),\n ...(this.bubbleIconColor !== undefined && { bubbleIconColor: this.bubbleIconColor }),\n ...(this.desktopPosition !== undefined && { desktopPosition: this.desktopPosition }),\n ...(this.mobilePosition !== undefined && { mobilePosition: this.mobilePosition }),\n ...(this.poweredByVisible !== undefined && { poweredByVisible: this.poweredByVisible }),\n ...(this.botName !== undefined && { botName: this.botName }),\n ...(this.greetingMessage !== undefined && { greetingMessage: this.greetingMessage }),\n ...(this.textboxPlaceholder !== undefined && { textboxPlaceholder: this.textboxPlaceholder }),\n ...(this.showActionButtons !== undefined && { showActionButtons: this.showActionButtons }),\n };\n\n // Initialize widget\n this.service.initialize(this.config);\n }\n\n ngOnDestroy(): void {\n // Cleanup widget\n this.service.destroy();\n }\n}\n","/**\n * @bubblav/ai-chatbot-angular\n *\n * Angular component and service for embedding the BubblaV AI chatbot widget.\n *\n * @example\n * ```ts\n * import { BubblaVWidgetComponent } from '@bubblav/ai-chatbot-angular';\n * ```\n */\n\n// Component export\nexport { BubblaVWidgetComponent } from './lib/bubblav-widget.component';\n\n// Service export\nexport { BubblaVWidgetService } from './lib/bubblav-widget.service';\n\n// Type exports\nexport type {\n BubblaVWidgetConfig,\n BubblaVWidgetSDK,\n BubblaVSDK,\n} from './lib/types';\n\n// Utility exports\nexport {\n propsToDataAttributes,\n getWidgetScriptUrl,\n validateWebsiteId,\n getConfigProps,\n isBrowser,\n isScriptLoaded,\n} from './lib/utils';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { OnInit, OnDestroy } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* BubblaVWidget Component
|
|
7
|
+
*
|
|
8
|
+
* Standalone Angular component for embedding the BubblaV chat widget.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```html
|
|
12
|
+
* <bubblav-widget
|
|
13
|
+
* websiteId="your-website-id"
|
|
14
|
+
* bubbleColor="#3b82f6"
|
|
15
|
+
* desktopPosition="bottom-right">
|
|
16
|
+
* </bubblav-widget>
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
declare class BubblaVWidgetComponent implements OnInit, OnDestroy {
|
|
21
|
+
private service;
|
|
22
|
+
private elementRef;
|
|
23
|
+
websiteId: string;
|
|
24
|
+
apiUrl?: string;
|
|
25
|
+
bubbleColor?: string;
|
|
26
|
+
bubbleIconColor?: string;
|
|
27
|
+
desktopPosition?: 'bottom-left' | 'bottom-right';
|
|
28
|
+
mobilePosition?: 'bottom-left' | 'bottom-right';
|
|
29
|
+
poweredByVisible?: boolean;
|
|
30
|
+
botName?: string;
|
|
31
|
+
greetingMessage?: string;
|
|
32
|
+
textboxPlaceholder?: string;
|
|
33
|
+
showActionButtons?: boolean;
|
|
34
|
+
private config;
|
|
35
|
+
ngOnInit(): void;
|
|
36
|
+
ngOnDestroy(): void;
|
|
37
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<BubblaVWidgetComponent, never>;
|
|
38
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<BubblaVWidgetComponent, "bubblav-widget", never, { "websiteId": { "alias": "websiteId"; "required": true; }; "apiUrl": { "alias": "apiUrl"; "required": false; }; "bubbleColor": { "alias": "bubbleColor"; "required": false; }; "bubbleIconColor": { "alias": "bubbleIconColor"; "required": false; }; "desktopPosition": { "alias": "desktopPosition"; "required": false; }; "mobilePosition": { "alias": "mobilePosition"; "required": false; }; "poweredByVisible": { "alias": "poweredByVisible"; "required": false; }; "botName": { "alias": "botName"; "required": false; }; "greetingMessage": { "alias": "greetingMessage"; "required": false; }; "textboxPlaceholder": { "alias": "textboxPlaceholder"; "required": false; }; "showActionButtons": { "alias": "showActionButtons"; "required": false; }; }, {}, never, never, true, never>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Type definitions for @bubblav/ai-chatbot-angular
|
|
43
|
+
*/
|
|
44
|
+
/** Widget position options */
|
|
45
|
+
type WidgetPosition = 'bottom-left' | 'bottom-right';
|
|
46
|
+
/**
|
|
47
|
+
* Configuration for the BubblaV widget
|
|
48
|
+
*/
|
|
49
|
+
interface BubblaVWidgetConfig {
|
|
50
|
+
/** Required: Your website ID from the BubblaV dashboard */
|
|
51
|
+
websiteId: string;
|
|
52
|
+
/** Optional: Custom API URL (defaults to production) */
|
|
53
|
+
apiUrl?: string;
|
|
54
|
+
/** Optional: Bubble button color (hex) */
|
|
55
|
+
bubbleColor?: string;
|
|
56
|
+
/** Optional: Bubble icon color (hex) */
|
|
57
|
+
bubbleIconColor?: string;
|
|
58
|
+
/** Optional: Desktop position */
|
|
59
|
+
desktopPosition?: WidgetPosition;
|
|
60
|
+
/** Optional: Mobile position */
|
|
61
|
+
mobilePosition?: WidgetPosition;
|
|
62
|
+
/** Optional: Show/hide powered by branding */
|
|
63
|
+
poweredByVisible?: boolean;
|
|
64
|
+
/** Optional: Custom bot name */
|
|
65
|
+
botName?: string;
|
|
66
|
+
/** Optional: Greeting message shown when widget opens */
|
|
67
|
+
greetingMessage?: string;
|
|
68
|
+
/** Optional: Placeholder text for input field */
|
|
69
|
+
textboxPlaceholder?: string;
|
|
70
|
+
/** Optional: Whether to show action buttons */
|
|
71
|
+
showActionButtons?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* SDK methods available via the service
|
|
75
|
+
*/
|
|
76
|
+
interface BubblaVWidgetSDK {
|
|
77
|
+
/** Open the widget */
|
|
78
|
+
open(): void;
|
|
79
|
+
/** Close the widget */
|
|
80
|
+
close(): void;
|
|
81
|
+
/** Toggle widget open/close state */
|
|
82
|
+
toggle(): void;
|
|
83
|
+
/** Check if widget is currently open */
|
|
84
|
+
isOpen(): boolean;
|
|
85
|
+
/** Send a message programmatically */
|
|
86
|
+
sendMessage(text: string, conversationId?: string): void;
|
|
87
|
+
/** Show greeting message */
|
|
88
|
+
showGreeting(message?: string): void;
|
|
89
|
+
/** Hide greeting message */
|
|
90
|
+
hideGreeting(): void;
|
|
91
|
+
/** Get current widget configuration */
|
|
92
|
+
getConfig(): Record<string, unknown>;
|
|
93
|
+
/** Enable or disable debug mode */
|
|
94
|
+
setDebug(enabled: boolean): void;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Global window.BubblaV SDK interface
|
|
98
|
+
*/
|
|
99
|
+
interface BubblaVSDK extends BubblaVWidgetSDK {
|
|
100
|
+
/** Register event listener */
|
|
101
|
+
on(event: string, callback: (...args: unknown[]) => void): void;
|
|
102
|
+
/** Unregister event listener */
|
|
103
|
+
off(event: string, callback: (...args: unknown[]) => void): void;
|
|
104
|
+
/** Emit event */
|
|
105
|
+
emit(event: string, data?: unknown): void;
|
|
106
|
+
/** Ready callback for widget loaded */
|
|
107
|
+
ready(callback: () => void): void;
|
|
108
|
+
/** Track analytics event */
|
|
109
|
+
track(eventName: string, properties?: Record<string, unknown>): void;
|
|
110
|
+
}
|
|
111
|
+
declare global {
|
|
112
|
+
interface Window {
|
|
113
|
+
BubblaV?: BubblaVSDK;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
declare class BubblaVWidgetService implements BubblaVWidgetSDK {
|
|
118
|
+
private ngZone;
|
|
119
|
+
private destroyRef;
|
|
120
|
+
private sdk;
|
|
121
|
+
private scriptElement;
|
|
122
|
+
private isInitialized;
|
|
123
|
+
private isOpenSubject;
|
|
124
|
+
readonly isOpen$: Observable<boolean>;
|
|
125
|
+
constructor();
|
|
126
|
+
/**
|
|
127
|
+
* Initialize the widget with configuration
|
|
128
|
+
*/
|
|
129
|
+
initialize(config: BubblaVWidgetConfig): void;
|
|
130
|
+
/**
|
|
131
|
+
* Setup event listeners for widget state changes
|
|
132
|
+
*/
|
|
133
|
+
private setupEventListeners;
|
|
134
|
+
/**
|
|
135
|
+
* Cleanup widget
|
|
136
|
+
*/
|
|
137
|
+
destroy(): void;
|
|
138
|
+
open(): void;
|
|
139
|
+
close(): void;
|
|
140
|
+
toggle(): void;
|
|
141
|
+
isOpen(): boolean;
|
|
142
|
+
sendMessage(text: string, conversationId?: string): void;
|
|
143
|
+
showGreeting(message?: string): void;
|
|
144
|
+
hideGreeting(): void;
|
|
145
|
+
getConfig(): Record<string, unknown>;
|
|
146
|
+
setDebug(enabled: boolean): void;
|
|
147
|
+
/**
|
|
148
|
+
* Listen to widget events
|
|
149
|
+
*/
|
|
150
|
+
on(event: string, callback: (...args: unknown[]) => void): void;
|
|
151
|
+
/**
|
|
152
|
+
* Unregister event listener
|
|
153
|
+
*/
|
|
154
|
+
off(event: string, callback: (...args: unknown[]) => void): void;
|
|
155
|
+
/**
|
|
156
|
+
* Emit event
|
|
157
|
+
*/
|
|
158
|
+
emit(event: string, data?: unknown): void;
|
|
159
|
+
/**
|
|
160
|
+
* Ready callback for widget loaded
|
|
161
|
+
*/
|
|
162
|
+
ready(callback: () => void): void;
|
|
163
|
+
/**
|
|
164
|
+
* Track analytics event
|
|
165
|
+
*/
|
|
166
|
+
track(eventName: string, properties?: Record<string, unknown>): void;
|
|
167
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<BubblaVWidgetService, never>;
|
|
168
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<BubblaVWidgetService>;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Utility functions for @bubblav/ai-chatbot-angular
|
|
173
|
+
*/
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Convert camelCase props to data-* attribute names
|
|
177
|
+
*/
|
|
178
|
+
declare function propsToDataAttributes(props: Record<string, unknown>): Record<string, string>;
|
|
179
|
+
/**
|
|
180
|
+
* Get the widget script URL
|
|
181
|
+
*/
|
|
182
|
+
declare function getWidgetScriptUrl(apiUrl?: string): string;
|
|
183
|
+
/**
|
|
184
|
+
* Validate website ID format
|
|
185
|
+
*/
|
|
186
|
+
declare function validateWebsiteId(websiteId: string): boolean;
|
|
187
|
+
/**
|
|
188
|
+
* Filter config to only include widget config (exclude websiteId)
|
|
189
|
+
*/
|
|
190
|
+
declare function getConfigProps(config: BubblaVWidgetConfig): Omit<BubblaVWidgetConfig, 'websiteId'>;
|
|
191
|
+
/**
|
|
192
|
+
* Check if we're in a browser environment
|
|
193
|
+
*/
|
|
194
|
+
declare function isBrowser(): boolean;
|
|
195
|
+
/**
|
|
196
|
+
* Check if a widget script is already loaded
|
|
197
|
+
*/
|
|
198
|
+
declare function isScriptLoaded(url: string): boolean;
|
|
199
|
+
|
|
200
|
+
export { BubblaVWidgetComponent, BubblaVWidgetService, getConfigProps, getWidgetScriptUrl, isBrowser, isScriptLoaded, propsToDataAttributes, validateWebsiteId };
|
|
201
|
+
export type { BubblaVSDK, BubblaVWidgetConfig, BubblaVWidgetSDK };
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bubblav/ai-chatbot-angular",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Angular component for embedding the BubblaV AI chatbot widget",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "ng-packagr -p ng-package.json",
|
|
21
|
+
"build:watch": "ng-packagr -p ng-package.json --watch",
|
|
22
|
+
"dev": "ng-packagr -p ng-package.json --watch",
|
|
23
|
+
"prepublishOnly": "npm run build",
|
|
24
|
+
"type-check": "tsc --noEmit"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"bubblav",
|
|
28
|
+
"chatbot",
|
|
29
|
+
"ai",
|
|
30
|
+
"widget",
|
|
31
|
+
"angular",
|
|
32
|
+
"angular-component"
|
|
33
|
+
],
|
|
34
|
+
"author": "BubblaV",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/tonnguyen/botcanchat.git",
|
|
39
|
+
"directory": "packages/ai-chatbot-angular"
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@angular/common": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
43
|
+
"@angular/core": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
|
|
44
|
+
"rxjs": "^6.5.0 || ^7.0.0"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@angular/common": "^21.1.5",
|
|
48
|
+
"@angular/compiler": "^21.1.5",
|
|
49
|
+
"@angular/compiler-cli": "^21.1.5",
|
|
50
|
+
"@angular/core": "^21.1.5",
|
|
51
|
+
"ng-packagr": "^21.1.0",
|
|
52
|
+
"rxjs": "^7.8.1",
|
|
53
|
+
"typescript": "~5.9.3"
|
|
54
|
+
},
|
|
55
|
+
"engines": {
|
|
56
|
+
"node": ">=18.0.0"
|
|
57
|
+
},
|
|
58
|
+
"sideEffects": false
|
|
59
|
+
}
|