@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 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).