@magicfeedback/popup-sdk 1.3.0 → 1.4.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.
@@ -24,6 +24,10 @@ var POPUP_TRIGGER_CONDITION_STATUSES = [
24
24
  ];
25
25
 
26
26
  // src/triggers/index.ts
27
+ function toNumericValue(value, fallback) {
28
+ const n = Number(value);
29
+ return Number.isFinite(n) ? n : fallback;
30
+ }
27
31
  function setupTrigger(popups, trigger) {
28
32
  switch (trigger.type) {
29
33
  case "time":
@@ -44,14 +48,14 @@ function setupTrigger(popups, trigger) {
44
48
  }
45
49
  }
46
50
  function setupTimeTrigger(popups, trigger) {
47
- const delay = trigger.value || 5e3;
51
+ const delay = toNumericValue(trigger.value, 5e3);
48
52
  setTimeout(() => {
49
53
  popups.triggerSurvey(trigger.surveyId, trigger.popupId);
50
54
  }, delay);
51
55
  popups.debug(`Time trigger set for ${delay}ms`);
52
56
  }
53
57
  function setupScrollTrigger(popups, trigger) {
54
- const threshold = trigger.value || 50;
58
+ const threshold = toNumericValue(trigger.value, 50);
55
59
  const handler = () => {
56
60
  const scrollPercentage = window.scrollY / (document.documentElement.scrollHeight - window.innerHeight) * 100;
57
61
  if (scrollPercentage >= threshold) {
@@ -226,6 +230,16 @@ function buildSurveyHtml(opts) {
226
230
  const colorScheme = isDark ? "dark" : "light";
227
231
  const textPrimary = isDark ? "#f0f0f0" : "#111";
228
232
  const pos = POSITION_MAP[opts.position ?? "center"] ?? POSITION_MAP.center;
233
+ const chrome = opts.chrome !== false;
234
+ const bodyBg = chrome ? pos.background : "transparent";
235
+ const bodyPadding = chrome ? pos.padding : "0";
236
+ const bodyDisplay = chrome ? "flex" : "block";
237
+ const cardBg = chrome ? popupBg : "transparent";
238
+ const cardShadow = chrome ? "0 4px 6px rgba(0,0,0,0.1)" : "none";
239
+ const cardRadius = chrome ? "8px" : "0";
240
+ const cardMaxWidth = chrome ? "600px" : "none";
241
+ const cardWidth = chrome ? "90%" : "100%";
242
+ const cardMinHeight = chrome ? "200px" : "0";
229
243
  const backLabel = JSON.stringify(opts.actions?.back?.label ?? "Back");
230
244
  const startLabel = JSON.stringify(opts.actions?.start?.label ?? "Start survey");
231
245
  const completeLabel = JSON.stringify(opts.actions?.complete?.label ?? "Complete survey");
@@ -237,8 +251,8 @@ function buildSurveyHtml(opts) {
237
251
  ${style_default}
238
252
  ${fontFaceCss}
239
253
  html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
240
- body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${pos.background};padding:${pos.padding};box-sizing:border-box}
241
- .deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${popupBg};color-scheme:${colorScheme};border-radius:8px;padding:24px;box-shadow:0 4px 6px rgba(0,0,0,0.1);max-width:600px;width:90%;min-height:200px;box-sizing:border-box}
254
+ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${bodyBg};padding:${bodyPadding};box-sizing:border-box}
255
+ .deepdots-popup{position:relative;display:flex;flex-direction:column;justify-content:flex-start;background:${cardBg};color-scheme:${colorScheme};border-radius:${cardRadius};padding:24px;box-shadow:${cardShadow};max-width:${cardMaxWidth};width:${cardWidth};min-height:${cardMinHeight};box-sizing:border-box}
242
256
  .deepdots-popup-header{display:flex;justify-content:flex-end;align-items:center;width:100%}
243
257
  #dd-close{background:transparent;border:none;width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:8px;cursor:pointer;color:${textPrimary};padding:4px}
244
258
  .deepdots-popup-container-content{display:flex;flex-direction:column;padding:0 20px 12px 20px;max-height:80vh;overflow:hidden;box-sizing:border-box}
@@ -296,6 +310,7 @@ body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignI
296
310
  function emit(s){ try{ if(window.ReactNativeWebView){ window.ReactNativeWebView.postMessage(s); } }catch(e){} }
297
311
  function emitJSON(n,p){ try{ emit(JSON.stringify({name:n,payload:p||{}})); }catch(e){} }
298
312
  window.DeepdotsClose = function(){ emitJSON('popup_close'); };
313
+ var chromeOn=${chrome ? "true" : "false"};
299
314
 
300
315
  var popup=document.getElementById('dd-popup');
301
316
  var main=document.getElementById('dd-main');
@@ -371,7 +386,7 @@ body{display:flex;justify-content:${pos.justifyContent};align-items:${pos.alignI
371
386
  var s=args && args.formData ? args.formData.style : null;
372
387
  if(s && !stylesInjected){
373
388
  stylesInjected=true;
374
- if(s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
389
+ if(chromeOn && s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
375
390
  if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
376
391
  if(s.buttonPrimaryColor){
377
392
  submitBtn.style.background=s.buttonPrimaryColor; submitBtn.style.border='none'; submitBtn.style.color='#fff';
@@ -460,7 +475,7 @@ var ReactNativePopupRenderer = class {
460
475
  }
461
476
  init() {
462
477
  }
463
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
478
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, renderChrome) {
464
479
  this.emitFn = emit;
465
480
  this.onCloseFn = onClose;
466
481
  this.currentSurveyId = surveyId;
@@ -475,7 +490,9 @@ var ReactNativePopupRenderer = class {
475
490
  font: style?.font,
476
491
  theme: style?.theme,
477
492
  position: style?.position,
478
- actions
493
+ actions,
494
+ // renderChrome:false (init) → HTML sin tarjeta/overlay: el host lo envuelve en su Modal.
495
+ chrome: renderChrome
479
496
  });
480
497
  if (this.options.onShow) {
481
498
  this.options.onShow({ surveyId, productId, html });
@@ -566,13 +583,13 @@ var BrowserPopupRenderer = class {
566
583
  document.body.appendChild(this.container);
567
584
  }
568
585
  }
569
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
586
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome) {
570
587
  if (this.visible) return;
571
588
  if (!this.container || !document.body.contains(this.container)) this.init();
572
589
  if (!this.container) return;
573
590
  this.visible = true;
574
591
  const container = this.container;
575
- void import("./renderPopup-ZYXOUBCP.mjs").then(({ renderPopup }) => {
592
+ void import("./renderPopup-UYMGEBSI.mjs").then(({ renderPopup }) => {
576
593
  renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId);
577
594
  });
578
595
  }
@@ -1420,6 +1437,8 @@ var DeepdotsPopups = class {
1420
1437
  this.deferredExitTimers = [];
1421
1438
  this.baseUrl = "";
1422
1439
  this.env = "production";
1440
+ /** Si el SDK pinta el "modal" del popup (RN). Default true; false = el host lo envuelve. */
1441
+ this.renderChrome = true;
1423
1442
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
1424
1443
  this.tracking = null;
1425
1444
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -1470,6 +1489,7 @@ var DeepdotsPopups = class {
1470
1489
  const env = resolveEnvironment(config.nodeEnv);
1471
1490
  this.baseUrl = env.apiBaseUrl;
1472
1491
  this.env = config.nodeEnv || "production";
1492
+ this.renderChrome = config.renderChrome ?? true;
1473
1493
  this.config = {
1474
1494
  apiKey: config.apiKey || void 0,
1475
1495
  debug: config.debug || false,
@@ -1655,6 +1675,22 @@ var DeepdotsPopups = class {
1655
1675
  trackFunnelStep(funnel, step, taskId, params) {
1656
1676
  this.track("deepdots_funnel_step", { funnel, step, task_id: taskId, ...params ?? {} });
1657
1677
  }
1678
+ /**
1679
+ * Product Effectiveness: una interacción significativa del usuario (`interaction_type` es la
1680
+ * dimensión de agrupación, p. ej. `get_help`, `homepage`, `contact_support`).
1681
+ *
1682
+ * Existe como evento RESERVADO para que la convención no la invente cada host: un `track()`
1683
+ * custom acaba como `deepdots_event_…` y no lo cuenta la analítica de effectiveness.
1684
+ * Un `interactionType` vacío se descarta con warning (no aporta dato agrupable).
1685
+ */
1686
+ trackMeaningfulInteraction(interactionType, params) {
1687
+ const type = typeof interactionType === "string" ? interactionType.trim() : "";
1688
+ if (!type) {
1689
+ console.warn("[DeepdotsPopups] trackMeaningfulInteraction discarded: empty interaction_type");
1690
+ return;
1691
+ }
1692
+ this.track("deepdots_meaningful_interaction", { interaction_type: type, ...params ?? {} });
1693
+ }
1658
1694
  /**
1659
1695
  * Messaging (#18–22): registra una etapa del funnel de una notificación (push/in-app).
1660
1696
  * No-op si tracking off. El evento se descarta (con warning) si el `channel` no es válido,
@@ -2255,13 +2291,14 @@ var DeepdotsPopups = class {
2255
2291
  style,
2256
2292
  sessionId,
2257
2293
  miniService,
2258
- analyticsFeedbackSessionId
2294
+ analyticsFeedbackSessionId,
2295
+ this.renderChrome
2259
2296
  );
2260
2297
  return;
2261
2298
  }
2262
2299
  if (!this.popupContainer) return;
2263
2300
  const container = this.popupContainer;
2264
- void import("./renderPopup-ZYXOUBCP.mjs").then(({ renderPopup }) => {
2301
+ void import("./renderPopup-UYMGEBSI.mjs").then(({ renderPopup }) => {
2265
2302
  renderPopup(
2266
2303
  container,
2267
2304
  surveyId,