@magicfeedback/popup-sdk 1.14.0 → 1.16.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.
@@ -18,7 +18,7 @@ import {
18
18
  sdkWarn,
19
19
  setLogger,
20
20
  style_default
21
- } from "./chunk-GUJSOVS6.mjs";
21
+ } from "./chunk-VHTN5UXD.mjs";
22
22
 
23
23
  // src/react-native/index.tsx
24
24
  import { createContext, useContext, useEffect, useRef, useState } from "react";
@@ -294,12 +294,14 @@ function buildSurveyHtml(opts) {
294
294
  const startLabel = jsonForScript(initActionLabels.start);
295
295
  const completeLabel = jsonForScript(initActionLabels.complete);
296
296
  const submitLabel = jsonForScript(initActionLabels.accept);
297
+ const cancelLabel = jsonForScript(initActionLabels.decline);
297
298
  const labelsTableJson = jsonForScript(LABELS);
298
299
  const actionOverridesJson = jsonForScript({
299
300
  accept: trimmedOrNull(opts.actions?.accept?.label),
300
301
  start: trimmedOrNull(opts.actions?.start?.label),
301
302
  complete: trimmedOrNull(opts.actions?.complete?.label),
302
- back: trimmedOrNull(opts.actions?.back?.label)
303
+ back: trimmedOrNull(opts.actions?.back?.label),
304
+ decline: trimmedOrNull(opts.actions?.decline?.label)
303
305
  });
304
306
  const initLangJson = jsonForScript(opts.language ?? "");
305
307
  const initDirection = directionFor(opts.language);
@@ -361,6 +363,7 @@ body.dd-ready #dd-popup{visibility:visible}
361
363
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
362
364
  .deepdots-error-hint{display:none;margin:12px 0 0 0;padding:10px 12px;border-radius:6px;background:#FEF3C7;color:#92400E;border:1px solid #FCD34D;font-size:13px}
363
365
  /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
366
+ .deepdots-load-error{display:none;width:100%;text-align:center;padding:24px 0;font-size:15px;color:${textPrimary}}
364
367
  .deepdots-success{display:none;width:100%;text-align:center;padding:24px 0;color:${textPrimary}}
365
368
  .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
366
369
  .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
@@ -368,7 +371,7 @@ body.dd-ready #dd-popup{visibility:visible}
368
371
  .dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:999px;cursor:pointer;font-size:15px;font-weight:600;align-items:center;justify-content:center;text-align:center}
369
372
  /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
370
373
  #dd-back{background:transparent;color:${textMuted};border:none}
371
- #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
374
+ #dd-start,#dd-complete,#dd-submit,#dd-cancel{background:#1E293B;color:#fff;border:none}
372
375
  #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
373
376
  .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
374
377
  .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
@@ -409,6 +412,7 @@ ${customCss}
409
412
  <div class="mf-spinner" id="dd-spinner" role="status" aria-label="${initLabels.loadingAria}"><div class="mf-spinner-circle"></div></div>
410
413
  <div id="mf"></div>
411
414
  </div>
415
+ <div class="deepdots-load-error" id="dd-load-error" role="alert"></div>
412
416
  <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
413
417
  </div>
414
418
  <div class="deepdots-popup-footer" id="dd-footer">
@@ -416,6 +420,7 @@ ${customCss}
416
420
  <button type="button" class="dd-nav-btn" id="dd-back"></button>
417
421
  <button type="button" class="dd-nav-btn" id="dd-complete"></button>
418
422
  <button type="button" class="dd-nav-btn" id="dd-start"></button>
423
+ <button type="button" class="dd-nav-btn" id="dd-cancel"></button>
419
424
  </div>
420
425
  </div>
421
426
  </div>
@@ -438,6 +443,8 @@ ${customCss}
438
443
  var startBtn=document.getElementById('dd-start');
439
444
  var completeBtn=document.getElementById('dd-complete');
440
445
  var submitBtn=document.getElementById('dd-submit');
446
+ var cancelBtn=document.getElementById('dd-cancel');
447
+ var loadErrorEl=document.getElementById('dd-load-error');
441
448
  var titleEl=document.getElementById('dd-title');
442
449
  var progressEl=document.getElementById('dd-progress');
443
450
  var progressLabel=document.getElementById('dd-progress-label');
@@ -467,6 +474,8 @@ ${customCss}
467
474
  startBtn.textContent=DD_ACTION_OVERRIDES.start||next.start;
468
475
  completeBtn.textContent=DD_ACTION_OVERRIDES.complete||next.complete;
469
476
  submitBtn.textContent=DD_ACTION_OVERRIDES.accept||next.accept;
477
+ cancelBtn.textContent=DD_ACTION_OVERRIDES.decline||next.decline;
478
+ loadErrorEl.textContent=next.errorLoad;
470
479
  if(closeEl){ closeEl.setAttribute('aria-label',next.closeAria); }
471
480
  if(spinner){ spinner.setAttribute('aria-label',next.loadingAria); }
472
481
  progressFollowUp.textContent=next.followUp;
@@ -521,6 +530,7 @@ ${customCss}
521
530
  startBtn.textContent=${startLabel};
522
531
  completeBtn.textContent=${completeLabel};
523
532
  submitBtn.textContent=${submitLabel};
533
+ cancelBtn.textContent=${cancelLabel};
524
534
 
525
535
  closeEl.onclick=function(){ emitJSON('popup_close'); };
526
536
  backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
@@ -532,6 +542,8 @@ ${customCss}
532
542
  }
