@magicfeedback/popup-sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +372 -0
- package/dist/index.css +730 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.mts +190 -0
- package/dist/index.d.ts +190 -0
- package/dist/index.js +2261 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2251 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +61 -0
package/README.md
ADDED
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
# deepdots-popup-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK to render and manage survey popups for MagicFeedback.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install deepdots-popup-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { DeepdotsPopups } from 'deepdots-popup-sdk';
|
|
15
|
+
|
|
16
|
+
// Initialize the SDK (basic)
|
|
17
|
+
const popups = new DeepdotsPopups();
|
|
18
|
+
popups.init({
|
|
19
|
+
apiKey: 'your-api-key',
|
|
20
|
+
debug: true // Optional: enable debug logging
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Show a popup immediately (productId now required)
|
|
24
|
+
popups.show({
|
|
25
|
+
surveyId: 'survey-123',
|
|
26
|
+
productId: 'product-xyz'
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// Listen to events
|
|
30
|
+
popups.on('popup_shown', (event) => {
|
|
31
|
+
console.log('Popup shown:', event);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
popups.on('survey_completed', (event) => {
|
|
35
|
+
console.log('Survey completed:', event);
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Features
|
|
40
|
+
|
|
41
|
+
- ✅ Simple API: Initialize and show popups with minimal code
|
|
42
|
+
- ✅ Auto-launch Triggers: Time-based, scroll-based, and exit-intent triggers
|
|
43
|
+
- ✅ Automatic trigger derivation from remote popup definitions (time_on_page → time, etc.)
|
|
44
|
+
- ✅ Conditions & Cooldowns: Avoid showing to answered users or during cooldown periods
|
|
45
|
+
- ✅ Event System: Listen to popup_shown, popup_clicked, and survey_completed events
|
|
46
|
+
- ✅ TypeScript: Full TypeScript support with type definitions
|
|
47
|
+
- ✅ Lightweight: Minimal dependencies
|
|
48
|
+
- ✅ Framework Agnostic: Works with any framework or vanilla JavaScript
|
|
49
|
+
|
|
50
|
+
## API Reference
|
|
51
|
+
|
|
52
|
+
### `DeepdotsPopups`
|
|
53
|
+
|
|
54
|
+
Main class for managing survey popups.
|
|
55
|
+
|
|
56
|
+
#### Methods
|
|
57
|
+
|
|
58
|
+
##### `init(config: DeepdotsInitParams): void`
|
|
59
|
+
|
|
60
|
+
Initialize the SDK with configuration.
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
popups.init({
|
|
64
|
+
apiKey: 'your-api-key',
|
|
65
|
+
nodeEnv: 'production', // Optional: 'development' | 'production' (affects baseUrl)
|
|
66
|
+
mode: 'client', // Optional: 'client' (use provided popups) | 'server' (fetch remote popups)
|
|
67
|
+
debug: false, // Optional
|
|
68
|
+
popups: [ // Optional: preload popup definitions (client mode)
|
|
69
|
+
{
|
|
70
|
+
id: 'popup-123',
|
|
71
|
+
title: 'Would you like to help us improve?',
|
|
72
|
+
message: '<p>Take a quick survey and share your thoughts.</p>',
|
|
73
|
+
trigger: { type: 'time_on_page', value: 5, condition: [{ answered: false, cooldownDays: 7 }] },
|
|
74
|
+
actions: {
|
|
75
|
+
accept: { label: 'Take Survey', surveyId: 'survey-abc' },
|
|
76
|
+
decline: { label: 'Not Now', cooldownDays: 7 }
|
|
77
|
+
},
|
|
78
|
+
surveyId: 'survey-abc',
|
|
79
|
+
productId: 'product-xyz',
|
|
80
|
+
style: { theme: 'light', position: 'bottom-right', imageUrl: null },
|
|
81
|
+
segments: { lang: ['en'], path: ['/checkout'] }
|
|
82
|
+
}
|
|
83
|
+
]
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The SDK resolves `baseUrl` internally from `nodeEnv` (defaults to production) and will convert `time_on_page` trigger values (seconds) into internal `time` triggers (milliseconds).
|
|
88
|
+
|
|
89
|
+
##### `show(options: ShowOptions): void`
|
|
90
|
+
|
|
91
|
+
Show a popup immediately.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
popups.show({
|
|
95
|
+
surveyId: 'survey-123',
|
|
96
|
+
productId: 'product-xyz', // REQUIRED now
|
|
97
|
+
data: { // Optional custom data passed to events
|
|
98
|
+
userId: 'user-456',
|
|
99
|
+
source: 'homepage'
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
##### `showByPopupId(popupId: string): void`
|
|
105
|
+
|
|
106
|
+
Show a popup using its definition `id` (will internally map to `surveyId`).
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
popups.showByPopupId('popup-123');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
##### `configureTriggers(triggers: TriggerConfig[]): void`
|
|
113
|
+
|
|
114
|
+
Manually configure triggers for auto-launching popups.
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
popups.configureTriggers([
|
|
118
|
+
{ type: 'time', value: 5000, surveyId: 'survey-123' },
|
|
119
|
+
{ type: 'scroll', value: 50, surveyId: 'survey-456' },
|
|
120
|
+
{ type: 'exit', surveyId: 'survey-789' }
|
|
121
|
+
]);
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
##### `autoLaunch(): void`
|
|
125
|
+
|
|
126
|
+
Enable auto-launch functionality with previously configured triggers OR triggers derived automatically from popup definitions.
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
popups.autoLaunch();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
##### `triggerSurvey(surveyId: string): void`
|
|
133
|
+
|
|
134
|
+
Internal trigger evaluation + display (used by trigger system). You normally don't call this directly; use `configureTriggers` + `autoLaunch`.
|
|
135
|
+
|
|
136
|
+
##### `markSurveyAnswered(surveyId: string): void`
|
|
137
|
+
|
|
138
|
+
Manually mark a survey as answered (affects future conditions like `answered: false`).
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
popups.markSurveyAnswered('survey-123');
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
##### `on(eventType: DeepdotsEventType, listener: EventListener): void`
|
|
145
|
+
|
|
146
|
+
Add an event listener.
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
popups.on('popup_shown', (event) => {
|
|
150
|
+
console.log('Event:', event);
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
##### `off(eventType: DeepdotsEventType, listener: EventListener): void`
|
|
155
|
+
|
|
156
|
+
Remove an event listener.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const listener = (event) => console.log(event);
|
|
160
|
+
popups.on('popup_shown', listener);
|
|
161
|
+
popups.off('popup_shown', listener);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Popup Definitions (Advanced)
|
|
165
|
+
|
|
166
|
+
When operating in `client` mode you can preload popup definitions via `popups` in `init()`. In `server` mode the SDK (currently) simulates a fetch and loads remote definitions asynchronously.
|
|
167
|
+
|
|
168
|
+
Structure:
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
interface PopupDefinition {
|
|
172
|
+
id: string;
|
|
173
|
+
title: string;
|
|
174
|
+
message: string; // HTML string
|
|
175
|
+
trigger: {
|
|
176
|
+
type: 'time_on_page' | 'scroll' | 'exit';
|
|
177
|
+
value: number; // seconds for time_on_page, percentage for scroll
|
|
178
|
+
condition?: { answered: boolean; cooldownDays: number; }[];
|
|
179
|
+
};
|
|
180
|
+
actions: {
|
|
181
|
+
accept: { label: string; surveyId: string };
|
|
182
|
+
decline: { label: string; cooldownDays?: number };
|
|
183
|
+
};
|
|
184
|
+
surveyId: string;
|
|
185
|
+
productId: string;
|
|
186
|
+
style: { theme: 'light' | 'dark'; position: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'center'; imageUrl: string | null };
|
|
187
|
+
segments?: { lang?: string[]; path?: string[]; [key: string]: unknown };
|
|
188
|
+
}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Conditions inside `trigger.condition` are evaluated before showing:
|
|
192
|
+
|
|
193
|
+
- `answered: false` → only show if the survey hasn't been completed yet
|
|
194
|
+
- `cooldownDays: X` → don't show again until X days have passed since last display
|
|
195
|
+
|
|
196
|
+
### Events
|
|
197
|
+
|
|
198
|
+
The SDK emits the following events:
|
|
199
|
+
|
|
200
|
+
- `popup_shown`: Fired when a popup is displayed
|
|
201
|
+
- `popup_clicked`: Fired on the first interaction (form load or manual click)
|
|
202
|
+
- `survey_completed`: Fired when a user completes the survey
|
|
203
|
+
|
|
204
|
+
Each event includes:
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
{
|
|
208
|
+
type: 'popup_shown' | 'popup_clicked' | 'survey_completed',
|
|
209
|
+
surveyId: string,
|
|
210
|
+
timestamp: number,
|
|
211
|
+
data?: Record<string, unknown>
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Trigger Types (Manual Configuration)
|
|
216
|
+
|
|
217
|
+
#### Time Trigger (`type: 'time'`)
|
|
218
|
+
Show popup after a specified delay in milliseconds.
|
|
219
|
+
|
|
220
|
+
```typescript
|
|
221
|
+
{ type: 'time', value: 5000, surveyId: 'survey-123' }
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
#### Scroll Trigger (`type: 'scroll'`)
|
|
225
|
+
Show popup when user scrolls to a certain percentage of the page.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
{ type: 'scroll', value: 50, surveyId: 'survey-123' }
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
#### Exit Intent Trigger (`type: 'exit'`)
|
|
232
|
+
Show popup when user moves mouse to exit the page.
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
{ type: 'exit', surveyId: 'survey-123' }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Automatic Trigger Derivation
|
|
239
|
+
When using popup definitions, remote trigger types like `time_on_page` are mapped to internal `time` triggers (seconds → milliseconds) and started automatically after `autoLaunch()`.
|
|
240
|
+
|
|
241
|
+
## Styling & Container
|
|
242
|
+
The SDK creates a container with id `deepdots-popup-container` covering the viewport and renders a popup inside with a basic layout. You can override styles via global CSS targeting `.deepdots-popup` or the container id.
|
|
243
|
+
|
|
244
|
+
## Examples
|
|
245
|
+
|
|
246
|
+
See the [examples](./examples) directory for a complete demo.
|
|
247
|
+
|
|
248
|
+
To run the demo:
|
|
249
|
+
|
|
250
|
+
1. Open `examples/demo.html` in your browser
|
|
251
|
+
2. Try different triggers and see events in the console
|
|
252
|
+
|
|
253
|
+
## Development
|
|
254
|
+
|
|
255
|
+
### Install Dependencies
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
npm install
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### Build
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
npm run build
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Lint
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
npm run lint
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Test
|
|
274
|
+
|
|
275
|
+
```bash
|
|
276
|
+
npm test
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Watch Mode (Tests)
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
npm run test:watch
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## TypeScript Types
|
|
286
|
+
|
|
287
|
+
The package includes full TypeScript type definitions. Import types as needed:
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
import type {
|
|
291
|
+
DeepdotsConfig,
|
|
292
|
+
TriggerConfig,
|
|
293
|
+
ShowOptions,
|
|
294
|
+
DeepdotsEvent,
|
|
295
|
+
DeepdotsEventType,
|
|
296
|
+
EventListener
|
|
297
|
+
} from 'deepdots-popup-sdk';
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Plataforma y Adaptación a Mobile
|
|
301
|
+
|
|
302
|
+
Desde la versión actual se introdujo una abstracción de renderizado (`PopupRenderer`) que permite portar la lógica de estado y triggers a otros entornos.
|
|
303
|
+
|
|
304
|
+
### Arquitectura
|
|
305
|
+
- Core (triggers, condiciones, eventos, definiciones) es independiente del DOM.
|
|
306
|
+
- `BrowserPopupRenderer` implementa la UI sobre el DOM (usa `renderPopup`).
|
|
307
|
+
- `NoopPopupRenderer` evita errores en SSR / pruebas sin DOM.
|
|
308
|
+
|
|
309
|
+
### Cómo usar en Android / iOS (WebView)
|
|
310
|
+
Caso rápido (Cordova / Capacitor / React Native WebView / Flutter WebView):
|
|
311
|
+
1. Construye el bundle (`npm run build`).
|
|
312
|
+
2. Sirve o empaqueta los archivos de `dist/` dentro de un WebView.
|
|
313
|
+
3. Expone una API puente (bridge) para recibir eventos (`popup_shown`, `survey_completed`) usando `window.ReactNativeWebView.postMessage` (RN) o `webView.evaluateJavascript` (Android nativo).
|
|
314
|
+
4. Inyecta script de inicialización: `popups.init({...}); popups.autoLaunch();`.
|
|
315
|
+
|
|
316
|
+
### Implementación nativa (sin WebView)
|
|
317
|
+
Para una experiencia 100% nativa se escribiría un nuevo renderer:
|
|
318
|
+
```typescript
|
|
319
|
+
class ReactNativePopupRenderer implements PopupRenderer {
|
|
320
|
+
init() { /* preparar store/modal */ }
|
|
321
|
+
show(surveyId, productId, data, emit, onClose) {
|
|
322
|
+
// 1. Mostrar un Modal nativo
|
|
323
|
+
// 2. Renderizar componentes equivalentes
|
|
324
|
+
// 3. Integrar MagicFeedback vía API HTTP si el SDK web no está disponible
|
|
325
|
+
emit('popup_clicked', surveyId, data); // cuando el usuario interactúe
|
|
326
|
+
}
|
|
327
|
+
hide() { /* cerrar modal */ }
|
|
328
|
+
}
|
|
329
|
+
```
|
|
330
|
+
Luego se inyecta:
|
|
331
|
+
```typescript
|
|
332
|
+
const popups = new DeepdotsPopups();
|
|
333
|
+
popups.setRenderer(new ReactNativePopupRenderer()); // inyectar renderer nativo
|
|
334
|
+
popups.init({...});
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Pasos para extender
|
|
338
|
+
1. Crear archivo `src/platform/react-native-renderer.ts`.
|
|
339
|
+
2. Implementar interfaz `PopupRenderer`.
|
|
340
|
+
3. Exportar factoría condicional (detectar `global.navigator.product === 'ReactNative'`).
|
|
341
|
+
4. Reemplazar `createDefaultRenderer()` por lógica de detección.
|
|
342
|
+
|
|
343
|
+
### Eventos & Bridge
|
|
344
|
+
Ejemplo puente en React Native:
|
|
345
|
+
```js
|
|
346
|
+
window.addEventListener('message', (e) => {
|
|
347
|
+
// recibir comandos desde native -> web
|
|
348
|
+
});
|
|
349
|
+
// Envío desde web a native
|
|
350
|
+
function forwardEvent(ev) {
|
|
351
|
+
window.ReactNativeWebView?.postMessage(JSON.stringify(ev));
|
|
352
|
+
}
|
|
353
|
+
popups.on('popup_shown', forwardEvent);
|
|
354
|
+
popups.on('survey_completed', forwardEvent);
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Ventajas
|
|
358
|
+
- Reutilizas lógica de triggers sin duplicar código.
|
|
359
|
+
- Puedes iterar primero con WebView y luego migrar a UI nativa.
|
|
360
|
+
- Tests siguen funcionando porque usan el renderer de navegador.
|
|
361
|
+
|
|
362
|
+
## License
|
|
363
|
+
|
|
364
|
+
MIT
|
|
365
|
+
|
|
366
|
+
## Contributing
|
|
367
|
+
|
|
368
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
369
|
+
|
|
370
|
+
## Support
|
|
371
|
+
|
|
372
|
+
For issues and questions, please visit the [GitHub repository](https://github.com/MagicFeedback/deepdots-popup-sdk).
|