@bliptarjs/sdk 0.1.0 → 0.2.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 +491 -54
- package/dist/index.cjs +16 -16
- package/dist/index.d.cts +26 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.js +16 -16
- package/package.json +11 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @bliptarjs/sdk
|
|
2
2
|
|
|
3
|
-
Lightweight SDK for collecting in-app user feedback. Designed to be small (~
|
|
3
|
+
Lightweight SDK for collecting in-app user feedback. Designed to be small (~7KB gzipped) and non-intrusive.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -8,123 +8,560 @@ Lightweight SDK for collecting in-app user feedback. Designed to be small (~5KB
|
|
|
8
8
|
npm install @bliptarjs/sdk
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
+
```bash
|
|
12
|
+
yarn add @bliptarjs/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pnpm add @bliptarjs/sdk
|
|
17
|
+
```
|
|
18
|
+
|
|
11
19
|
## Quick Start
|
|
12
20
|
|
|
13
21
|
```javascript
|
|
14
22
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
15
23
|
|
|
16
|
-
const
|
|
24
|
+
const bliptar = createBliptar({
|
|
17
25
|
apiKey: 'your-api-key',
|
|
18
26
|
});
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
bliptar.init();
|
|
21
29
|
```
|
|
22
30
|
|
|
23
|
-
|
|
31
|
+
That's it! The SDK will automatically track page views and display forms based on your campaign rules configured in the Bliptar dashboard.
|
|
24
32
|
|
|
25
|
-
|
|
33
|
+
## Core Concepts
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
Bliptar uses **campaigns** to determine when to show feedback forms. Campaigns can be triggered by:
|
|
36
|
+
|
|
37
|
+
- **Page View** - When a user visits a page
|
|
38
|
+
- **Button Click** - When a user clicks a specific element
|
|
39
|
+
- **Scroll Depth** - When a user scrolls to a certain percentage
|
|
40
|
+
- **Time on Page** - After a user spends time on a page
|
|
41
|
+
- **Exit Intent** - When a user is about to leave
|
|
42
|
+
- **Custom Events** - Triggered programmatically via SDK
|
|
43
|
+
|
|
44
|
+
## API Reference
|
|
45
|
+
|
|
46
|
+
### `createBliptar(config)`
|
|
47
|
+
|
|
48
|
+
Creates a new Bliptar instance.
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
28
51
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
29
52
|
|
|
30
|
-
const
|
|
53
|
+
const bliptar = createBliptar({
|
|
31
54
|
apiKey: 'your-api-key',
|
|
32
|
-
apiUrl: 'https://api.bliptar.com', // optional
|
|
33
|
-
debug: false, // optional
|
|
55
|
+
apiUrl: 'https://api.bliptar.com', // optional
|
|
56
|
+
debug: false, // optional
|
|
57
|
+
userId: 'user-123', // optional
|
|
58
|
+
userAttributes: { plan: 'pro' }, // optional
|
|
59
|
+
onShow: (form) => console.log('Showing form:', form.formId),
|
|
60
|
+
onSubmit: (answers) => console.log('Submitted:', answers),
|
|
61
|
+
onDismiss: () => console.log('Form dismissed'),
|
|
62
|
+
onError: (error) => console.error('Error:', error),
|
|
34
63
|
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
#### Configuration Options
|
|
67
|
+
|
|
68
|
+
| Option | Type | Default | Description |
|
|
69
|
+
|--------|------|---------|-------------|
|
|
70
|
+
| `apiKey` | `string` | **required** | Your Bliptar API key |
|
|
71
|
+
| `apiUrl` | `string` | `https://api.bliptar.com` | API endpoint URL |
|
|
72
|
+
| `debug` | `boolean` | `false` | Enable console logging for debugging |
|
|
73
|
+
| `userId` | `string` | - | Pre-set user ID for attribution |
|
|
74
|
+
| `userAttributes` | `object` | - | Pre-set user attributes for targeting |
|
|
75
|
+
| `onShow` | `function` | - | Callback when a form is displayed |
|
|
76
|
+
| `onSubmit` | `function` | - | Callback when a response is submitted |
|
|
77
|
+
| `onDismiss` | `function` | - | Callback when a form is dismissed |
|
|
78
|
+
| `onError` | `function` | - | Callback when an error occurs |
|
|
35
79
|
|
|
36
|
-
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### `init()`
|
|
83
|
+
|
|
84
|
+
Initializes the SDK. This sets up event listeners and triggers a `page_view` event.
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
bliptar.init();
|
|
37
88
|
```
|
|
38
89
|
|
|
39
|
-
|
|
90
|
+
> **Note:** Call this once after creating the instance, typically when your app loads.
|
|
91
|
+
|
|
92
|
+
---
|
|
40
93
|
|
|
41
|
-
|
|
94
|
+
### `identify(userId, attributes?)`
|
|
95
|
+
|
|
96
|
+
Associates feedback with a specific user. Call this when a user logs in.
|
|
42
97
|
|
|
43
98
|
```javascript
|
|
44
|
-
|
|
99
|
+
// Basic identification
|
|
100
|
+
bliptar.identify('user-123');
|
|
101
|
+
|
|
102
|
+
// With attributes for targeting
|
|
103
|
+
bliptar.identify('user-123', {
|
|
104
|
+
email: 'user@example.com',
|
|
45
105
|
plan: 'pro',
|
|
46
106
|
company: 'Acme Inc',
|
|
107
|
+
signupDate: '2024-01-15',
|
|
47
108
|
});
|
|
48
109
|
```
|
|
49
110
|
|
|
50
|
-
|
|
111
|
+
User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
|
|
112
|
+
|
|
113
|
+
---
|
|
51
114
|
|
|
52
|
-
|
|
115
|
+
### `track(event, data?)`
|
|
116
|
+
|
|
117
|
+
Tracks a custom event that may trigger a feedback form.
|
|
53
118
|
|
|
54
119
|
```javascript
|
|
55
|
-
|
|
120
|
+
// Simple event
|
|
121
|
+
bliptar.track('checkout_complete');
|
|
122
|
+
|
|
123
|
+
// With event data
|
|
124
|
+
bliptar.track('checkout_complete', {
|
|
56
125
|
orderId: 'order-456',
|
|
57
126
|
total: 99.99,
|
|
127
|
+
items: 3,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Feature usage
|
|
131
|
+
bliptar.track('feature_used', {
|
|
132
|
+
feature: 'export_pdf',
|
|
133
|
+
duration: 5000,
|
|
58
134
|
});
|
|
59
135
|
```
|
|
60
136
|
|
|
61
|
-
|
|
137
|
+
Configure campaigns in the dashboard to trigger forms based on these events.
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### `trackClicks(options)`
|
|
62
142
|
|
|
63
|
-
|
|
143
|
+
Sets up click tracking on specific elements. When a user clicks a matching element, it fires a `button_click` event that can trigger campaigns.
|
|
64
144
|
|
|
65
145
|
```javascript
|
|
66
|
-
|
|
146
|
+
// Track clicks on a specific button
|
|
147
|
+
bliptar.trackClicks({
|
|
148
|
+
selector: '#upgrade-button',
|
|
149
|
+
name: 'upgrade-cta',
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Track clicks on all pricing buttons
|
|
153
|
+
bliptar.trackClicks({
|
|
154
|
+
selector: '.pricing-cta',
|
|
155
|
+
name: 'pricing-click',
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Track only the first click
|
|
159
|
+
bliptar.trackClicks({
|
|
160
|
+
selector: '.signup-button',
|
|
161
|
+
name: 'signup-click',
|
|
162
|
+
once: true,
|
|
163
|
+
});
|
|
67
164
|
```
|
|
68
165
|
|
|
69
|
-
|
|
166
|
+
#### Options
|
|
167
|
+
|
|
168
|
+
| Option | Type | Default | Description |
|
|
169
|
+
|--------|------|---------|-------------|
|
|
170
|
+
| `selector` | `string` | **required** | CSS selector for elements to track |
|
|
171
|
+
| `name` | `string` | `selector` | Friendly name for the click target |
|
|
172
|
+
| `once` | `boolean` | `false` | Only track the first click per session |
|
|
173
|
+
|
|
174
|
+
#### Event Data Sent
|
|
175
|
+
|
|
176
|
+
When a click is detected, the following data is sent:
|
|
70
177
|
|
|
71
178
|
```javascript
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
179
|
+
{
|
|
180
|
+
selector: '.pricing-cta',
|
|
181
|
+
name: 'pricing-click',
|
|
182
|
+
element_text: 'Get Started',
|
|
183
|
+
element_id: 'cta-btn',
|
|
184
|
+
element_class: 'pricing-cta btn-primary'
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
### `enableExitIntent(options?)`
|
|
191
|
+
|
|
192
|
+
Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
// Basic usage (triggers once per session)
|
|
196
|
+
bliptar.enableExitIntent();
|
|
197
|
+
|
|
198
|
+
// Custom configuration
|
|
199
|
+
bliptar.enableExitIntent({
|
|
200
|
+
threshold: 50, // Pixels from top of viewport
|
|
201
|
+
cooldown: 10000, // Ms before can trigger again
|
|
202
|
+
once: true, // Only trigger once per session
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### Options
|
|
207
|
+
|
|
208
|
+
| Option | Type | Default | Description |
|
|
209
|
+
|--------|------|---------|-------------|
|
|
210
|
+
| `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
|
|
211
|
+
| `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
|
|
212
|
+
| `once` | `boolean` | `true` | Only trigger once per session |
|
|
213
|
+
|
|
214
|
+
#### How It Works
|
|
215
|
+
|
|
216
|
+
- **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
|
|
217
|
+
- **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
### `showForm(formId)`
|
|
222
|
+
|
|
223
|
+
Manually displays a specific form, bypassing campaign rules.
|
|
224
|
+
|
|
225
|
+
```javascript
|
|
226
|
+
// Show a form programmatically
|
|
227
|
+
await bliptar.showForm('form_abc123');
|
|
228
|
+
|
|
229
|
+
// Example: Show after completing onboarding
|
|
230
|
+
function completeOnboarding() {
|
|
231
|
+
// ... onboarding logic
|
|
232
|
+
bliptar.showForm('form_onboarding_feedback');
|
|
233
|
+
}
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
### `destroy()`
|
|
239
|
+
|
|
240
|
+
Removes all event listeners and cleans up the SDK. Call this when unmounting your app or when done collecting feedback.
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
bliptar.destroy();
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Usage Examples
|
|
249
|
+
|
|
250
|
+
### Basic Setup
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
254
|
+
|
|
255
|
+
const bliptar = createBliptar({
|
|
256
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
bliptar.init();
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### With User Identification
|
|
263
|
+
|
|
264
|
+
```javascript
|
|
265
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
266
|
+
|
|
267
|
+
const bliptar = createBliptar({
|
|
268
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
bliptar.init();
|
|
272
|
+
|
|
273
|
+
// When user logs in
|
|
274
|
+
function onUserLogin(user) {
|
|
275
|
+
bliptar.identify(user.id, {
|
|
276
|
+
email: user.email,
|
|
277
|
+
plan: user.subscription.plan,
|
|
278
|
+
createdAt: user.createdAt,
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### E-commerce Checkout Feedback
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
287
|
+
|
|
288
|
+
const bliptar = createBliptar({
|
|
289
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
77
290
|
onSubmit: (answers) => {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
onDismiss: () => {
|
|
81
|
-
console.log('Form dismissed');
|
|
82
|
-
},
|
|
83
|
-
onError: (error) => {
|
|
84
|
-
console.error('Error:', error);
|
|
291
|
+
// Send to your analytics
|
|
292
|
+
analytics.track('feedback_submitted', answers);
|
|
85
293
|
},
|
|
86
294
|
});
|
|
295
|
+
|
|
296
|
+
bliptar.init();
|
|
297
|
+
|
|
298
|
+
// Track successful purchase
|
|
299
|
+
function onPurchaseComplete(order) {
|
|
300
|
+
bliptar.track('purchase_complete', {
|
|
301
|
+
orderId: order.id,
|
|
302
|
+
total: order.total,
|
|
303
|
+
itemCount: order.items.length,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
87
306
|
```
|
|
88
307
|
|
|
89
|
-
###
|
|
308
|
+
### Exit Intent Survey
|
|
309
|
+
|
|
310
|
+
```javascript
|
|
311
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
312
|
+
|
|
313
|
+
const bliptar = createBliptar({
|
|
314
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
bliptar.init();
|
|
90
318
|
|
|
91
|
-
|
|
319
|
+
// Enable exit intent on pricing page
|
|
320
|
+
if (window.location.pathname === '/pricing') {
|
|
321
|
+
bliptar.enableExitIntent({
|
|
322
|
+
once: true, // Only show once
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Button Click Feedback
|
|
92
328
|
|
|
93
329
|
```javascript
|
|
94
|
-
|
|
330
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
331
|
+
|
|
332
|
+
const bliptar = createBliptar({
|
|
333
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
bliptar.init();
|
|
337
|
+
|
|
338
|
+
// Track clicks on upgrade buttons
|
|
339
|
+
bliptar.trackClicks({
|
|
340
|
+
selector: '[data-track="upgrade"]',
|
|
341
|
+
name: 'upgrade-button',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
// Track clicks on help buttons
|
|
345
|
+
bliptar.trackClicks({
|
|
346
|
+
selector: '.help-button',
|
|
347
|
+
name: 'help-click',
|
|
348
|
+
});
|
|
95
349
|
```
|
|
96
350
|
|
|
97
|
-
|
|
351
|
+
### React Integration
|
|
98
352
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
353
|
+
```jsx
|
|
354
|
+
import { useEffect } from 'react';
|
|
355
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
356
|
+
|
|
357
|
+
const bliptar = createBliptar({
|
|
358
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
function App() {
|
|
362
|
+
useEffect(() => {
|
|
363
|
+
bliptar.init();
|
|
364
|
+
|
|
365
|
+
return () => {
|
|
366
|
+
bliptar.destroy();
|
|
367
|
+
};
|
|
368
|
+
}, []);
|
|
369
|
+
|
|
370
|
+
return <div>Your app</div>;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// In a component that needs to identify users
|
|
374
|
+
function UserProfile({ user }) {
|
|
375
|
+
useEffect(() => {
|
|
376
|
+
if (user) {
|
|
377
|
+
bliptar.identify(user.id, {
|
|
378
|
+
email: user.email,
|
|
379
|
+
plan: user.plan,
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
}, [user]);
|
|
383
|
+
|
|
384
|
+
return <div>Welcome, {user.name}</div>;
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Next.js Integration
|
|
389
|
+
|
|
390
|
+
```jsx
|
|
391
|
+
// lib/bliptar.js
|
|
392
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
393
|
+
|
|
394
|
+
export const bliptar = typeof window !== 'undefined'
|
|
395
|
+
? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_API_KEY })
|
|
396
|
+
: null;
|
|
397
|
+
|
|
398
|
+
// app/layout.js or pages/_app.js
|
|
399
|
+
'use client';
|
|
400
|
+
|
|
401
|
+
import { useEffect } from 'react';
|
|
402
|
+
import { bliptar } from '@/lib/bliptar';
|
|
403
|
+
|
|
404
|
+
export default function RootLayout({ children }) {
|
|
405
|
+
useEffect(() => {
|
|
406
|
+
bliptar?.init();
|
|
407
|
+
|
|
408
|
+
return () => {
|
|
409
|
+
bliptar?.destroy();
|
|
410
|
+
};
|
|
411
|
+
}, []);
|
|
412
|
+
|
|
413
|
+
return (
|
|
414
|
+
<html>
|
|
415
|
+
<body>{children}</body>
|
|
416
|
+
</html>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### Vue Integration
|
|
422
|
+
|
|
423
|
+
```vue
|
|
424
|
+
<script setup>
|
|
425
|
+
import { onMounted, onUnmounted } from 'vue';
|
|
426
|
+
import { createBliptar } from '@bliptarjs/sdk';
|
|
427
|
+
|
|
428
|
+
const bliptar = createBliptar({
|
|
429
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
onMounted(() => {
|
|
433
|
+
bliptar.init();
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
onUnmounted(() => {
|
|
437
|
+
bliptar.destroy();
|
|
438
|
+
});
|
|
439
|
+
</script>
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Automatic Tracking
|
|
445
|
+
|
|
446
|
+
The SDK automatically tracks the following without any additional code:
|
|
447
|
+
|
|
448
|
+
| Data | Description |
|
|
449
|
+
|------|-------------|
|
|
450
|
+
| `page_view` | Fired on `init()` |
|
|
451
|
+
| `scroll_depth` | Percentage scrolled (0-100%) |
|
|
452
|
+
| `time_on_page` | Seconds spent on page |
|
|
453
|
+
| `device_type` | `desktop`, `mobile`, or `tablet` |
|
|
454
|
+
| `page_url` | Current page URL |
|
|
455
|
+
| `referrer` | Previous page URL |
|
|
456
|
+
|
|
457
|
+
This data is sent with every trigger request and can be used in campaign conditions.
|
|
458
|
+
|
|
459
|
+
---
|
|
106
460
|
|
|
107
461
|
## Form Types
|
|
108
462
|
|
|
109
463
|
The SDK supports multiple feedback form types:
|
|
110
464
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
465
|
+
| Type | Description |
|
|
466
|
+
|------|-------------|
|
|
467
|
+
| **Binary** | Thumbs up/down, Yes/No questions |
|
|
468
|
+
| **Star Rating** | 1-5 star ratings with optional half stars |
|
|
469
|
+
| **Emoji Scale** | 3 or 5 point emoji scales |
|
|
470
|
+
| **NPS** | Net Promoter Score (0-10) |
|
|
471
|
+
| **Single Choice** | Multiple choice questions |
|
|
472
|
+
| **Text Input** | Free-form text responses |
|
|
473
|
+
|
|
474
|
+
Forms are configured in the Bliptar dashboard and automatically rendered by the SDK.
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## TypeScript Support
|
|
479
|
+
|
|
480
|
+
The SDK is written in TypeScript and includes full type definitions.
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
import {
|
|
484
|
+
createBliptar,
|
|
485
|
+
BliptarInstance,
|
|
486
|
+
BliptarConfig,
|
|
487
|
+
ClickTrackingOptions,
|
|
488
|
+
ExitIntentOptions,
|
|
489
|
+
} from '@bliptarjs/sdk';
|
|
490
|
+
|
|
491
|
+
const config: BliptarConfig = {
|
|
492
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
493
|
+
debug: true,
|
|
494
|
+
onSubmit: (answers) => {
|
|
495
|
+
console.log(answers);
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
const bliptar: BliptarInstance = createBliptar(config);
|
|
500
|
+
|
|
501
|
+
// Type-safe click tracking
|
|
502
|
+
const clickOptions: ClickTrackingOptions = {
|
|
503
|
+
selector: '.cta-button',
|
|
504
|
+
name: 'main-cta',
|
|
505
|
+
once: true,
|
|
506
|
+
};
|
|
507
|
+
bliptar.trackClicks(clickOptions);
|
|
508
|
+
|
|
509
|
+
// Type-safe exit intent
|
|
510
|
+
const exitOptions: ExitIntentOptions = {
|
|
511
|
+
threshold: 50,
|
|
512
|
+
once: true,
|
|
513
|
+
};
|
|
514
|
+
bliptar.enableExitIntent(exitOptions);
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
---
|
|
117
518
|
|
|
118
519
|
## Browser Support
|
|
119
520
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
521
|
+
| Browser | Supported |
|
|
522
|
+
|---------|-----------|
|
|
523
|
+
| Chrome | Latest 2 versions |
|
|
524
|
+
| Firefox | Latest 2 versions |
|
|
525
|
+
| Safari | Latest 2 versions |
|
|
526
|
+
| Edge | Latest 2 versions |
|
|
527
|
+
|
|
528
|
+
---
|
|
124
529
|
|
|
125
530
|
## Bundle Size
|
|
126
531
|
|
|
127
|
-
~
|
|
532
|
+
- **ESM:** ~25KB (minified)
|
|
533
|
+
- **Gzipped:** ~7KB
|
|
534
|
+
|
|
535
|
+
The SDK has zero dependencies.
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
539
|
+
## Troubleshooting
|
|
540
|
+
|
|
541
|
+
### Forms not showing?
|
|
542
|
+
|
|
543
|
+
1. **Check your API key** - Ensure it's valid and for the correct environment
|
|
544
|
+
2. **Check campaign rules** - Verify your campaign is active and targeting rules match
|
|
545
|
+
3. **Enable debug mode** - Set `debug: true` to see console logs
|
|
546
|
+
4. **Check throttling** - Campaigns have frequency limits to avoid annoying users
|
|
547
|
+
|
|
548
|
+
### Debug Mode
|
|
549
|
+
|
|
550
|
+
```javascript
|
|
551
|
+
const bliptar = createBliptar({
|
|
552
|
+
apiKey: 'blip_live_xxxxxxxx',
|
|
553
|
+
debug: true, // Enables console logging
|
|
554
|
+
});
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
This will log:
|
|
558
|
+
- SDK initialization
|
|
559
|
+
- Events being tracked
|
|
560
|
+
- Form display triggers
|
|
561
|
+
- API responses
|
|
562
|
+
- Errors
|
|
563
|
+
|
|
564
|
+
---
|
|
128
565
|
|
|
129
566
|
## License
|
|
130
567
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var v=Object.defineProperty;var S=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var A=(i,e)=>{for(var t in e)v(i,t,{get:e[t],enumerable:!0})},B=(i,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of L(e))!P.call(i,o)&&o!==t&&v(i,o,{get:()=>e[o],enumerable:!(r=S(e,o))||r.enumerable});return i};var j=i=>B(v({},"__esModule",{value:!0}),i);var Y={};A(Y,{Bliptar:()=>b,createBliptar:()=>k});module.exports=j(Y);var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},h={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function D(i,e){try{let r=window.getComputedStyle(i).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function H(i){try{return getComputedStyle(document.documentElement).getPropertyValue(i).trim()||null}catch{return null}}function C(i){let e=i.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=i.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function M(i,e,t){let[r,o,a]=[i,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*o+.0722*a}function _(i){let e=C(i);return e?M(e.r,e.g,e.b)<.5:!1}function f(i,e){let t=C(i);if(!t)return i;let r=o=>Math.min(255,Math.max(0,Math.round(o+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function z(i){return _(i)?"#ffffff":"#1a1a1a"}function R(i,e){let t={},r=null;i&&(r=document.querySelector(i)),r||(r=document.body);let o=D(r,"background-color");if(o){let a=_(o);t.mode=a?"dark":"light",t.backgroundColor=o,t.surfaceColor=f(o,a?10:-5),t.textPrimary=z(o),t.textSecondary=f(t.textPrimary,a?-30:30),t.borderColor=f(o,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let p=H(n);p&&(t[l]=p)}}return t.primaryColor&&(t.primaryHover=f(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function w(i){let{theme:e,customTheme:t,adaptiveOptions:r}=i;if(e==="custom"&&t)return{mode:_(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let o=r||{fallback:"light",autoContrast:!0},a=R(o.sourceElement,o.inheritCssVars);return a?{...a.mode==="dark"?h:u,...a}:o.fallback==="dark"?h:u}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?h:u:e==="dark"?h:u}function $(i){return`
|
|
2
2
|
--bliptar-bg: ${i.backgroundColor};
|
|
3
3
|
--bliptar-surface: ${i.surfaceColor};
|
|
4
4
|
--bliptar-text: ${i.textPrimary};
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
--bliptar-border: ${i.borderColor};
|
|
9
9
|
--bliptar-shadow: ${i.shadowColor};
|
|
10
10
|
--bliptar-star: ${i.starColor};
|
|
11
|
-
`}var g=class{constructor(
|
|
12
|
-
:root { ${
|
|
11
|
+
`}var g=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=w(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
|
|
12
|
+
:root { ${$(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
15
15
|
position: fixed;
|
|
@@ -28,19 +28,19 @@
|
|
|
28
28
|
inset: 0;
|
|
29
29
|
background: rgba(0, 0, 0, 0.5);
|
|
30
30
|
animation: bliptar-fade-in 0.2s ease-out;
|
|
31
|
-
${
|
|
31
|
+
${e.backdropBlur?"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);":""}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.bliptar__card {
|
|
35
35
|
background: ${t.backgroundColor};
|
|
36
36
|
color: ${t.textPrimary};
|
|
37
|
-
border-radius: ${
|
|
37
|
+
border-radius: ${e.borderRadius}px;
|
|
38
38
|
box-shadow: 0 8px 32px ${t.shadowColor}, 0 2px 8px ${t.shadowColor};
|
|
39
|
-
max-width: ${
|
|
39
|
+
max-width: ${e.maxWidth}px;
|
|
40
40
|
width: calc(100vw - 40px);
|
|
41
41
|
padding: 24px;
|
|
42
42
|
position: relative;
|
|
43
|
-
${this.getAnimationStyles(
|
|
43
|
+
${this.getAnimationStyles(r)}
|
|
44
44
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
.bliptar__card--exit {
|
|
52
|
-
animation: bliptar-${
|
|
52
|
+
animation: bliptar-${r}-out 0.2s ease-in forwards;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
@keyframes bliptar-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -372,20 +372,20 @@
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
@media print { .bliptar { display: none !important; } }
|
|
375
|
-
`}getAnimationStyles(
|
|
375
|
+
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,o=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
|
|
376
376
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
377
377
|
<path d="M1 1l12 12M1 13L13 1"/>
|
|
378
378
|
</svg>
|
|
379
|
-
</button>`),
|
|
380
|
-
<button class="bliptar__btn${
|
|
381
|
-
${
|
|
379
|
+
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(o.question)}</div>`,n+=this.renderAnswerOptions(o),(!r.autoAdvance||o.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
|
|
380
|
+
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
|
+
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
|
-
<button class="bliptar__btn${
|
|
384
|
-
${
|
|
383
|
+
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
|
+
${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
|
|
385
385
|
</button>
|
|
386
|
-
</div>`;case"star-rating":let
|
|
386
|
+
</div>`;case"star-rating":let o='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)o+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return o+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,p='<div class="bliptar__emojis">';return l.forEach((s,d)=>{p+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),p+"</div>";case"nps":let c='<div class="bliptar__nps">';for(let s=0;s<=10;s++)c+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return c+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,c;case"single-choice":let m='<div class="bliptar__options">';return e.options.forEach(s=>{m+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),m+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let o=this.container.querySelector(".bliptar__backdrop");o&&t.allowDismiss&&o.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((x,I)=>x.classList.toggle("bliptar__star--active",I<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,x)=>d.classList.toggle("bliptar__star--active",s?x<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let p=this.container.querySelector(".bliptar__textarea");p&&(p.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=p.value}),setTimeout(()=>p.focus(),100));let c=this.container.querySelector(".bliptar__nav-btn--back");c&&c.addEventListener("click",()=>this.goBack());let m=this.container.querySelector(".bliptar__nav-btn--next");m&&m.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
|
|
387
387
|
<div class="bliptar__thankyou">
|
|
388
388
|
<div class="bliptar__thankyou-emoji">\u{1F389}</div>
|
|
389
389
|
<div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
|
|
390
390
|
</div>
|
|
391
|
-
</div>`,this.container.innerHTML=
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function T(){let i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=i.charAt(Math.floor(Math.random()*i.length));return`sess_${e}`}function y(){let i=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(i)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(i)?"mobile":"desktop"}function E(i,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>i(...r),e)}}var O="https://api.bliptar.com",b=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.config={apiUrl:O,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK");let e=E(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,o=t.scrollHeight-t.clientHeight;this.scrollDepth=o>0?r/o:0},100);this.addEventListener(window,"scroll",e),this.track("page_view")}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let o=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!o.ok)throw new Error(`API error: ${o.status}`);let a=await o.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(o){this.handleError(o)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:o=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let c=l.target.closest(t);c&&(o&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),o&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:c.textContent?.trim().slice(0,100)||"",element_id:c.id||null,element_class:c.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:o=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||o&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),o||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(o&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};y()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new g(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let o=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:o,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:y()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=T(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function k(i){return new b(i)}if(typeof window<"u"){let i=document.currentScript;if(i?.dataset.apiKey){let e=k({apiKey:i.dataset.apiKey,debug:i.dataset.debug==="true"});window.bliptar=e,e.init()}}0&&(module.exports={Bliptar,createBliptar});
|
package/dist/index.d.cts
CHANGED
|
@@ -9,11 +9,31 @@ interface BliptarConfig {
|
|
|
9
9
|
onDismiss?: () => void;
|
|
10
10
|
onError?: (error: Error) => void;
|
|
11
11
|
}
|
|
12
|
+
interface ClickTrackingOptions {
|
|
13
|
+
/** CSS selector for elements to track */
|
|
14
|
+
selector: string;
|
|
15
|
+
/** Optional name for this click target (defaults to selector) */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Only track once per session */
|
|
18
|
+
once?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface ExitIntentOptions {
|
|
21
|
+
/** Sensitivity threshold in pixels from top of viewport (default: 50) */
|
|
22
|
+
threshold?: number;
|
|
23
|
+
/** Delay before exit intent can trigger again in ms (default: 10000) */
|
|
24
|
+
cooldown?: number;
|
|
25
|
+
/** Only trigger once per session (default: true) */
|
|
26
|
+
once?: boolean;
|
|
27
|
+
}
|
|
12
28
|
interface BliptarInstance {
|
|
13
29
|
init: () => void;
|
|
14
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
15
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
16
32
|
showForm: (formId: string) => Promise<void>;
|
|
33
|
+
/** Track clicks on elements matching the selector */
|
|
34
|
+
trackClicks: (options: ClickTrackingOptions) => void;
|
|
35
|
+
/** Enable exit intent detection */
|
|
36
|
+
enableExitIntent: (options?: ExitIntentOptions) => void;
|
|
17
37
|
destroy: () => void;
|
|
18
38
|
}
|
|
19
39
|
interface Form {
|
|
@@ -110,11 +130,16 @@ declare class Bliptar implements BliptarInstance {
|
|
|
110
130
|
private scrollDepth;
|
|
111
131
|
private isInitialized;
|
|
112
132
|
private eventListeners;
|
|
133
|
+
private trackedClicks;
|
|
134
|
+
private exitIntentTriggered;
|
|
135
|
+
private exitIntentCooldownTimer;
|
|
113
136
|
constructor(config: BliptarConfig);
|
|
114
137
|
init(): void;
|
|
115
138
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
116
139
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
117
140
|
showForm(formId: string): Promise<void>;
|
|
141
|
+
trackClicks(options: ClickTrackingOptions): void;
|
|
142
|
+
enableExitIntent(options?: ExitIntentOptions): void;
|
|
118
143
|
destroy(): void;
|
|
119
144
|
private displayForm;
|
|
120
145
|
private submitResponse;
|
|
@@ -148,4 +173,4 @@ declare class Bliptar implements BliptarInstance {
|
|
|
148
173
|
*/
|
|
149
174
|
declare function createBliptar(config: BliptarConfig): BliptarInstance;
|
|
150
175
|
|
|
151
|
-
export { type Answer, Bliptar, type BliptarConfig, type BliptarInstance, type Form, type Question, createBliptar };
|
|
176
|
+
export { type Answer, Bliptar, type BliptarConfig, type BliptarInstance, type ClickTrackingOptions, type ExitIntentOptions, type Form, type Question, createBliptar };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,11 +9,31 @@ interface BliptarConfig {
|
|
|
9
9
|
onDismiss?: () => void;
|
|
10
10
|
onError?: (error: Error) => void;
|
|
11
11
|
}
|
|
12
|
+
interface ClickTrackingOptions {
|
|
13
|
+
/** CSS selector for elements to track */
|
|
14
|
+
selector: string;
|
|
15
|
+
/** Optional name for this click target (defaults to selector) */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** Only track once per session */
|
|
18
|
+
once?: boolean;
|
|
19
|
+
}
|
|
20
|
+
interface ExitIntentOptions {
|
|
21
|
+
/** Sensitivity threshold in pixels from top of viewport (default: 50) */
|
|
22
|
+
threshold?: number;
|
|
23
|
+
/** Delay before exit intent can trigger again in ms (default: 10000) */
|
|
24
|
+
cooldown?: number;
|
|
25
|
+
/** Only trigger once per session (default: true) */
|
|
26
|
+
once?: boolean;
|
|
27
|
+
}
|
|
12
28
|
interface BliptarInstance {
|
|
13
29
|
init: () => void;
|
|
14
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
15
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
16
32
|
showForm: (formId: string) => Promise<void>;
|
|
33
|
+
/** Track clicks on elements matching the selector */
|
|
34
|
+
trackClicks: (options: ClickTrackingOptions) => void;
|
|
35
|
+
/** Enable exit intent detection */
|
|
36
|
+
enableExitIntent: (options?: ExitIntentOptions) => void;
|
|
17
37
|
destroy: () => void;
|
|
18
38
|
}
|
|
19
39
|
interface Form {
|
|
@@ -110,11 +130,16 @@ declare class Bliptar implements BliptarInstance {
|
|
|
110
130
|
private scrollDepth;
|
|
111
131
|
private isInitialized;
|
|
112
132
|
private eventListeners;
|
|
133
|
+
private trackedClicks;
|
|
134
|
+
private exitIntentTriggered;
|
|
135
|
+
private exitIntentCooldownTimer;
|
|
113
136
|
constructor(config: BliptarConfig);
|
|
114
137
|
init(): void;
|
|
115
138
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
116
139
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
117
140
|
showForm(formId: string): Promise<void>;
|
|
141
|
+
trackClicks(options: ClickTrackingOptions): void;
|
|
142
|
+
enableExitIntent(options?: ExitIntentOptions): void;
|
|
118
143
|
destroy(): void;
|
|
119
144
|
private displayForm;
|
|
120
145
|
private submitResponse;
|
|
@@ -148,4 +173,4 @@ declare class Bliptar implements BliptarInstance {
|
|
|
148
173
|
*/
|
|
149
174
|
declare function createBliptar(config: BliptarConfig): BliptarInstance;
|
|
150
175
|
|
|
151
|
-
export { type Answer, Bliptar, type BliptarConfig, type BliptarInstance, type Form, type Question, createBliptar };
|
|
176
|
+
export { type Answer, Bliptar, type BliptarConfig, type BliptarInstance, type ClickTrackingOptions, type ExitIntentOptions, type Form, type Question, createBliptar };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},h={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function
|
|
1
|
+
var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary:"#1a1a1a",textSecondary:"#666666",primaryColor:"#3b82f6",primaryHover:"#2563eb",borderColor:"#e0e0e0",shadowColor:"rgba(0, 0, 0, 0.15)",starColor:"#fbbf24"},h={mode:"dark",backgroundColor:"#1a1a1a",surfaceColor:"#2a2a2a",textPrimary:"#ffffff",textSecondary:"#a0a0a0",primaryColor:"#3b82f6",primaryHover:"#60a5fa",borderColor:"#333333",shadowColor:"rgba(0, 0, 0, 0.4)",starColor:"#fbbf24"};function I(i,e){try{let r=window.getComputedStyle(i).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function S(i){try{return getComputedStyle(document.documentElement).getPropertyValue(i).trim()||null}catch{return null}}function y(i){let e=i.match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i);if(e)return{r:parseInt(e[1],16),g:parseInt(e[2],16),b:parseInt(e[3],16)};let t=i.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)/);return t?{r:parseInt(t[1],10),g:parseInt(t[2],10),b:parseInt(t[3],10)}:null}function L(i,e,t){let[r,o,a]=[i,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*o+.0722*a}function v(i){let e=y(i);return e?L(e.r,e.g,e.b)<.5:!1}function f(i,e){let t=y(i);if(!t)return i;let r=o=>Math.min(255,Math.max(0,Math.round(o+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function P(i){return v(i)?"#ffffff":"#1a1a1a"}function A(i,e){let t={},r=null;i&&(r=document.querySelector(i)),r||(r=document.body);let o=I(r,"background-color");if(o){let a=v(o);t.mode=a?"dark":"light",t.backgroundColor=o,t.surfaceColor=f(o,a?10:-5),t.textPrimary=P(o),t.textSecondary=f(t.textPrimary,a?-30:30),t.borderColor=f(o,a?20:-15),t.shadowColor=a?"rgba(0, 0, 0, 0.4)":"rgba(0, 0, 0, 0.15)"}if(e){let a={"--primary":"primaryColor","--primary-color":"primaryColor","--accent":"primaryColor","--accent-color":"primaryColor","--brand":"primaryColor","--brand-color":"primaryColor","--color-primary":"primaryColor","--background":"backgroundColor","--bg":"backgroundColor","--bg-color":"backgroundColor","--text":"textPrimary","--text-color":"textPrimary","--foreground":"textPrimary","--border":"borderColor","--border-color":"borderColor"};for(let[n,l]of Object.entries(a)){let p=S(n);p&&(t[l]=p)}}return t.primaryColor&&(t.primaryHover=f(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function k(i){let{theme:e,customTheme:t,adaptiveOptions:r}=i;if(e==="custom"&&t)return{mode:v(t.backgroundColor)?"dark":"light",backgroundColor:t.backgroundColor,surfaceColor:t.surfaceColor,textPrimary:t.textPrimary,textSecondary:t.textSecondary,primaryColor:t.primaryColor,primaryHover:t.primaryHover,borderColor:t.borderColor,shadowColor:t.shadowColor,starColor:"#fbbf24"};if(e==="adaptive"){let o=r||{fallback:"light",autoContrast:!0},a=A(o.sourceElement,o.inheritCssVars);return a?{...a.mode==="dark"?h:u,...a}:o.fallback==="dark"?h:u}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?h:u:e==="dark"?h:u}function C(i){return`
|
|
2
2
|
--bliptar-bg: ${i.backgroundColor};
|
|
3
3
|
--bliptar-surface: ${i.surfaceColor};
|
|
4
4
|
--bliptar-text: ${i.textPrimary};
|
|
@@ -8,8 +8,8 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
|
|
|
8
8
|
--bliptar-border: ${i.borderColor};
|
|
9
9
|
--bliptar-shadow: ${i.shadowColor};
|
|
10
10
|
--bliptar-star: ${i.starColor};
|
|
11
|
-
`}var g=class{constructor(
|
|
12
|
-
:root { ${
|
|
11
|
+
`}var g=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=k(e.appearance)}render(){this.createContainer(),this.injectStyles(),this.renderQuestion()}destroy(){if(this.container){let e=this.container.querySelector(".bliptar__card");e?(e.classList.add("bliptar__card--exit"),setTimeout(()=>{this.container?.remove(),this.container=null},200)):(this.container.remove(),this.container=null)}}createContainer(){this.container=document.createElement("div"),this.container.id="bliptar-form",this.container.className=`bliptar bliptar--${this.form.appearance.position}`,document.body.appendChild(this.container)}injectStyles(){let e=document.getElementById("bliptar-styles");e&&e.remove();let t=document.createElement("style");t.id="bliptar-styles",t.textContent=this.getStyles(),document.head.appendChild(t)}getStyles(){let{appearance:e}=this.form,t=this.theme,r=e.animation||"slide-up";return`
|
|
12
|
+
:root { ${C(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
15
15
|
position: fixed;
|
|
@@ -28,19 +28,19 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
|
|
|
28
28
|
inset: 0;
|
|
29
29
|
background: rgba(0, 0, 0, 0.5);
|
|
30
30
|
animation: bliptar-fade-in 0.2s ease-out;
|
|
31
|
-
${
|
|
31
|
+
${e.backdropBlur?"backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);":""}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
.bliptar__card {
|
|
35
35
|
background: ${t.backgroundColor};
|
|
36
36
|
color: ${t.textPrimary};
|
|
37
|
-
border-radius: ${
|
|
37
|
+
border-radius: ${e.borderRadius}px;
|
|
38
38
|
box-shadow: 0 8px 32px ${t.shadowColor}, 0 2px 8px ${t.shadowColor};
|
|
39
|
-
max-width: ${
|
|
39
|
+
max-width: ${e.maxWidth}px;
|
|
40
40
|
width: calc(100vw - 40px);
|
|
41
41
|
padding: 24px;
|
|
42
42
|
position: relative;
|
|
43
|
-
${this.getAnimationStyles(
|
|
43
|
+
${this.getAnimationStyles(r)}
|
|
44
44
|
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -49,7 +49,7 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
|
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
.bliptar__card--exit {
|
|
52
|
-
animation: bliptar-${
|
|
52
|
+
animation: bliptar-${r}-out 0.2s ease-in forwards;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
@keyframes bliptar-fade-in { from { opacity: 0; } to { opacity: 1; } }
|
|
@@ -372,20 +372,20 @@ var u={mode:"light",backgroundColor:"#ffffff",surfaceColor:"#f5f5f5",textPrimary
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
@media print { .bliptar { display: none !important; } }
|
|
375
|
-
`}getAnimationStyles(
|
|
375
|
+
`}getAnimationStyles(e){switch(e){case"slide-up":return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);";case"fade":return"animation: bliptar-fade 0.3s ease-out;";case"scale":return"animation: bliptar-scale 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);";case"none":return"";default:return"animation: bliptar-slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1);"}}renderQuestion(){if(!this.container)return;let{questions:e,appearance:t,behavior:r}=this.form,o=e[this.currentIndex],a=this.currentIndex===e.length-1,n="";if(t.backdrop&&(n+='<div class="bliptar__backdrop"></div>'),n+='<div class="bliptar__card">',r.allowDismiss&&(n+=`<button class="bliptar__close" aria-label="Close">
|
|
376
376
|
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
|
|
377
377
|
<path d="M1 1l12 12M1 13L13 1"/>
|
|
378
378
|
</svg>
|
|
379
|
-
</button>`),
|
|
380
|
-
<button class="bliptar__btn${
|
|
381
|
-
${
|
|
379
|
+
</button>`),r.showProgress&&e.length>1){n+='<div class="bliptar__progress">';for(let l=0;l<e.length;l++)n+=`<div class="bliptar__progress-bar${l<=this.currentIndex?" bliptar__progress-bar--active":""}"></div>`;n+="</div>"}n+=`<div class="bliptar__question">${this.escapeHtml(o.question)}</div>`,n+=this.renderAnswerOptions(o),(!r.autoAdvance||o.type==="text-input")&&(n+='<div class="bliptar__nav">',n+=this.currentIndex>0?'<button class="bliptar__nav-btn bliptar__nav-btn--back">Back</button>':"<div></div>",n+=`<button class="bliptar__nav-btn bliptar__nav-btn--next">${a?"Submit":"Continue"}</button>`,n+="</div>"),n+="</div>",this.container.innerHTML=n,this.attachEventListeners()}escapeHtml(e){let t=document.createElement("div");return t.textContent=e,t.innerHTML}renderAnswerOptions(e){let t=`q${this.currentIndex}`,r=this.answers[t];switch(e.type){case"binary":return`<div class="bliptar__options">
|
|
380
|
+
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
|
+
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
|
-
<button class="bliptar__btn${
|
|
384
|
-
${
|
|
383
|
+
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
|
+
${e.style==="thumbs"?"\u{1F44E} ":""}${this.escapeHtml(e.negativeLabel)}
|
|
385
385
|
</button>
|
|
386
|
-
</div>`;case"star-rating":let
|
|
386
|
+
</div>`;case"star-rating":let o='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)o+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return o+"</div>";case"emoji-scale":let a=["\u{1F61E}","\u{1F615}","\u{1F610}","\u{1F642}","\u{1F60A}"],n=["\u{1F61E}","\u{1F610}","\u{1F60A}"],l=e.scale===3?n:a,p='<div class="bliptar__emojis">';return l.forEach((s,d)=>{p+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),p+"</div>";case"nps":let c='<div class="bliptar__nps">';for(let s=0;s<=10;s++)c+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return c+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,c;case"single-choice":let b='<div class="bliptar__options">';return e.options.forEach(s=>{b+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),b+"</div>";case"text-input":return`<textarea class="bliptar__textarea" placeholder="${this.escapeHtml(e.placeholder)}" maxlength="${e.maxLength}">${r||""}</textarea>`;default:return""}}attachEventListeners(){if(!this.container)return;let e=this.form.questions[this.currentIndex],{behavior:t}=this.form,r=this.container.querySelector(".bliptar__close");r&&r.addEventListener("click",()=>this.options.onDismiss());let o=this.container.querySelector(".bliptar__backdrop");o&&t.allowDismiss&&o.addEventListener("click",()=>this.options.onDismiss());let a=s=>{this.answers[`q${this.currentIndex}`]=s,t.autoAdvance&&e.type!=="text-input"?setTimeout(()=>this.goNext(),t.autoAdvanceDelay||300):this.renderQuestion()};this.container.querySelectorAll(".bliptar__btn, .bliptar__nps-btn").forEach(s=>{s.addEventListener("click",()=>{let d=s.dataset.value;e.type==="binary"?a(d==="true"):e.type==="nps"?a(parseInt(d,10)):a(d)})});let n=this.container.querySelectorAll(".bliptar__star");n.forEach((s,d)=>{s.addEventListener("mouseenter",()=>{n.forEach((x,E)=>x.classList.toggle("bliptar__star--active",E<=d))}),s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let l=this.container.querySelector(".bliptar__stars");l&&l.addEventListener("mouseleave",()=>{let s=this.answers[`q${this.currentIndex}`];n.forEach((d,x)=>d.classList.toggle("bliptar__star--active",s?x<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let p=this.container.querySelector(".bliptar__textarea");p&&(p.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=p.value}),setTimeout(()=>p.focus(),100));let c=this.container.querySelector(".bliptar__nav-btn--back");c&&c.addEventListener("click",()=>this.goBack());let b=this.container.querySelector(".bliptar__nav-btn--next");b&&b.addEventListener("click",()=>this.goNext())}goBack(){this.currentIndex>0&&(this.currentIndex--,this.renderQuestion())}async goNext(){this.currentIndex===this.form.questions.length-1?await this.submit():(this.currentIndex++,this.renderQuestion())}async submit(){if(this.container){let e=this.container.querySelector(".bliptar__card");e&&(e.innerHTML='<div class="bliptar__loading"><div class="bliptar__spinner"></div></div>')}try{await this.options.onSubmit(this.answers),this.form.behavior.showThankYou?(this.renderThankYou(),setTimeout(()=>this.options.onDismiss(),this.form.behavior.thankYouDuration||2e3)):this.options.onDismiss()}catch{this.options.onDismiss()}}renderThankYou(){if(!this.container)return;let{appearance:e,behavior:t}=this.form,r="";e.backdrop&&(r+='<div class="bliptar__backdrop"></div>'),r+=`<div class="bliptar__card">
|
|
387
387
|
<div class="bliptar__thankyou">
|
|
388
388
|
<div class="bliptar__thankyou-emoji">\u{1F389}</div>
|
|
389
389
|
<div class="bliptar__thankyou-text">${this.escapeHtml(t.thankYouMessage||"Thank you for your feedback!")}</div>
|
|
390
390
|
</div>
|
|
391
|
-
</div>`,this.container.innerHTML=
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function w(){let i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=i.charAt(Math.floor(Math.random()*i.length));return`sess_${e}`}function _(){let i=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(i)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(i)?"mobile":"desktop"}function $(i,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>i(...r),e)}}var B="https://api.bliptar.com",m=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.config={apiUrl:B,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}init(){if(this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK");let e=$(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,o=t.scrollHeight-t.clientHeight;this.scrollDepth=o>0?r/o:0},100);this.addEventListener(window,"scroll",e),this.track("page_view")}identify(e,t){this.config.userId=e,this.config.userAttributes={...this.config.userAttributes,...t},this.log("User identified:",e)}async track(e,t){this.log("Tracking event:",e,t);let r=this.getContext();try{let o=await fetch(`${this.config.apiUrl}/api/sdk/trigger`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,event:e,event_data:t||{},context:r})});if(!o.ok)throw new Error(`API error: ${o.status}`);let a=await o.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(o){this.handleError(o)}}async showForm(e){this.log("Manually showing form:",e);try{let t=await fetch(`${this.config.apiUrl}/api/sdk/form/${e}`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!t.ok)throw new Error(`Failed to fetch form: ${t.status}`);let r=await t.json();await this.displayForm(r)}catch(t){this.handleError(t)}}trackClicks(e){let{selector:t,name:r,once:o=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let c=l.target.closest(t);c&&(o&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),o&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:c.textContent?.trim().slice(0,100)||"",element_id:c.id||null,element_class:c.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:o=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||o&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),o||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(o&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};_()!=="desktop"&&this.addEventListener(document,"visibilitychange",n)}destroy(){this.log("Destroying Bliptar SDK"),this.eventListeners.forEach(({target:e,type:t,handler:r})=>{e.removeEventListener(t,r)}),this.eventListeners=[],this.renderer&&(this.renderer.destroy(),this.renderer=null),this.exitIntentCooldownTimer&&(clearTimeout(this.exitIntentCooldownTimer),this.exitIntentCooldownTimer=null),this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new g(e,{onSubmit:async r=>{await this.submitResponse(e.formId,r,t),this.config.onSubmit?.(r)},onDismiss:()=>{this.config.onDismiss?.(),this.renderer?.destroy(),this.renderer=null}}),this.renderer.render()}async submitResponse(e,t,r){this.log("Submitting response:",e,t);let o=this.getContext(),a=new Date().toISOString();try{await fetch(`${this.config.apiUrl}/api/sdk/submit`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.config.apiKey}`},body:JSON.stringify({session_id:this.sessionId,user_id:this.config.userId,form_id:e,campaign_id:r,answers:t,context:o,completed_at:a})})}catch(n){this.handleError(n)}}getContext(){return{pageUrl:window.location.href,referrer:document.referrer,timeOnPage:Math.floor((Date.now()-this.pageLoadTime)/1e3),scrollDepth:Math.round(this.scrollDepth*100)/100,deviceType:_()}}loadOrCreateSessionId(){let e="bliptar_session",t=sessionStorage.getItem(e);return t||(t=w(),sessionStorage.setItem(e,t)),t}addEventListener(e,t,r){e.addEventListener(t,r),this.eventListeners.push({target:e,type:t,handler:r})}log(...e){this.config.debug&&console.log("[Bliptar]",...e)}handleError(e){this.log("Error:",e),this.config.onError?.(e)}};function T(i){return new m(i)}if(typeof window<"u"){let i=document.currentScript;if(i?.dataset.apiKey){let e=T({apiKey:i.dataset.apiKey,debug:i.dataset.debug==="true"});window.bliptar=e,e.init()}}export{m as Bliptar,T as createBliptar};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bliptarjs/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Bliptar SDK - Lightweight micro-feedback for web apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -14,8 +14,17 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"files": [
|
|
17
|
-
"dist"
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
18
19
|
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/bliptar/sdk"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/bliptar/sdk/issues"
|
|
26
|
+
},
|
|
27
|
+
"homepage": "https://bliptar.com/docs/sdk",
|
|
19
28
|
"scripts": {
|
|
20
29
|
"build": "tsup src/index.ts --format esm,cjs --dts --minify",
|
|
21
30
|
"dev": "tsup src/index.ts --format esm,cjs --dts --watch",
|