533
543
  };
534
544
  completeBtn.onclick=function(){ emitJSON('popup_close'); };
545
+ // \xDAnica acci\xF3n del survey que no carga: cierra igual que la X.
546
+ cancelBtn.onclick=function(){ emitJSON('popup_close'); };
535
547
  var surveyCompletedEmitted=false;
536
548
  submitBtn.onclick=function(){
537
549
  if(surveyCompletedEmitted) return;
@@ -573,6 +585,7 @@ ${customCss}
573
585
  startBtn.style.display='none';
574
586
  submitBtn.style.display='none';
575
587
  completeBtn.style.display='none';
588
+ cancelBtn.style.display='none';
576
589
  backBtn.style.width='';
577
590
  startBtn.style.width='';
578
591
  submitBtn.style.width='';
@@ -588,6 +601,8 @@ ${customCss}
588
601
  } else if(state==='completed'){
589
602
  completeBtn.style.display='inline-flex';
590
603
  completeBtn.style.width='100%';
604
+ } else if(state==='load_error'){
605
+ cancelBtn.style.display='inline-flex';
591
606
  }
592
607
  }
593
608
 
@@ -634,6 +649,26 @@ ${REVEAL_JS}
634
649
  }
635
650
  setLoading(true);
636
651
 
652
+ // Survey que no ha cargado (406 de /info, sin red, bundle del CDN ca\xEDdo). Antes se emit\xEDa
653
+ // \`loaded\` y se pintaba un Send sobre un survey vac\xEDo. Espejo de KMP: aviso traducido, la X
654
+ // sigue, un \xFAnico Cancel, y se revela ya (manda \`ready\`): no hay nada m\xE1s que esperar.
655
+ // No es interacci\xF3n del usuario: el renderer no lo convierte en popup_clicked.
656
+ var ddLoadFailed=false;
657
+ function ddShowLoadError(name, reason){
658
+ if(ddLoadFailed){ return; }
659
+ ddLoadFailed=true;
660
+ var why=(reason && reason.message) ? reason.message : String(reason);
661
+ emitJSON(name, { message: why });
662
+ progressEl.style.display='none';
663
+ formWrapper.style.display='none';
664
+ errorHint.style.display='none';
665
+ loadErrorEl.textContent=DD_LABELS_ACTIVE.errorLoad;
666
+ loadErrorEl.style.display='block';
667
+ setLoading(false);
668
+ updateButtons('load_error');
669
+ ddReveal.reveal();
670
+ }
671
+
637
672
  var initialized=false;
