@bliptarjs/sdk 0.2.1 → 0.3.1
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 +111 -464
- package/dist/index.cjs +15 -15
- package/dist/index.d.cts +11 -2
- package/dist/index.d.ts +11 -2
- package/dist/index.js +16 -16
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -1,422 +1,154 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
Lightweight SDK for collecting in-app user feedback. Designed to be small (~7KB gzipped) and non-intrusive.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install @bliptarjs/sdk
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
yarn add @bliptarjs/sdk
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
```bash
|
|
16
|
-
pnpm add @bliptarjs/sdk
|
|
17
|
-
```
|
|
18
|
-
|
|
19
|
-
## Quick Start
|
|
20
|
-
|
|
21
|
-
```javascript
|
|
22
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
23
|
-
|
|
24
|
-
const bliptar = createBliptar({
|
|
25
|
-
apiKey: 'your-api-key',
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
bliptar.init();
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
That's it! The SDK will automatically track page views and display forms based on your campaign rules configured in the Bliptar dashboard.
|
|
32
|
-
|
|
33
|
-
## Core Concepts
|
|
34
|
-
|
|
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
|
|
51
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
52
|
-
|
|
53
|
-
const bliptar = createBliptar({
|
|
54
|
-
apiKey: 'your-api-key',
|
|
55
|
-
debug: false, // optional
|
|
56
|
-
userId: 'user-123', // optional
|
|
57
|
-
userAttributes: { plan: 'pro' }, // optional
|
|
58
|
-
onShow: (form) => console.log('Showing form:', form.formId),
|
|
59
|
-
onSubmit: (answers) => console.log('Submitted:', answers),
|
|
60
|
-
onDismiss: () => console.log('Form dismissed'),
|
|
61
|
-
onError: (error) => console.error('Error:', error),
|
|
62
|
-
});
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
#### Configuration Options
|
|
66
|
-
|
|
67
|
-
| Option | Type | Default | Description |
|
|
68
|
-
|--------|------|---------|-------------|
|
|
69
|
-
| `apiKey` | `string` | **required** | Your Bliptar API key |
|
|
70
|
-
| `debug` | `boolean` | `false` | Enable console logging for debugging |
|
|
71
|
-
| `userId` | `string` | - | Pre-set user ID for attribution |
|
|
72
|
-
| `userAttributes` | `object` | - | Pre-set user attributes for targeting |
|
|
73
|
-
| `onShow` | `function` | - | Callback when a form is displayed |
|
|
74
|
-
| `onSubmit` | `function` | - | Callback when a response is submitted |
|
|
75
|
-
| `onDismiss` | `function` | - | Callback when a form is dismissed |
|
|
76
|
-
| `onError` | `function` | - | Callback when an error occurs |
|
|
77
|
-
|
|
78
|
-
---
|
|
79
|
-
|
|
80
|
-
### `init()`
|
|
81
|
-
|
|
82
|
-
Initializes the SDK. This sets up event listeners and triggers a `page_view` event.
|
|
83
|
-
|
|
84
|
-
```javascript
|
|
85
|
-
bliptar.init();
|
|
86
|
-
```
|
|
1
|
+
<div align="center">
|
|
87
2
|
|
|
88
|
-
|
|
3
|
+
# @bliptarjs/sdk
|
|
89
4
|
|
|
90
|
-
|
|
5
|
+
**Micro-feedback that users actually respond to.**
|
|
91
6
|
|
|
92
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/@bliptarjs/sdk)
|
|
8
|
+
[](https://bundlephobia.com/package/@bliptarjs/sdk)
|
|
9
|
+
[](LICENSE)
|
|
93
10
|
|
|
94
|
-
|
|
11
|
+
[Website](https://bliptar.com) · [Documentation](https://app.bliptar.com/docs) · [Dashboard](https://app.bliptar.com)
|
|
95
12
|
|
|
96
|
-
|
|
97
|
-
// Basic identification
|
|
98
|
-
bliptar.identify('user-123');
|
|
99
|
-
|
|
100
|
-
// With attributes for targeting
|
|
101
|
-
bliptar.identify('user-123', {
|
|
102
|
-
email: 'user@example.com',
|
|
103
|
-
plan: 'pro',
|
|
104
|
-
company: 'Acme Inc',
|
|
105
|
-
signupDate: '2024-01-15',
|
|
106
|
-
});
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
User attributes can be used in campaign targeting rules (e.g., only show to "pro" plan users).
|
|
13
|
+
</div>
|
|
110
14
|
|
|
111
15
|
---
|
|
112
16
|
|
|
113
|
-
|
|
17
|
+
Users abandon your app, churn from your service, and leave features unused – **without ever telling you why**. Traditional surveys are too long. Pop-ups are annoying. You're left guessing.
|
|
114
18
|
|
|
115
|
-
|
|
19
|
+
**Bliptar** captures real-time user sentiment with **nano feedback forms** – quick 1-2 question surveys that appear at exactly the right moment.
|
|
116
20
|
|
|
117
|
-
|
|
118
|
-
// Simple event
|
|
119
|
-
bliptar.track('checkout_complete');
|
|
120
|
-
|
|
121
|
-
// With event data
|
|
122
|
-
bliptar.track('checkout_complete', {
|
|
123
|
-
orderId: 'order-456',
|
|
124
|
-
total: 99.99,
|
|
125
|
-
items: 3,
|
|
126
|
-
});
|
|
21
|
+
> One question. Two seconds. Real insights.
|
|
127
22
|
|
|
128
|
-
|
|
129
|
-
bliptar.track('feature_used', {
|
|
130
|
-
feature: 'export_pdf',
|
|
131
|
-
duration: 5000,
|
|
132
|
-
});
|
|
133
|
-
```
|
|
23
|
+
## Features
|
|
134
24
|
|
|
135
|
-
|
|
25
|
+
- **Thumbs up/down** · **Emoji scales** · **Star ratings** · **NPS** · **Single choice** · **Text input**
|
|
26
|
+
- **Smart triggers** – Page view, scroll depth, time on page, exit intent, custom events
|
|
27
|
+
- **Dashboard controlled** – No code changes to update campaigns
|
|
28
|
+
- **Lightweight** – ~8KB gzipped, zero dependencies
|
|
29
|
+
- **Privacy first** – No cookies, GDPR friendly
|
|
136
30
|
|
|
137
31
|
---
|
|
138
32
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
// Track only the first click
|
|
157
|
-
bliptar.trackClicks({
|
|
158
|
-
selector: '.signup-button',
|
|
159
|
-
name: 'signup-click',
|
|
160
|
-
once: true,
|
|
161
|
-
});
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
#### Options
|
|
165
|
-
|
|
166
|
-
| Option | Type | Default | Description |
|
|
167
|
-
|--------|------|---------|-------------|
|
|
168
|
-
| `selector` | `string` | **required** | CSS selector for elements to track |
|
|
169
|
-
| `name` | `string` | `selector` | Friendly name for the click target |
|
|
170
|
-
| `once` | `boolean` | `false` | Only track the first click per session |
|
|
171
|
-
|
|
172
|
-
#### Event Data Sent
|
|
173
|
-
|
|
174
|
-
When a click is detected, the following data is sent:
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
{
|
|
178
|
-
selector: '.pricing-cta',
|
|
179
|
-
name: 'pricing-click',
|
|
180
|
-
element_text: 'Get Started',
|
|
181
|
-
element_id: 'cta-btn',
|
|
182
|
-
element_class: 'pricing-cta btn-primary'
|
|
183
|
-
}
|
|
184
|
-
```
|
|
185
|
-
|
|
186
|
-
---
|
|
187
|
-
|
|
188
|
-
### `enableExitIntent(options?)`
|
|
189
|
-
|
|
190
|
-
Enables exit intent detection. Triggers an `exit_intent` event when a user appears to be leaving the page.
|
|
191
|
-
|
|
192
|
-
```javascript
|
|
193
|
-
// Basic usage (triggers once per session)
|
|
194
|
-
bliptar.enableExitIntent();
|
|
195
|
-
|
|
196
|
-
// Custom configuration
|
|
197
|
-
bliptar.enableExitIntent({
|
|
198
|
-
threshold: 50, // Pixels from top of viewport
|
|
199
|
-
cooldown: 10000, // Ms before can trigger again
|
|
200
|
-
once: true, // Only trigger once per session
|
|
201
|
-
});
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
#### Options
|
|
205
|
-
|
|
206
|
-
| Option | Type | Default | Description |
|
|
207
|
-
|--------|------|---------|-------------|
|
|
208
|
-
| `threshold` | `number` | `50` | Distance in pixels from top of viewport to trigger |
|
|
209
|
-
| `cooldown` | `number` | `10000` | Milliseconds before exit intent can trigger again |
|
|
210
|
-
| `once` | `boolean` | `true` | Only trigger once per session |
|
|
211
|
-
|
|
212
|
-
#### How It Works
|
|
213
|
-
|
|
214
|
-
- **Desktop:** Detects when the mouse cursor leaves the viewport from the top (user moving to close tab, click back, or navigate away)
|
|
215
|
-
- **Mobile:** Detects when the page visibility changes (user switching apps or tabs)
|
|
33
|
+
## Works With
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/react/react-original.svg" alt="React" width="40" height="40"/>
|
|
37
|
+
|
|
38
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/nextjs/nextjs-original.svg" alt="Next.js" width="40" height="40"/>
|
|
39
|
+
|
|
40
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/vuejs/vuejs-original.svg" alt="Vue" width="40" height="40"/>
|
|
41
|
+
|
|
42
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/angularjs/angularjs-original.svg" alt="Angular" width="40" height="40"/>
|
|
43
|
+
|
|
44
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/javascript/javascript-original.svg" alt="JavaScript" width="40" height="40"/>
|
|
45
|
+
|
|
46
|
+
<img src="https://raw.githubusercontent.com/devicons/devicon/master/icons/typescript/typescript-original.svg" alt="TypeScript" width="40" height="40"/>
|
|
47
|
+
</p>
|
|
216
48
|
|
|
217
49
|
---
|
|
218
50
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
Manually displays a specific form, bypassing campaign rules.
|
|
222
|
-
|
|
223
|
-
```javascript
|
|
224
|
-
// Show a form programmatically
|
|
225
|
-
await bliptar.showForm('form_abc123');
|
|
226
|
-
|
|
227
|
-
// Example: Show after completing onboarding
|
|
228
|
-
function completeOnboarding() {
|
|
229
|
-
// ... onboarding logic
|
|
230
|
-
bliptar.showForm('form_onboarding_feedback');
|
|
231
|
-
}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
---
|
|
235
|
-
|
|
236
|
-
### `destroy()`
|
|
237
|
-
|
|
238
|
-
Removes all event listeners and cleans up the SDK. Call this when unmounting your app or when done collecting feedback.
|
|
51
|
+
## Installation
|
|
239
52
|
|
|
240
|
-
```
|
|
241
|
-
|
|
53
|
+
```bash
|
|
54
|
+
npm install @bliptarjs/sdk
|
|
242
55
|
```
|
|
243
56
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
## Usage Examples
|
|
247
|
-
|
|
248
|
-
### Basic Setup
|
|
57
|
+
## Quick Start
|
|
249
58
|
|
|
250
59
|
```javascript
|
|
251
60
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
252
61
|
|
|
253
62
|
const bliptar = createBliptar({
|
|
254
|
-
apiKey: '
|
|
63
|
+
apiKey: 'your-api-key',
|
|
255
64
|
});
|
|
256
65
|
|
|
257
66
|
bliptar.init();
|
|
258
67
|
```
|
|
259
68
|
|
|
260
|
-
|
|
69
|
+
That's it! The SDK automatically fetches your campaigns and sets up triggers.
|
|
261
70
|
|
|
262
|
-
|
|
263
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
264
|
-
|
|
265
|
-
const bliptar = createBliptar({
|
|
266
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
bliptar.init();
|
|
270
|
-
|
|
271
|
-
// When user logs in
|
|
272
|
-
function onUserLogin(user) {
|
|
273
|
-
bliptar.identify(user.id, {
|
|
274
|
-
email: user.email,
|
|
275
|
-
plan: user.subscription.plan,
|
|
276
|
-
createdAt: user.createdAt,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
```
|
|
71
|
+
---
|
|
280
72
|
|
|
281
|
-
|
|
73
|
+
## How It Works
|
|
282
74
|
|
|
283
|
-
|
|
284
|
-
|
|
75
|
+
1. **Create campaigns** in the [Bliptar Dashboard](https://app.bliptar.com)
|
|
76
|
+
2. **Configure triggers** – page view, scroll depth, time on page, exit intent, or custom events
|
|
77
|
+
3. **Add the SDK** to your app with your API key
|
|
78
|
+
4. **Done** – forms appear automatically based on your campaign rules
|
|
285
79
|
|
|
286
|
-
|
|
287
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
288
|
-
onSubmit: (answers) => {
|
|
289
|
-
// Send to your analytics
|
|
290
|
-
analytics.track('feedback_submitted', answers);
|
|
291
|
-
},
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
bliptar.init();
|
|
80
|
+
---
|
|
295
81
|
|
|
296
|
-
|
|
297
|
-
function onPurchaseComplete(order) {
|
|
298
|
-
bliptar.track('purchase_complete', {
|
|
299
|
-
orderId: order.id,
|
|
300
|
-
total: order.total,
|
|
301
|
-
itemCount: order.items.length,
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
```
|
|
82
|
+
## Core API
|
|
305
83
|
|
|
306
|
-
|
|
84
|
+
| Method | Description |
|
|
85
|
+
|--------|-------------|
|
|
86
|
+
| `init()` | Initialize SDK and fetch campaigns |
|
|
87
|
+
| `identify(userId, attributes?)` | Associate feedback with a user |
|
|
88
|
+
| `track(event, data?)` | Trigger custom events |
|
|
89
|
+
| `destroy()` | Cleanup listeners and timers |
|
|
307
90
|
|
|
308
91
|
```javascript
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
const bliptar = createBliptar({
|
|
312
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
bliptar.init();
|
|
92
|
+
// Identify user when they log in
|
|
93
|
+
bliptar.identify('user-123', { plan: 'pro' });
|
|
316
94
|
|
|
317
|
-
//
|
|
318
|
-
|
|
319
|
-
bliptar.enableExitIntent({
|
|
320
|
-
once: true, // Only show once
|
|
321
|
-
});
|
|
322
|
-
}
|
|
95
|
+
// Track custom events
|
|
96
|
+
bliptar.track('checkout_complete', { total: 99.99 });
|
|
323
97
|
```
|
|
324
98
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
```javascript
|
|
328
|
-
import { createBliptar } from '@bliptarjs/sdk';
|
|
329
|
-
|
|
330
|
-
const bliptar = createBliptar({
|
|
331
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
332
|
-
});
|
|
99
|
+
For the full API reference, see the [Documentation](https://app.bliptar.com/docs).
|
|
333
100
|
|
|
334
|
-
|
|
101
|
+
---
|
|
335
102
|
|
|
336
|
-
|
|
337
|
-
bliptar.trackClicks({
|
|
338
|
-
selector: '[data-track="upgrade"]',
|
|
339
|
-
name: 'upgrade-button',
|
|
340
|
-
});
|
|
103
|
+
## Framework Examples
|
|
341
104
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
selector: '.help-button',
|
|
345
|
-
name: 'help-click',
|
|
346
|
-
});
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
### React Integration
|
|
105
|
+
<details>
|
|
106
|
+
<summary><strong>React</strong></summary>
|
|
350
107
|
|
|
351
108
|
```jsx
|
|
352
109
|
import { useEffect } from 'react';
|
|
353
110
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
354
111
|
|
|
355
112
|
const bliptar = createBliptar({
|
|
356
|
-
apiKey:
|
|
113
|
+
apiKey: process.env.REACT_APP_BLIPTAR_KEY,
|
|
357
114
|
});
|
|
358
115
|
|
|
359
116
|
function App() {
|
|
360
117
|
useEffect(() => {
|
|
361
118
|
bliptar.init();
|
|
362
|
-
|
|
363
|
-
return () => {
|
|
364
|
-
bliptar.destroy();
|
|
365
|
-
};
|
|
119
|
+
return () => bliptar.destroy();
|
|
366
120
|
}, []);
|
|
367
121
|
|
|
368
122
|
return <div>Your app</div>;
|
|
369
123
|
}
|
|
370
|
-
|
|
371
|
-
// In a component that needs to identify users
|
|
372
|
-
function UserProfile({ user }) {
|
|
373
|
-
useEffect(() => {
|
|
374
|
-
if (user) {
|
|
375
|
-
bliptar.identify(user.id, {
|
|
376
|
-
email: user.email,
|
|
377
|
-
plan: user.plan,
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
}, [user]);
|
|
381
|
-
|
|
382
|
-
return <div>Welcome, {user.name}</div>;
|
|
383
|
-
}
|
|
384
124
|
```
|
|
125
|
+
</details>
|
|
385
126
|
|
|
386
|
-
|
|
127
|
+
<details>
|
|
128
|
+
<summary><strong>Next.js</strong></summary>
|
|
387
129
|
|
|
388
130
|
```jsx
|
|
389
|
-
//
|
|
131
|
+
// app/providers.js
|
|
132
|
+
'use client';
|
|
133
|
+
import { useEffect } from 'react';
|
|
390
134
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
391
135
|
|
|
392
|
-
|
|
393
|
-
? createBliptar({ apiKey: process.env.
|
|
136
|
+
const bliptar = typeof window !== 'undefined'
|
|
137
|
+
? createBliptar({ apiKey: process.env.NEXT_PUBLIC_BLIPTAR_KEY })
|
|
394
138
|
: null;
|
|
395
139
|
|
|
396
|
-
|
|
397
|
-
'use client';
|
|
398
|
-
|
|
399
|
-
import { useEffect } from 'react';
|
|
400
|
-
import { bliptar } from '@/lib/bliptar';
|
|
401
|
-
|
|
402
|
-
export default function RootLayout({ children }) {
|
|
140
|
+
export function BliptarProvider({ children }) {
|
|
403
141
|
useEffect(() => {
|
|
404
142
|
bliptar?.init();
|
|
405
|
-
|
|
406
|
-
return () => {
|
|
407
|
-
bliptar?.destroy();
|
|
408
|
-
};
|
|
143
|
+
return () => bliptar?.destroy();
|
|
409
144
|
}, []);
|
|
410
|
-
|
|
411
|
-
return (
|
|
412
|
-
<html>
|
|
413
|
-
<body>{children}</body>
|
|
414
|
-
</html>
|
|
415
|
-
);
|
|
145
|
+
return children;
|
|
416
146
|
}
|
|
417
147
|
```
|
|
148
|
+
</details>
|
|
418
149
|
|
|
419
|
-
|
|
150
|
+
<details>
|
|
151
|
+
<summary><strong>Vue</strong></summary>
|
|
420
152
|
|
|
421
153
|
```vue
|
|
422
154
|
<script setup>
|
|
@@ -424,142 +156,57 @@ import { onMounted, onUnmounted } from 'vue';
|
|
|
424
156
|
import { createBliptar } from '@bliptarjs/sdk';
|
|
425
157
|
|
|
426
158
|
const bliptar = createBliptar({
|
|
427
|
-
apiKey:
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
onMounted(() => {
|
|
431
|
-
bliptar.init();
|
|
159
|
+
apiKey: import.meta.env.VITE_BLIPTAR_KEY,
|
|
432
160
|
});
|
|
433
161
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
});
|
|
162
|
+
onMounted(() => bliptar.init());
|
|
163
|
+
onUnmounted(() => bliptar.destroy());
|
|
437
164
|
</script>
|
|
438
165
|
```
|
|
166
|
+
</details>
|
|
439
167
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
## Automatic Tracking
|
|
443
|
-
|
|
444
|
-
The SDK automatically tracks the following without any additional code:
|
|
445
|
-
|
|
446
|
-
| Data | Description |
|
|
447
|
-
|------|-------------|
|
|
448
|
-
| `page_view` | Fired on `init()` |
|
|
449
|
-
| `scroll_depth` | Percentage scrolled (0-100%) |
|
|
450
|
-
| `time_on_page` | Seconds spent on page |
|
|
451
|
-
| `device_type` | `desktop`, `mobile`, or `tablet` |
|
|
452
|
-
| `page_url` | Current page URL |
|
|
453
|
-
| `referrer` | Previous page URL |
|
|
454
|
-
|
|
455
|
-
This data is sent with every trigger request and can be used in campaign conditions.
|
|
456
|
-
|
|
457
|
-
---
|
|
458
|
-
|
|
459
|
-
## Form Types
|
|
460
|
-
|
|
461
|
-
The SDK supports multiple feedback form types:
|
|
462
|
-
|
|
463
|
-
| Type | Description |
|
|
464
|
-
|------|-------------|
|
|
465
|
-
| **Binary** | Thumbs up/down, Yes/No questions |
|
|
466
|
-
| **Star Rating** | 1-5 star ratings with optional half stars |
|
|
467
|
-
| **Emoji Scale** | 3 or 5 point emoji scales |
|
|
468
|
-
| **NPS** | Net Promoter Score (0-10) |
|
|
469
|
-
| **Single Choice** | Multiple choice questions |
|
|
470
|
-
| **Text Input** | Free-form text responses |
|
|
471
|
-
|
|
472
|
-
Forms are configured in the Bliptar dashboard and automatically rendered by the SDK.
|
|
473
|
-
|
|
474
|
-
---
|
|
475
|
-
|
|
476
|
-
## TypeScript Support
|
|
477
|
-
|
|
478
|
-
The SDK is written in TypeScript and includes full type definitions.
|
|
168
|
+
<details>
|
|
169
|
+
<summary><strong>Angular</strong></summary>
|
|
479
170
|
|
|
480
171
|
```typescript
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
// Type-safe click tracking
|
|
500
|
-
const clickOptions: ClickTrackingOptions = {
|
|
501
|
-
selector: '.cta-button',
|
|
502
|
-
name: 'main-cta',
|
|
503
|
-
once: true,
|
|
504
|
-
};
|
|
505
|
-
bliptar.trackClicks(clickOptions);
|
|
506
|
-
|
|
507
|
-
// Type-safe exit intent
|
|
508
|
-
const exitOptions: ExitIntentOptions = {
|
|
509
|
-
threshold: 50,
|
|
510
|
-
once: true,
|
|
511
|
-
};
|
|
512
|
-
bliptar.enableExitIntent(exitOptions);
|
|
172
|
+
// bliptar.service.ts
|
|
173
|
+
import { Injectable, OnDestroy } from '@angular/core';
|
|
174
|
+
import { createBliptar, BliptarInstance } from '@bliptarjs/sdk';
|
|
175
|
+
|
|
176
|
+
@Injectable({ providedIn: 'root' })
|
|
177
|
+
export class BliptarService implements OnDestroy {
|
|
178
|
+
private bliptar: BliptarInstance;
|
|
179
|
+
|
|
180
|
+
constructor() {
|
|
181
|
+
this.bliptar = createBliptar({ apiKey: environment.bliptarKey });
|
|
182
|
+
this.bliptar.init();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
ngOnDestroy() {
|
|
186
|
+
this.bliptar.destroy();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
513
189
|
```
|
|
190
|
+
</details>
|
|
514
191
|
|
|
515
192
|
---
|
|
516
193
|
|
|
517
|
-
##
|
|
518
|
-
|
|
519
|
-
| Browser | Supported |
|
|
520
|
-
|---------|-----------|
|
|
521
|
-
| Chrome | Latest 2 versions |
|
|
522
|
-
| Firefox | Latest 2 versions |
|
|
523
|
-
| Safari | Latest 2 versions |
|
|
524
|
-
| Edge | Latest 2 versions |
|
|
525
|
-
|
|
526
|
-
---
|
|
527
|
-
|
|
528
|
-
## Bundle Size
|
|
194
|
+
## TypeScript
|
|
529
195
|
|
|
530
|
-
|
|
531
|
-
- **Gzipped:** ~7KB
|
|
196
|
+
Full TypeScript support included.
|
|
532
197
|
|
|
533
|
-
|
|
198
|
+
```typescript
|
|
199
|
+
import { createBliptar, BliptarConfig, BliptarInstance } from '@bliptarjs/sdk';
|
|
200
|
+
```
|
|
534
201
|
|
|
535
202
|
---
|
|
536
203
|
|
|
537
|
-
##
|
|
538
|
-
|
|
539
|
-
### Forms not showing?
|
|
540
|
-
|
|
541
|
-
1. **Check your API key** - Ensure it's valid and for the correct environment
|
|
542
|
-
2. **Check campaign rules** - Verify your campaign is active and targeting rules match
|
|
543
|
-
3. **Enable debug mode** - Set `debug: true` to see console logs
|
|
544
|
-
4. **Check throttling** - Campaigns have frequency limits to avoid annoying users
|
|
545
|
-
|
|
546
|
-
### Debug Mode
|
|
547
|
-
|
|
548
|
-
```javascript
|
|
549
|
-
const bliptar = createBliptar({
|
|
550
|
-
apiKey: 'blip_live_xxxxxxxx',
|
|
551
|
-
debug: true, // Enables console logging
|
|
552
|
-
});
|
|
553
|
-
```
|
|
554
|
-
|
|
555
|
-
This will log:
|
|
556
|
-
- SDK initialization
|
|
557
|
-
- Events being tracked
|
|
558
|
-
- Form display triggers
|
|
559
|
-
- API responses
|
|
560
|
-
- Errors
|
|
204
|
+
## Links
|
|
561
205
|
|
|
562
|
-
|
|
206
|
+
- [Website](https://bliptar.com)
|
|
207
|
+
- [Documentation](https://app.bliptar.com/docs)
|
|
208
|
+
- [Dashboard](https://app.bliptar.com)
|
|
209
|
+
- [npm](https://www.npmjs.com/package/@bliptarjs/sdk)
|
|
563
210
|
|
|
564
211
|
## License
|
|
565
212
|
|
package/dist/index.cjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
"use strict";var
|
|
2
|
-
--bliptar-bg: ${
|
|
3
|
-
--bliptar-surface: ${
|
|
4
|
-
--bliptar-text: ${
|
|
5
|
-
--bliptar-text-secondary: ${
|
|
6
|
-
--bliptar-primary: ${
|
|
7
|
-
--bliptar-primary-hover: ${
|
|
8
|
-
--bliptar-border: ${
|
|
9
|
-
--bliptar-shadow: ${
|
|
10
|
-
--bliptar-star: ${
|
|
11
|
-
`}var
|
|
1
|
+
"use strict";var x=Object.defineProperty;var I=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var P=Object.prototype.hasOwnProperty;var D=(o,e)=>{for(var t in e)x(o,t,{get:e[t],enumerable:!0})},A=(o,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of L(e))!P.call(o,i)&&i!==t&&x(o,i,{get:()=>e[i],enumerable:!(r=I(e,i))||r.enumerable});return o};var B=o=>A(x({},"__esModule",{value:!0}),o);var F={};D(F,{Bliptar:()=>h,createBliptar:()=>C});module.exports=B(F);var b={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"},u={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 j(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function H(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function w(o){let e=o.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=o.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(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function _(o){let e=w(o);return e?M(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=w(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function z(o){return _(o)?"#ffffff":"#1a1a1a"}function R(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=j(r,"background-color");if(i){let a=_(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=z(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,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 c=H(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function T(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;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 i=r||{fallback:"light",autoContrast:!0},a=R(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function $(o){return`
|
|
2
|
+
--bliptar-bg: ${o.backgroundColor};
|
|
3
|
+
--bliptar-surface: ${o.surfaceColor};
|
|
4
|
+
--bliptar-text: ${o.textPrimary};
|
|
5
|
+
--bliptar-text-secondary: ${o.textSecondary};
|
|
6
|
+
--bliptar-primary: ${o.primaryColor};
|
|
7
|
+
--bliptar-primary-hover: ${o.primaryHover};
|
|
8
|
+
--bliptar-border: ${o.borderColor};
|
|
9
|
+
--bliptar-shadow: ${o.shadowColor};
|
|
10
|
+
--bliptar-star: ${o.starColor};
|
|
11
|
+
`}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=T(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
12
|
:root { ${$(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
@@ -372,20 +372,20 @@
|
|
|
372
372
|
}
|
|
373
373
|
|
|
374
374
|
@media print { .bliptar { display: none !important; } }
|
|
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,
|
|
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,i=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>`),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(
|
|
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(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.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
380
|
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
381
|
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
383
|
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
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 i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</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,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;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 i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.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((v,E)=>v.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,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.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=r}};function
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function y(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function k(){if(typeof navigator>"u")return"desktop";let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function S(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var O="https://api.bliptar.com",h=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:O,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(typeof window>"u"||this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=S(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}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 i=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(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}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:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&this.exitIntentTriggered)return;this.log("Exit intent detected (visibility change)"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"visibility_change"})}};k()!=="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.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(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 i=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:i,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:k()}}loadOrCreateSessionId(){let e="bliptar_session";if(typeof sessionStorage>"u")return y();let t=sessionStorage.getItem(e);return t||(t=y(),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 C(o){return new h(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=C({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}0&&(module.exports={Bliptar,createBliptar});
|
package/dist/index.d.cts
CHANGED
|
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
|
|
|
26
26
|
once?: boolean;
|
|
27
27
|
}
|
|
28
28
|
interface BliptarInstance {
|
|
29
|
-
init: () => void
|
|
29
|
+
init: () => Promise<void>;
|
|
30
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
31
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
32
32
|
showForm: (formId: string) => Promise<void>;
|
|
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
|
|
|
128
128
|
private renderer;
|
|
129
129
|
private pageLoadTime;
|
|
130
130
|
private scrollDepth;
|
|
131
|
+
private maxScrollDepth;
|
|
131
132
|
private isInitialized;
|
|
132
133
|
private eventListeners;
|
|
133
134
|
private trackedClicks;
|
|
134
135
|
private exitIntentTriggered;
|
|
135
136
|
private exitIntentCooldownTimer;
|
|
137
|
+
private timeOnPageTimers;
|
|
138
|
+
private scrollDepthThresholds;
|
|
139
|
+
private triggeredScrollThresholds;
|
|
140
|
+
private campaigns;
|
|
136
141
|
constructor(config: BliptarConfig);
|
|
137
|
-
init(): void
|
|
142
|
+
init(): Promise<void>;
|
|
143
|
+
private fetchConfigAndSetupTriggers;
|
|
144
|
+
private setupTriggersFromCampaigns;
|
|
145
|
+
private setupTimeOnPageTrigger;
|
|
146
|
+
private checkScrollThresholds;
|
|
138
147
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
139
148
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
140
149
|
showForm(formId: string): Promise<void>;
|
package/dist/index.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ interface ExitIntentOptions {
|
|
|
26
26
|
once?: boolean;
|
|
27
27
|
}
|
|
28
28
|
interface BliptarInstance {
|
|
29
|
-
init: () => void
|
|
29
|
+
init: () => Promise<void>;
|
|
30
30
|
identify: (userId: string, attributes?: Record<string, unknown>) => void;
|
|
31
31
|
track: (event: string, data?: Record<string, unknown>) => void;
|
|
32
32
|
showForm: (formId: string) => Promise<void>;
|
|
@@ -128,13 +128,22 @@ declare class Bliptar implements BliptarInstance {
|
|
|
128
128
|
private renderer;
|
|
129
129
|
private pageLoadTime;
|
|
130
130
|
private scrollDepth;
|
|
131
|
+
private maxScrollDepth;
|
|
131
132
|
private isInitialized;
|
|
132
133
|
private eventListeners;
|
|
133
134
|
private trackedClicks;
|
|
134
135
|
private exitIntentTriggered;
|
|
135
136
|
private exitIntentCooldownTimer;
|
|
137
|
+
private timeOnPageTimers;
|
|
138
|
+
private scrollDepthThresholds;
|
|
139
|
+
private triggeredScrollThresholds;
|
|
140
|
+
private campaigns;
|
|
136
141
|
constructor(config: BliptarConfig);
|
|
137
|
-
init(): void
|
|
142
|
+
init(): Promise<void>;
|
|
143
|
+
private fetchConfigAndSetupTriggers;
|
|
144
|
+
private setupTriggersFromCampaigns;
|
|
145
|
+
private setupTimeOnPageTrigger;
|
|
146
|
+
private checkScrollThresholds;
|
|
138
147
|
identify(userId: string, attributes?: Record<string, unknown>): void;
|
|
139
148
|
track(event: string, data?: Record<string, unknown>): Promise<void>;
|
|
140
149
|
showForm(formId: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
var
|
|
2
|
-
--bliptar-bg: ${
|
|
3
|
-
--bliptar-surface: ${
|
|
4
|
-
--bliptar-text: ${
|
|
5
|
-
--bliptar-text-secondary: ${
|
|
6
|
-
--bliptar-primary: ${
|
|
7
|
-
--bliptar-primary-hover: ${
|
|
8
|
-
--bliptar-border: ${
|
|
9
|
-
--bliptar-shadow: ${
|
|
10
|
-
--bliptar-star: ${
|
|
11
|
-
`}var
|
|
12
|
-
:root { ${
|
|
1
|
+
var b={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"},u={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 E(o,e){try{let r=window.getComputedStyle(o).getPropertyValue(e);return r&&r!=="rgba(0, 0, 0, 0)"?r:null}catch{return null}}function I(o){try{return getComputedStyle(document.documentElement).getPropertyValue(o).trim()||null}catch{return null}}function k(o){let e=o.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=o.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(o,e,t){let[r,i,a]=[o,e,t].map(n=>(n=n/255,n<=.03928?n/12.92:Math.pow((n+.055)/1.055,2.4)));return .2126*r+.7152*i+.0722*a}function x(o){let e=k(o);return e?L(e.r,e.g,e.b)<.5:!1}function g(o,e){let t=k(o);if(!t)return o;let r=i=>Math.min(255,Math.max(0,Math.round(i+e/100*255)));return`rgb(${r(t.r)}, ${r(t.g)}, ${r(t.b)})`}function P(o){return x(o)?"#ffffff":"#1a1a1a"}function D(o,e){let t={},r=null;o&&(r=document.querySelector(o)),r||(r=document.body);let i=E(r,"background-color");if(i){let a=x(i);t.mode=a?"dark":"light",t.backgroundColor=i,t.surfaceColor=g(i,a?10:-5),t.textPrimary=P(i),t.textSecondary=g(t.textPrimary,a?-30:30),t.borderColor=g(i,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 c=I(n);c&&l!=="mode"&&(t[l]=c)}}return t.primaryColor&&(t.primaryHover=g(t.primaryColor,t.mode==="dark"?15:-10)),Object.keys(t).length>0?t:null}function C(o){let{theme:e,customTheme:t,adaptiveOptions:r}=o;if(e==="custom"&&t)return{mode:x(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 i=r||{fallback:"light",autoContrast:!0},a=D(i.sourceElement,i.inheritCssVars);return a?{...a.mode==="dark"?u:b,...a}:i.fallback==="dark"?u:b}return e==="auto"?window.matchMedia("(prefers-color-scheme: dark)").matches?u:b:e==="dark"?u:b}function w(o){return`
|
|
2
|
+
--bliptar-bg: ${o.backgroundColor};
|
|
3
|
+
--bliptar-surface: ${o.surfaceColor};
|
|
4
|
+
--bliptar-text: ${o.textPrimary};
|
|
5
|
+
--bliptar-text-secondary: ${o.textSecondary};
|
|
6
|
+
--bliptar-primary: ${o.primaryColor};
|
|
7
|
+
--bliptar-primary-hover: ${o.primaryHover};
|
|
8
|
+
--bliptar-border: ${o.borderColor};
|
|
9
|
+
--bliptar-shadow: ${o.shadowColor};
|
|
10
|
+
--bliptar-star: ${o.starColor};
|
|
11
|
+
`}var f=class{constructor(e,t){this.container=null;this.currentIndex=0;this.answers={};this.form=e,this.options=t,this.theme=C(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 { ${w(t)} }
|
|
13
13
|
|
|
14
14
|
.bliptar {
|
|
15
15
|
position: fixed;
|
|
@@ -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(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,
|
|
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,i=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>`),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(
|
|
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(i.question)}</div>`,n+=this.renderAnswerOptions(i),(!r.autoAdvance||i.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
380
|
<button class="bliptar__btn${r===!0?" bliptar__btn--selected":""}" data-value="true">
|
|
381
381
|
${e.style==="thumbs"?"\u{1F44D} ":""}${this.escapeHtml(e.positiveLabel)}
|
|
382
382
|
</button>
|
|
383
383
|
<button class="bliptar__btn${r===!1?" bliptar__btn--selected":""}" data-value="false">
|
|
384
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 i='<div class="bliptar__stars">';for(let s=1;s<=e.maxStars;s++)i+=`<span class="bliptar__star${r>=s?" bliptar__star--active":""}" data-value="${s}">\u2605</span>`;return i+"</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,c='<div class="bliptar__emojis">';return l.forEach((s,d)=>{c+=`<span class="bliptar__emoji${r===d+1?" bliptar__emoji--active":""}" data-value="${d+1}" title="${e.labels?.[d]||""}">${s}</span>`}),c+"</div>";case"nps":let p='<div class="bliptar__nps">';for(let s=0;s<=10;s++)p+=`<button class="bliptar__nps-btn${r===s?" bliptar__nps-btn--selected":""}" data-value="${s}">${s}</button>`;return p+=`</div><div class="bliptar__nps-labels"><span>${this.escapeHtml(e.lowLabel)}</span><span>${this.escapeHtml(e.highLabel)}</span></div>`,p;case"single-choice":let h='<div class="bliptar__options">';return e.options.forEach(s=>{h+=`<button class="bliptar__btn${r===s?" bliptar__btn--selected":""}" data-value="${this.escapeHtml(s)}">${this.escapeHtml(s)}</button>`}),h+"</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 i=this.container.querySelector(".bliptar__backdrop");i&&t.allowDismiss&&i.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((v,S)=>v.classList.toggle("bliptar__star--active",S<=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,v)=>d.classList.toggle("bliptar__star--active",s?v<s:!1))}),this.container.querySelectorAll(".bliptar__emoji").forEach(s=>{s.addEventListener("click",()=>a(parseInt(s.dataset.value,10)))});let c=this.container.querySelector(".bliptar__textarea");c&&(c.addEventListener("input",()=>{this.answers[`q${this.currentIndex}`]=c.value}),setTimeout(()=>c.focus(),100));let p=this.container.querySelector(".bliptar__nav-btn--back");p&&p.addEventListener("click",()=>this.goBack());let h=this.container.querySelector(".bliptar__nav-btn--next");h&&h.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=r}};function
|
|
391
|
+
</div>`,this.container.innerHTML=r}};function _(){let o="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",e="";for(let t=0;t<16;t++)e+=o.charAt(Math.floor(Math.random()*o.length));return`sess_${e}`}function y(){if(typeof navigator>"u")return"desktop";let o=navigator.userAgent;return/tablet|ipad|playbook|silk/i.test(o)?"tablet":/mobile|iphone|ipod|android|blackberry|opera mini|iemobile/i.test(o)?"mobile":"desktop"}function T(o,e){let t;return(...r)=>{clearTimeout(t),t=setTimeout(()=>o(...r),e)}}var A="https://api.bliptar.com",m=class{constructor(e){this.renderer=null;this.scrollDepth=0;this.maxScrollDepth=0;this.isInitialized=!1;this.eventListeners=[];this.trackedClicks=new Set;this.exitIntentTriggered=!1;this.exitIntentCooldownTimer=null;this.timeOnPageTimers=[];this.scrollDepthThresholds=new Set;this.triggeredScrollThresholds=new Set;this.campaigns=[];this.config={apiUrl:A,debug:!1,onShow:()=>{},onSubmit:()=>{},onDismiss:()=>{},onError:()=>{},...e},this.sessionId=this.loadOrCreateSessionId(),this.pageLoadTime=Date.now()}async init(){if(typeof window>"u"||this.isInitialized)return;this.isInitialized=!0,this.log("Initializing Bliptar SDK"),await this.fetchConfigAndSetupTriggers();let e=T(()=>{let t=document.documentElement,r=window.scrollY||t.scrollTop,i=t.scrollHeight-t.clientHeight;this.scrollDepth=i>0?r/i:0;let a=Math.round(this.scrollDepth*100);a>this.maxScrollDepth&&(this.maxScrollDepth=a,this.checkScrollThresholds(a))},100);this.addEventListener(window,"scroll",e),this.track("page_view")}async fetchConfigAndSetupTriggers(){try{let e=await fetch(`${this.config.apiUrl}/api/sdk/config`,{headers:{Authorization:`Bearer ${this.config.apiKey}`}});if(!e.ok){this.log("Failed to fetch config:",e.status);return}let t=await e.json();this.campaigns=t.campaigns||[],this.log("Loaded campaigns:",this.campaigns.length),this.setupTriggersFromCampaigns()}catch(e){this.handleError(e)}}setupTriggersFromCampaigns(){let e=new Set;for(let t of this.campaigns){if(e.add(t.trigger_event),t.trigger_event==="scroll_depth"){let r=t.trigger_conditions?.find(i=>i.field==="scroll_depth_percent");r?.value&&this.scrollDepthThresholds.add(Number(r.value))}if(t.trigger_event==="time_on_page"){let r=t.trigger_conditions?.find(i=>i.field==="time_on_page_seconds");if(r?.value){let i=Number(r.value);this.setupTimeOnPageTrigger(i)}}}this.log("Active trigger types:",Array.from(e)),e.has("exit_intent")&&(this.log("Auto-enabling exit intent detection"),this.enableExitIntent({once:!0})),this.scrollDepthThresholds.size>0&&this.log("Scroll depth thresholds:",Array.from(this.scrollDepthThresholds))}setupTimeOnPageTrigger(e){this.log("Setting up time on page trigger:",e,"seconds");let t=setTimeout(()=>{this.track("time_on_page",{seconds:e,actual_time:Math.floor((Date.now()-this.pageLoadTime)/1e3)})},e*1e3);this.timeOnPageTimers.push(t)}checkScrollThresholds(e){for(let t of this.scrollDepthThresholds)e>=t&&!this.triggeredScrollThresholds.has(t)&&(this.triggeredScrollThresholds.add(t),this.log("Scroll threshold reached:",t+"%"),this.track("scroll_depth",{threshold:t,actual_depth:e}))}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 i=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(!i.ok)throw new Error(`API error: ${i.status}`);let a=await i.json();a.show_form&&a.form&&await this.displayForm(a.form,a.campaign_id)}catch(i){this.handleError(i)}}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:i=!1}=e,a=r||t;this.log("Setting up click tracking for:",t);let n=l=>{let p=l.target.closest(t);p&&(i&&this.trackedClicks.has(a)||(this.log("Button click detected:",a),i&&this.trackedClicks.add(a),this.track("button_click",{selector:t,name:a,element_text:p.textContent?.trim().slice(0,100)||"",element_id:p.id||null,element_class:p.className||null})))};this.addEventListener(document,"click",n)}enableExitIntent(e={}){let{threshold:t=50,cooldown:r=1e4,once:i=!0}=e;this.log("Enabling exit intent detection");let a=l=>{l.clientY>t||i&&this.exitIntentTriggered||this.exitIntentCooldownTimer||(this.log("Exit intent detected"),this.exitIntentTriggered=!0,this.track("exit_intent",{trigger:"mouse_leave",mouse_y:l.clientY}),i||(this.exitIntentCooldownTimer=setTimeout(()=>{this.exitIntentCooldownTimer=null},r)))};this.addEventListener(document,"mouseleave",a);let n=()=>{if(document.visibilityState==="hidden"){if(i&&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.timeOnPageTimers.forEach(e=>clearTimeout(e)),this.timeOnPageTimers=[],this.trackedClicks.clear(),this.exitIntentTriggered=!1,this.scrollDepthThresholds.clear(),this.triggeredScrollThresholds.clear(),this.campaigns=[],this.maxScrollDepth=0,this.isInitialized=!1}async displayForm(e,t){this.log("Displaying form:",e.formId),this.config.onShow?.(e),this.renderer=new f(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 i=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:i,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";if(typeof sessionStorage>"u")return _();let t=sessionStorage.getItem(e);return 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 $(o){return new m(o)}if(typeof window<"u"){let o=document.currentScript;if(o?.dataset.apiKey){let e=$({apiKey:o.dataset.apiKey,debug:o.dataset.debug==="true"});window.bliptar=e,e.init()}}export{m as Bliptar,$ as createBliptar};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bliptarjs/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Bliptar SDK - Lightweight micro-feedback for web apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -41,7 +41,10 @@
|
|
|
41
41
|
"micro-feedback",
|
|
42
42
|
"nps",
|
|
43
43
|
"user-feedback",
|
|
44
|
-
"analytics"
|
|
44
|
+
"analytics",
|
|
45
|
+
"angular",
|
|
46
|
+
"react",
|
|
47
|
+
"vue"
|
|
45
48
|
],
|
|
46
49
|
"author": "",
|
|
47
50
|
"license": "MIT",
|