@prosdevlab/experience-sdk-plugins 0.1.3 → 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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +56 -0
- package/dist/index.d.ts +626 -2
- package/dist/index.js +799 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/banner/banner.ts +149 -51
- package/src/exit-intent/exit-intent.test.ts +423 -0
- package/src/exit-intent/exit-intent.ts +372 -0
- package/src/exit-intent/index.ts +6 -0
- package/src/exit-intent/types.ts +59 -0
- package/src/index.ts +5 -0
- package/src/integration.test.ts +362 -0
- package/src/page-visits/index.ts +6 -0
- package/src/page-visits/page-visits.test.ts +562 -0
- package/src/page-visits/page-visits.ts +314 -0
- package/src/page-visits/types.ts +119 -0
- package/src/scroll-depth/index.ts +6 -0
- package/src/scroll-depth/scroll-depth.test.ts +545 -0
- package/src/scroll-depth/scroll-depth.ts +400 -0
- package/src/scroll-depth/types.ts +122 -0
- package/src/time-delay/index.ts +6 -0
- package/src/time-delay/time-delay.test.ts +477 -0
- package/src/time-delay/time-delay.ts +297 -0
- package/src/time-delay/types.ts +89 -0
- package/src/utils/sanitize.ts +1 -1
package/src/banner/banner.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface BannerPluginConfig {
|
|
|
14
14
|
position?: 'top' | 'bottom';
|
|
15
15
|
dismissable?: boolean;
|
|
16
16
|
zIndex?: number;
|
|
17
|
+
pushDown?: string; // CSS selector of element to push down (add margin-top)
|
|
17
18
|
};
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -33,7 +34,23 @@ export interface BannerPlugin {
|
|
|
33
34
|
* import { createInstance } from '@prosdevlab/experience-sdk';
|
|
34
35
|
* import { bannerPlugin } from '@prosdevlab/experience-sdk-plugins';
|
|
35
36
|
*
|
|
36
|
-
*
|
|
37
|
+
* // Basic usage (banner overlays at top)
|
|
38
|
+
* const sdk = createInstance({
|
|
39
|
+
* banner: {
|
|
40
|
+
* position: 'top',
|
|
41
|
+
* dismissable: true
|
|
42
|
+
* }
|
|
43
|
+
* });
|
|
44
|
+
* sdk.use(bannerPlugin);
|
|
45
|
+
*
|
|
46
|
+
* // With pushDown (pushes navigation down instead of overlaying)
|
|
47
|
+
* const sdk = createInstance({
|
|
48
|
+
* banner: {
|
|
49
|
+
* position: 'top',
|
|
50
|
+
* dismissable: true,
|
|
51
|
+
* pushDown: 'header' // CSS selector of element to push down
|
|
52
|
+
* }
|
|
53
|
+
* });
|
|
37
54
|
* sdk.use(bannerPlugin);
|
|
38
55
|
* ```
|
|
39
56
|
*/
|
|
@@ -69,16 +86,12 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
69
86
|
left: 0;
|
|
70
87
|
right: 0;
|
|
71
88
|
width: 100%;
|
|
72
|
-
padding: 16px 20px;
|
|
73
89
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
74
90
|
font-size: 14px;
|
|
75
91
|
line-height: 1.5;
|
|
76
|
-
display: flex;
|
|
77
|
-
align-items: center;
|
|
78
|
-
justify-content: space-between;
|
|
79
92
|
box-sizing: border-box;
|
|
80
93
|
z-index: 10000;
|
|
81
|
-
background: #
|
|
94
|
+
background: #ffffff;
|
|
82
95
|
color: #111827;
|
|
83
96
|
border-bottom: 1px solid #e5e7eb;
|
|
84
97
|
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.05);
|
|
@@ -98,33 +111,38 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
98
111
|
.xp-banner__container {
|
|
99
112
|
display: flex;
|
|
100
113
|
align-items: center;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
114
|
+
gap: 16px;
|
|
115
|
+
max-width: 1280px;
|
|
116
|
+
margin: 0 auto;
|
|
117
|
+
padding: 14px 24px;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
.xp-banner__content {
|
|
107
121
|
flex: 1;
|
|
108
122
|
min-width: 0;
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: column;
|
|
125
|
+
gap: 4px;
|
|
109
126
|
}
|
|
110
127
|
|
|
111
128
|
.xp-banner__title {
|
|
112
129
|
font-weight: 600;
|
|
113
|
-
margin
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
margin: 0;
|
|
131
|
+
font-size: 15px;
|
|
132
|
+
line-height: 1.4;
|
|
116
133
|
}
|
|
117
134
|
|
|
118
135
|
.xp-banner__message {
|
|
119
136
|
margin: 0;
|
|
120
137
|
font-size: 14px;
|
|
138
|
+
line-height: 1.5;
|
|
139
|
+
color: #6b7280;
|
|
121
140
|
}
|
|
122
141
|
|
|
123
142
|
.xp-banner__buttons {
|
|
124
143
|
display: flex;
|
|
125
144
|
align-items: center;
|
|
126
|
-
gap:
|
|
127
|
-
flex-wrap: wrap;
|
|
145
|
+
gap: 8px;
|
|
128
146
|
flex-shrink: 0;
|
|
129
147
|
}
|
|
130
148
|
|
|
@@ -137,6 +155,10 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
137
155
|
cursor: pointer;
|
|
138
156
|
transition: all 0.2s;
|
|
139
157
|
text-decoration: none;
|
|
158
|
+
display: inline-flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
justify-content: center;
|
|
161
|
+
white-space: nowrap;
|
|
140
162
|
}
|
|
141
163
|
|
|
142
164
|
.xp-banner__button--primary {
|
|
@@ -149,71 +171,93 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
149
171
|
}
|
|
150
172
|
|
|
151
173
|
.xp-banner__button--secondary {
|
|
152
|
-
background: #
|
|
174
|
+
background: #f3f4f6;
|
|
153
175
|
color: #374151;
|
|
154
|
-
border: 1px solid #
|
|
176
|
+
border: 1px solid #e5e7eb;
|
|
155
177
|
}
|
|
156
178
|
|
|
157
179
|
.xp-banner__button--secondary:hover {
|
|
158
|
-
background: #
|
|
180
|
+
background: #e5e7eb;
|
|
159
181
|
}
|
|
160
182
|
|
|
161
183
|
.xp-banner__button--link {
|
|
162
184
|
background: transparent;
|
|
163
185
|
color: #2563eb;
|
|
164
|
-
padding:
|
|
186
|
+
padding: 6px 12px;
|
|
165
187
|
font-weight: 400;
|
|
166
|
-
text-decoration: underline;
|
|
167
188
|
}
|
|
168
189
|
|
|
169
190
|
.xp-banner__button--link:hover {
|
|
170
|
-
background:
|
|
191
|
+
background: #f3f4f6;
|
|
192
|
+
text-decoration: underline;
|
|
171
193
|
}
|
|
172
194
|
|
|
173
195
|
.xp-banner__close {
|
|
174
196
|
background: transparent;
|
|
175
197
|
border: none;
|
|
176
|
-
color: #
|
|
177
|
-
font-size:
|
|
198
|
+
color: #9ca3af;
|
|
199
|
+
font-size: 20px;
|
|
178
200
|
line-height: 1;
|
|
179
201
|
cursor: pointer;
|
|
180
|
-
padding:
|
|
202
|
+
padding: 4px;
|
|
181
203
|
margin: 0;
|
|
182
|
-
|
|
183
|
-
transition: opacity 0.2s;
|
|
204
|
+
transition: color 0.2s;
|
|
184
205
|
flex-shrink: 0;
|
|
206
|
+
width: 28px;
|
|
207
|
+
height: 28px;
|
|
208
|
+
display: flex;
|
|
209
|
+
align-items: center;
|
|
210
|
+
justify-content: center;
|
|
211
|
+
border-radius: 4px;
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
.xp-banner__close:hover {
|
|
188
|
-
|
|
215
|
+
color: #111827;
|
|
216
|
+
background: #f3f4f6;
|
|
189
217
|
}
|
|
190
218
|
|
|
191
219
|
@media (max-width: 640px) {
|
|
192
220
|
.xp-banner__container {
|
|
193
|
-
flex-
|
|
194
|
-
|
|
221
|
+
flex-wrap: wrap;
|
|
222
|
+
padding: 14px 16px;
|
|
223
|
+
position: relative;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.xp-banner__content {
|
|
227
|
+
flex: 1 1 100%;
|
|
228
|
+
padding-right: 32px;
|
|
195
229
|
}
|
|
196
230
|
|
|
197
231
|
.xp-banner__buttons {
|
|
232
|
+
flex: 1 1 auto;
|
|
198
233
|
width: 100%;
|
|
199
|
-
flex-direction: column;
|
|
200
234
|
}
|
|
201
235
|
|
|
202
236
|
.xp-banner__button {
|
|
203
|
-
|
|
237
|
+
flex: 1;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.xp-banner__close {
|
|
241
|
+
position: absolute;
|
|
242
|
+
top: 12px;
|
|
243
|
+
right: 12px;
|
|
204
244
|
}
|
|
205
245
|
}
|
|
206
246
|
|
|
207
247
|
/* Dark mode support */
|
|
208
248
|
@media (prefers-color-scheme: dark) {
|
|
209
249
|
.xp-banner {
|
|
210
|
-
background: #
|
|
211
|
-
color: #
|
|
212
|
-
border-bottom-color: #
|
|
250
|
+
background: #111827;
|
|
251
|
+
color: #f9fafb;
|
|
252
|
+
border-bottom-color: #1f2937;
|
|
213
253
|
}
|
|
214
254
|
|
|
215
255
|
.xp-banner--bottom {
|
|
216
|
-
border-top-color: #
|
|
256
|
+
border-top-color: #1f2937;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
.xp-banner__message {
|
|
260
|
+
color: #9ca3af;
|
|
217
261
|
}
|
|
218
262
|
|
|
219
263
|
.xp-banner__button--primary {
|
|
@@ -225,21 +269,30 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
225
269
|
}
|
|
226
270
|
|
|
227
271
|
.xp-banner__button--secondary {
|
|
228
|
-
background: #
|
|
229
|
-
color: #
|
|
230
|
-
border-color: #
|
|
272
|
+
background: #1f2937;
|
|
273
|
+
color: #f9fafb;
|
|
274
|
+
border-color: #374151;
|
|
231
275
|
}
|
|
232
276
|
|
|
233
277
|
.xp-banner__button--secondary:hover {
|
|
234
|
-
background: #
|
|
278
|
+
background: #374151;
|
|
235
279
|
}
|
|
236
280
|
|
|
237
281
|
.xp-banner__button--link {
|
|
238
|
-
color: #
|
|
282
|
+
color: #60a5fa;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
.xp-banner__button--link:hover {
|
|
286
|
+
background: #1f2937;
|
|
239
287
|
}
|
|
240
288
|
|
|
241
289
|
.xp-banner__close {
|
|
242
|
-
color: #
|
|
290
|
+
color: #6b7280;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
.xp-banner__close:hover {
|
|
294
|
+
color: #f9fafb;
|
|
295
|
+
background: #1f2937;
|
|
243
296
|
}
|
|
244
297
|
}
|
|
245
298
|
`;
|
|
@@ -307,17 +360,6 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
307
360
|
|
|
308
361
|
container.appendChild(contentDiv);
|
|
309
362
|
|
|
310
|
-
banner.appendChild(contentDiv);
|
|
311
|
-
|
|
312
|
-
// Create button container for actions and/or dismiss
|
|
313
|
-
const buttonContainer = document.createElement('div');
|
|
314
|
-
buttonContainer.style.cssText = `
|
|
315
|
-
display: flex;
|
|
316
|
-
align-items: center;
|
|
317
|
-
gap: 12px;
|
|
318
|
-
flex-wrap: wrap;
|
|
319
|
-
`;
|
|
320
|
-
|
|
321
363
|
// Create buttons container
|
|
322
364
|
const buttonsDiv = document.createElement('div');
|
|
323
365
|
buttonsDiv.className = 'xp-banner__buttons';
|
|
@@ -401,6 +443,49 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
401
443
|
return banner;
|
|
402
444
|
}
|
|
403
445
|
|
|
446
|
+
/**
|
|
447
|
+
* Apply pushDown margin to target element
|
|
448
|
+
*/
|
|
449
|
+
function applyPushDown(banner: HTMLElement, position: 'top' | 'bottom'): void {
|
|
450
|
+
const pushDownSelector = config.get('banner.pushDown');
|
|
451
|
+
|
|
452
|
+
if (!pushDownSelector || position !== 'top') {
|
|
453
|
+
return; // Only push down for top banners
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const targetElement = document.querySelector(pushDownSelector);
|
|
457
|
+
if (!targetElement || !(targetElement instanceof HTMLElement)) {
|
|
458
|
+
return;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// Get banner height
|
|
462
|
+
const height = banner.offsetHeight;
|
|
463
|
+
|
|
464
|
+
// Apply margin-top with transition
|
|
465
|
+
targetElement.style.transition = 'margin-top 0.3s ease';
|
|
466
|
+
targetElement.style.marginTop = `${height}px`;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Remove pushDown margin from target element
|
|
471
|
+
*/
|
|
472
|
+
function removePushDown(): void {
|
|
473
|
+
const pushDownSelector = config.get('banner.pushDown');
|
|
474
|
+
|
|
475
|
+
if (!pushDownSelector) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
const targetElement = document.querySelector(pushDownSelector);
|
|
480
|
+
if (!targetElement || !(targetElement instanceof HTMLElement)) {
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Remove margin-top with transition
|
|
485
|
+
targetElement.style.transition = 'margin-top 0.3s ease';
|
|
486
|
+
targetElement.style.marginTop = '0';
|
|
487
|
+
}
|
|
488
|
+
|
|
404
489
|
/**
|
|
405
490
|
* Show a banner experience
|
|
406
491
|
*/
|
|
@@ -419,6 +504,11 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
419
504
|
document.body.appendChild(banner);
|
|
420
505
|
activeBanners.set(experience.id, banner);
|
|
421
506
|
|
|
507
|
+
// Apply pushDown to target element if configured
|
|
508
|
+
const content = experience.content as BannerContent;
|
|
509
|
+
const position = content.position ?? config.get('banner.position') ?? 'top';
|
|
510
|
+
applyPushDown(banner, position);
|
|
511
|
+
|
|
422
512
|
instance.emit('experiences:shown', {
|
|
423
513
|
experienceId: experience.id,
|
|
424
514
|
type: 'banner',
|
|
@@ -437,6 +527,11 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
437
527
|
banner.parentNode.removeChild(banner);
|
|
438
528
|
}
|
|
439
529
|
activeBanners.delete(experienceId);
|
|
530
|
+
|
|
531
|
+
// Remove pushDown if no more banners
|
|
532
|
+
if (activeBanners.size === 0) {
|
|
533
|
+
removePushDown();
|
|
534
|
+
}
|
|
440
535
|
} else {
|
|
441
536
|
// Remove all banners
|
|
442
537
|
for (const [id, banner] of activeBanners.entries()) {
|
|
@@ -445,6 +540,9 @@ export const bannerPlugin: PluginFunction = (plugin, instance, config) => {
|
|
|
445
540
|
}
|
|
446
541
|
activeBanners.delete(id);
|
|
447
542
|
}
|
|
543
|
+
|
|
544
|
+
// Remove pushDown
|
|
545
|
+
removePushDown();
|
|
448
546
|
}
|
|
449
547
|
}
|
|
450
548
|
|