638
673
  function initMF(){
639
674
  if(initialized || !window.magicfeedback) return initialized;
@@ -657,6 +692,7 @@ ${REVEAL_JS}
657
692
  // de native sin esta opci\xF3n la ignoran.
658
693
  autofocus:'navigation',
659
694
  onLoadedEvent:function(args){
695
+ if(args && args.error){ ddShowLoadError('error:load', args.error); return; }
660
696
  // Idioma en el que native muestra el survey: manda sobre el del init para que el
661
697
  // chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
662
698
  // env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
@@ -760,13 +796,13 @@ ${REVEAL_JS}
760
796
  updateProgress({ progress:p&&p.progress, total:form.total, completed:false, followup:p&&p.followup });
761
797
  emitJSON('back', p);
762
798
  }
763
- }).catch(function(e){ setLoading(false); emit('error:init'); });
764
- }catch(e){ setLoading(false); emit('error:exception'); }
799
+ }).catch(function(e){ ddShowLoadError('error:init', e); });
800
+ }catch(e){ ddShowLoadError('error:exception', e); }
765
801
  return true;
766
802
  }
767
803
  var s=document.createElement('script'); s.src=${JSON.stringify(cdn)}; s.async=true;
768
804
  s.onload=function(){ initMF(); };
769
- s.onerror=function(){ setLoading(false); emit('error:load'); };
805
+ s.onerror=function(){ ddShowLoadError('error:load', 'survey script failed to load'); };
770
806
  document.head.appendChild(s);
771
807
  var tries=0, t=setInterval(function(){ if(initMF()||++tries>40){ clearInterval(t); } },150);
772
808
  })();
@@ -883,6 +919,7 @@ var ReactNativePopupRenderer = class {
883
919
  this.onCloseFn?.();
884
920
  break;
885
921
  default:
922
+ if (typeof name === "string" && name.startsWith("error:")) this.emitReady(surveyId);
886
923
  break;
887
924
  }
888
925
  }
@@ -895,7 +932,7 @@ function createReactNativeRenderer() {
895
932
  }
896
933
 
897
934
  // src/ui/preload.ts
898
- var defaultLoader = () => import("./renderPopup-FAEZGWRS.mjs");
935
+ var defaultLoader = () => import("./renderPopup-S3RU3Y6J.mjs");
899
936
  var pending = null;
900
937
  function preloadRenderPopup(loader = defaultLoader) {
901
938
  if (pending) return pending;
@@ -968,7 +1005,7 @@ var BrowserPopupRenderer = class {
968
1005
  if (!this.container) return;
969
1006
  this.visible = true;
970
1007
  const container = this.container;
971
- void import("./renderPopup-FAEZGWRS.mjs").then(({ renderPopup }) => {
1008
+ void import("./renderPopup-S3RU3Y6J.mjs").then(({ renderPopup }) => {
972
1009
  renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
973
1010
  });
974
1011
  }
@@ -2004,8 +2041,6 @@ var DeepdotsPopups = class {
2004
2041
  this.renderer = createDefaultRenderer();
2005
2042
  /** Logger para el output de debug. Default `console`; el host puede inyectar el suyo en init(). */
2006
2043
  this.logger = console;
2007
- this.popupContainer = null;
2008
- // deprecated: mantenido para compatibilidad interna
2009
2044
  this.popupDefinitions = [];
2010
2045
  this.popupsLoaded = false;
2011
2046
  /**
@@ -2036,6 +2071,7 @@ var DeepdotsPopups = class {
2036
2071
  this.showProgressBar = void 0;
2037
2072
  /** CSS del host inyectado al final de los estilos del popup. */
2038
2073
  this.surveyCss = void 0;
2074
+ this.cspNonce = void 0;
2039
2075
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
2040
2076
  this.tracking = null;
2041
2077
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -2108,6 +2144,7 @@ var DeepdotsPopups = class {
2108
2144
  this.renderChrome = config.renderChrome ?? true;
2109
2145
  this.showProgressBar = config.showProgressBar;
2110
2146
  this.surveyCss = config.surveyCss;
2147
+ this.cspNonce = config.cspNonce;
2111
2148
  this.onFeedbackSession = config.onFeedbackSession;
2112
2149
  this.config = {
2113
2150
  apiKey: config.apiKey || void 0,
@@ -2208,7 +2245,6 @@ var DeepdotsPopups = class {
2208
2245
  this.initialized = true;
2209
2246
  this.log("SDK initialized", this.config);
2210
2247
  if (this.renderer.init) this.renderer.init();
2211
- this.setupPopupContainer();
2212
2248
  this.awaitingPopups = Boolean(this.config.apiKey && this.baseUrl);
2213
2249
  this.fetchPopupsFromServer().then((defs) => {
2214
2250
  if (!defs) this.dropPendingTriggers();
@@ -3179,27 +3215,6 @@ var DeepdotsPopups = class {
3179
3215
  listeners.delete(listener);
3180
3216
  }
3181
3217
  }
3182
- /** Setup popup container element */
3183
- setupPopupContainer() {
3184
- if (typeof document === "undefined") {
3185
- return;
3186
- }
3187
- this.popupContainer = document.createElement("div");
3188
- this.popupContainer.id = "deepdots-popup-container";
3189
- this.popupContainer.style.cssText = `
3190
- position: fixed;
3191
- top: 0;
3192
- left: 0;
3193
- width: 100%;
3194
- height: 100%;
3195
- display: none;
3196
- z-index: 999999;
3197
- background: rgba(0, 0, 0, 0.5);
3198
- justify-content: center;
3199
- align-items: center;
3200
- `;
3201
- document.body.appendChild(this.popupContainer);
3202
- }
3203
3218
  /** Render the popup UI */
3204
3219
  renderPopup(surveyId, productId, actions, style, title) {
3205
3220
  const userId = this.tracking?.getUserId() ?? this.config?.userId;
@@ -3207,54 +3222,25 @@ var DeepdotsPopups = class {
3207
3222
  const miniService = this.analytics?.getMiniService() ?? void 0;
3208
3223
  const analyticsFeedbackSessionId = this.analyticsFeedbackSessionId;
3209
3224
  this.log("tracking \xB7 survey identity \u2192", { userId: userId ?? null, sessionId: sessionId ?? null, miniService: miniService ?? null, analyticsFeedbackSessionId: analyticsFeedbackSessionId ?? null });
3210
- if (this.renderer) {
3211
- this.renderer.show(
3212
- surveyId,
3213
- productId,
3214
- actions,
3215
- (type, id, payload) => this.emitEvent(type, id, payload),
3216
- () => this.hidePopup(),
3217
- this.env,
3218
- userId,
3219
- style,
3220
- sessionId,
3221
- miniService,
3222
- analyticsFeedbackSessionId,
3223
- this.renderChrome,
3224
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
3225
- );
3226
- return;
3227
- }
3228
- if (!this.popupContainer) return;
3229
- const container = this.popupContainer;
3230
- void import("./renderPopup-FAEZGWRS.mjs").then(({ renderPopup }) => {
3231
- renderPopup(
3232
- container,
3233
- surveyId,
3234
- productId,
3235
- actions,
3236
- (type, id, payload) => this.emitEvent(type, id, payload),
3237
- () => this.hidePopup(),
3238
- this.env,
3239
- userId,
3240
- style,
3241
- sessionId,
3242
- miniService,
3243
- analyticsFeedbackSessionId,
3244
- { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language }
3245
- );
3246
- });
3225
+ this.renderer.show(
3226
+ surveyId,
3227
+ productId,
3228
+ actions,
3229
+ (type, id, payload) => this.emitEvent(type, id, payload),
3230
+ () => this.hidePopup(),
3231
+ this.env,
3232
+ userId,
3233
+ style,
3234
+ sessionId,
3235
+ miniService,
3236
+ analyticsFeedbackSessionId,
3237
+ this.renderChrome,
3238
+ { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss, language: this.language, cspNonce: this.cspNonce }
3239
+ );
3247
3240
  }
3248
3241
  /** Hide the popup */
3249
3242
  hidePopup() {
3250
- if (this.renderer) {
3251
- this.renderer.hide();
3252
- return;
3253
- }
3254
- if (this.popupContainer) {
3255
- this.popupContainer.style.display = "none";
3256
- this.popupContainer.innerHTML = "";
3257
- }
3243
+ this.renderer.hide();
3258
3244
  }
3259
3245
  /** Emit an event */
3260
3246
  emitEvent(type, surveyId, data) {