@magicfeedback/popup-sdk 1.7.1 → 1.7.2

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.
@@ -446,6 +446,82 @@ var init_labels = __esm({
446
446
  }
447
447
  });
448
448
 
449
+ // src/ui/reveal.ts
450
+ function revealWhenPainted(root, reveal) {
451
+ const pending2 = Array.from(root.querySelectorAll("img")).filter((img) => !img.complete);
452
+ if (!pending2.length) {
453
+ reveal();
454
+ return;
455
+ }
456
+ let left = pending2.length;
457
+ const onSettled = () => {
458
+ left -= 1;
459
+ if (left <= 0) reveal();
460
+ };
461
+ pending2.forEach((img) => {
462
+ img.addEventListener("load", onSettled, { once: true });
463
+ img.addEventListener("error", onSettled, { once: true });
464
+ });
465
+ }
466
+ var REVEAL_TIMEOUT_MS;
467
+ var init_reveal = __esm({
468
+ "src/ui/reveal.ts"() {
469
+ "use strict";
470
+ REVEAL_TIMEOUT_MS = 1200;
471
+ }
472
+ });
473
+
474
+ // src/ui/surveyPalette.ts
475
+ function resolveSurveyPrimaryColor(style) {
476
+ for (const candidate of [style?.primaryColor, style?.buttonPrimaryColor]) {
477
+ const value = (candidate ?? "").trim();
478
+ if (HEX_COLOR.test(value)) return value;
479
+ }
480
+ return null;
481
+ }
482
+ function surveyPrimaryVars(color) {
483
+ return [
484
+ ["--mf-primary", color],
485
+ ["--mf-primary-hover", `color-mix(in srgb, ${color} 85%, black)`],
486
+ ["--mf-primary-light", `color-mix(in srgb, ${color} 15%, white)`],
487
+ ["--mf-primary-border", `color-mix(in srgb, ${color} 35%, transparent)`],
488
+ ["--mf-border-focus", color]
489
+ ];
490
+ }
491
+ function applySurveyPrimaryColor(el, style) {
492
+ const color = resolveSurveyPrimaryColor(style);
493
+ if (!color) return;
494
+ for (const [name, value] of surveyPrimaryVars(color)) {
495
+ el.style.setProperty(name, value);
496
+ }
497
+ }
498
+ var HEX_COLOR, PRIMARY_COLOR_JS;
499
+ var init_surveyPalette = __esm({
500
+ "src/ui/surveyPalette.ts"() {
501
+ "use strict";
502
+ HEX_COLOR = /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
503
+ PRIMARY_COLOR_JS = `
504
+ function ddResolveSurveyPrimaryColor(style){
505
+ var hex=/^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/;
506
+ var candidates=[style&&style.primaryColor, style&&style.buttonPrimaryColor];
507
+ for(var i=0;i<candidates.length;i++){
508
+ var value=(candidates[i]||'').trim();
509
+ if(hex.test(value)){ return value; }
510
+ }
511
+ return null;
512
+ }
513
+ function ddApplySurveyPrimaryColor(el, style){
514
+ var color=ddResolveSurveyPrimaryColor(style);
515
+ if(!color||!el){ return; }
516
+ el.style.setProperty('--mf-primary', color);
517
+ el.style.setProperty('--mf-primary-hover', 'color-mix(in srgb, '+color+' 85%, black)');
518
+ el.style.setProperty('--mf-primary-light', 'color-mix(in srgb, '+color+' 15%, white)');
519
+ el.style.setProperty('--mf-primary-border', 'color-mix(in srgb, '+color+' 35%, transparent)');
520
+ el.style.setProperty('--mf-border-focus', color);
521
+ }`;
522
+ }
523
+ });
524
+
449
525
  // src/assets/style.css
450
526
  var style_default;
451
527
  var init_style = __esm({
@@ -2338,6 +2414,8 @@ var init_logo = __esm({
2338
2414
  // src/ui/renderPopup.ts
2339
2415
  var renderPopup_exports = {};
2340
2416
  __export(renderPopup_exports, {
2417
+ REVEAL_TIMEOUT_MS: () => REVEAL_TIMEOUT_MS,
2418
+ SURVEY_ALIGNMENT_STYLE_ID: () => SURVEY_ALIGNMENT_STYLE_ID,
2341
2419
  renderPopup: () => renderPopup
2342
2420
  });
2343
2421
  function ensureMagicFeedbackStyles(_popup) {
@@ -2348,6 +2426,17 @@ function ensureMagicFeedbackStyles(_popup) {
2348
2426
  style.textContent = style_default;
2349
2427
  document.head.appendChild(style);
2350
2428
  }
2429
+ function ensureSurveyAlignmentStyles() {
2430
+ if (document.getElementById(SURVEY_ALIGNMENT_STYLE_ID)) return;
2431
+ const style = document.createElement("style");
2432
+ style.id = SURVEY_ALIGNMENT_STYLE_ID;
2433
+ style.textContent = `
2434
+ .deepdots-popup .magicfeedback-container,
2435
+ .deepdots-popup .magicfeedback-form,
2436
+ .deepdots-popup .magicfeedback-div { padding-left: 0; padding-right: 0; }
2437
+ `;
2438
+ document.head.appendChild(style);
2439
+ }
2351
2440
  function ensureSpinnerStyles(_popup) {
2352
2441
  if (document.getElementById("deepdots-spinner-styles")) return;
2353
2442
  const style = document.createElement("style");
@@ -2600,6 +2689,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2600
2689
  progressFollowUp.style.display = p.followup ? "inline-block" : "none";
2601
2690
  }
2602
2691
  ensureMagicFeedbackStyles(popup);
2692
+ ensureSurveyAlignmentStyles();
2603
2693
  ensureSpinnerStyles(popup);
2604
2694
  ensureResponsiveStyles(popup);
2605
2695
  ensureCustomCss(options?.surveyCss);
@@ -2658,7 +2748,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2658
2748
  border: none;
2659
2749
  min-height: 44px;
2660
2750
  padding: 12px 24px;
2661
- border-radius: 6px;
2751
+ border-radius: 999px; /* p\xEDldora, como el Button de Material3 en KMP */
2662
2752
  cursor: pointer;
2663
2753
  font-size: 15px;
2664
2754
  font-weight: 600;
@@ -2688,7 +2778,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2688
2778
  border: none;
2689
2779
  min-height: 44px;
2690
2780
  padding: 12px 24px;
2691
- border-radius: 6px;
2781
+ border-radius: 999px; /* p\xEDldora, como el Button de Material3 en KMP */
2692
2782
  cursor: pointer;
2693
2783
  font-size: 15px;
2694
2784
  font-weight: 600;
@@ -2715,7 +2805,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2715
2805
  border: none;
2716
2806
  min-height: 44px;
2717
2807
  padding: 12px 24px;
2718
- border-radius: 6px;
2808
+ border-radius: 999px; /* p\xEDldora, como el Button de Material3 en KMP */
2719
2809
  cursor: pointer;
2720
2810
  font-size: 15px;
2721
2811
  font-weight: 600;
@@ -2746,7 +2836,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2746
2836
  border: none;
2747
2837
  min-height: 44px;
2748
2838
  padding: 12px 24px;
2749
- border-radius: 6px;
2839
+ border-radius: 999px; /* p\xEDldora, como el Button de Material3 en KMP */
2750
2840
  cursor: pointer;
2751
2841
  font-size: 15px;
2752
2842
  font-weight: 600;
@@ -2848,14 +2938,35 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2848
2938
  container.innerHTML = "";
2849
2939
  container.appendChild(popup);
2850
2940
  container.style.display = "flex";
2941
+ container.style.visibility = "hidden";
2942
+ container.style.pointerEvents = "none";
2851
2943
  container.style.justifyContent = pos.justifyContent;
2852
2944
  container.style.alignItems = pos.alignItems;
2853
2945
  container.style.background = pos.background;
2854
2946
  if (pos.padding) container.style.padding = pos.padding;
2947
+ let revealed = false;
2948
+ let revealTimer = null;
2949
+ function reveal() {
2950
+ if (revealed) return;
2951
+ revealed = true;
2952
+ if (revealTimer !== null) {
2953
+ clearTimeout(revealTimer);
2954
+ revealTimer = null;
2955
+ }
2956
+ if (!container.contains(popup)) return;
2957
+ container.style.visibility = "visible";
2958
+ container.style.pointerEvents = "";
2959
+ }
2960
+ function revealWhenPainted2() {
2961
+ if (revealed) return;
2962
+ revealWhenPainted(popup, reveal);
2963
+ }
2964
+ revealTimer = setTimeout(reveal, REVEAL_TIMEOUT_MS);
2855
2965
  function setLoading(isLoading) {
2856
2966
  spinnerEl.style.display = isLoading ? "flex" : "none";
2857
2967
  if (!isLoading) {
2858
2968
  formHost.style.visibility = "visible";
2969
+ revealWhenPainted2();
2859
2970
  }
2860
2971
  footer.style.display = isLoading ? "none" : "flex";
2861
2972
  backButton.disabled = isLoading;
@@ -2918,6 +3029,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2918
3029
  if (s.contentAlign) {
2919
3030
  main.style.justifyContent = s.contentAlign === "center" ? "center" : "flex-start";
2920
3031
  }
3032
+ applySurveyPrimaryColor(popup, s);
2921
3033
  if (s.buttonPrimaryColor) {
2922
3034
  submitButton.style.background = s.buttonPrimaryColor;
2923
3035
  submitButton.style.border = "none";
@@ -2999,17 +3111,21 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
2999
3111
  setLoading(false);
3000
3112
  }
3001
3113
  }
3002
- var import_native;
3114
+ var import_native, SURVEY_ALIGNMENT_STYLE_ID;
3003
3115
  var init_renderPopup = __esm({
3004
3116
  "src/ui/renderPopup.ts"() {
3005
3117
  "use strict";
3006
3118
  init_tracking_manager();
3007
3119
  init_font();
3008
3120
  init_logo();
3121
+ init_surveyPalette();
3009
3122
  init_logger();
3010
3123
  init_labels();
3124
+ init_reveal();
3011
3125
  import_native = __toESM(require("@magicfeedback/native"));
3012
3126
  init_style();
3127
+ init_reveal();
3128
+ SURVEY_ALIGNMENT_STYLE_ID = "deepdots-survey-alignment";
3013
3129
  }
3014
3130
  });
3015
3131
 
@@ -3220,7 +3336,33 @@ init_tracking_manager();
3220
3336
  // src/ui/surveyHtml.ts
3221
3337
  init_font();
3222
3338
  init_labels();
3339
+ init_reveal();
3340
+ init_surveyPalette();
3223
3341
  init_style();
3342
+ var REVEAL_JS = `
3343
+ function ddCreateReveal(popup, onReveal, timeoutMs){
3344
+ var revealed=false, timer=null;
3345
+ function reveal(){
3346
+ if(revealed){ return; }
3347
+ revealed=true;
3348
+ if(timer){ clearTimeout(timer); timer=null; }
3349
+ onReveal();
3350
+ }
3351
+ function revealWhenPainted(){
3352
+ if(revealed){ return; }
3353
+ var imgs=popup.querySelectorAll('img'), pending=[];
3354
+ for(var i=0;i<imgs.length;i++){ if(!imgs[i].complete){ pending.push(imgs[i]); } }
3355
+ if(!pending.length){ reveal(); return; }
3356
+ var left=pending.length;
3357
+ function onSettled(){ left--; if(left<=0){ reveal(); } }
3358
+ for(var j=0;j<pending.length;j++){
3359
+ pending[j].addEventListener('load', onSettled);
3360
+ pending[j].addEventListener('error', onSettled);
3361
+ }
3362
+ }
3363
+ timer=setTimeout(reveal, timeoutMs);
3364
+ return { reveal: reveal, whenPainted: revealWhenPainted };
3365
+ }`;
3224
3366
  var POSITION_MAP = {
3225
3367
  center: { justifyContent: "center", alignItems: "center", padding: "0", background: "rgba(0,0,0,0.5)" },
3226
3368
  bottom: { justifyContent: "center", alignItems: "flex-end", padding: "16px", background: "transparent" },
@@ -3294,7 +3436,13 @@ function buildSurveyHtml(opts) {
3294
3436
  ${style_default}
3295
3437
  ${fontFaceCss}
3296
3438
  html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
3297
- body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:${bodyBg};padding:${bodyPadding};box-sizing:border-box}
3439
+ /* Apertura diferida: ni la tarjeta ni el velo se pintan hasta que el survey est\xE1 listo
3440
+ (\`dd-ready\`). Si el fondo entrara antes, en RN se ver\xEDa el velo oscuro varios cientos de ms
3441
+ antes que el contenido, que es la misma mala sensaci\xF3n que el spinner. */
3442
+ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${pos.alignItems};background:transparent;padding:${bodyPadding};box-sizing:border-box}
3443
+ body.dd-ready{background:${bodyBg}}
3444
+ #dd-popup{visibility:hidden}
3445
+ body.dd-ready #dd-popup{visibility:visible}
3298
3446
  .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};height:${cardHeight};max-height:${cardMaxHeight};box-sizing:border-box}
3299
3447
  .deepdots-popup-header{display:flex;justify-content:space-between;align-items:center;gap:12px;width:100%;flex:0 0 auto}
3300
3448
  /* Neutraliza la regla \`.deepdots-popup h2\` del CSS del survey (uppercase, centrado y
@@ -3328,6 +3476,13 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
3328
3476
  #dd-form-wrapper{width:100%;flex:1 1 auto}
3329
3477
  #mf{width:100%;box-sizing:border-box;visibility:hidden}
3330
3478
  .deepdots-popup-main *{max-width:100%;box-sizing:border-box}
3479
+ /* El margen lateral lo pone la tarjeta: el CSS del survey lo a\xF1ade otra vez en el contenedor, en
3480
+ el formulario y en el bloque de cada pregunta, y eso deja el enunciado y las opciones m\xE1s
3481
+ adentro que el logo, el t\xEDtulo y la barra de progreso. Solo el lateral; el vertical separa las
3482
+ preguntas. Mismas reglas que en el popup DOM (renderPopup.ts). */
3483
+ .deepdots-popup .magicfeedback-container,
3484
+ .deepdots-popup .magicfeedback-form,
3485
+ .deepdots-popup .magicfeedback-div{padding-left:0;padding-right:0}
3331
3486
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain;display:block;margin:12px 0 0 0}
3332
3487
  .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}
3333
3488
  /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
@@ -3335,7 +3490,7 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
3335
3490
  .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
3336
3491
  .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
3337
3492
  .deepdots-popup-footer{display:flex;flex-direction:row-reverse;justify-content:space-between;align-items:center;gap:4px;margin-top:auto;width:100%;padding-top:12px}
3338
- .dd-nav-btn{display:none;border:none;min-height:44px;padding:12px 24px;border-radius:6px;cursor:pointer;font-size:15px;font-weight:600;align-items:center;justify-content:center;text-align:center}
3493
+ .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}
3339
3494
  /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
3340
3495
  #dd-back{background:transparent;color:${textMuted};border:none}
3341
3496
  #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
@@ -3560,9 +3715,23 @@ ${customCss}
3560
3715
  }
3561
3716
  }
3562
3717
 
3718
+ ${PRIMARY_COLOR_JS}
3719
+ ${REVEAL_JS}
3720
+ // El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
3721
+ // Modal en ese momento en vez de montar un WebView en blanco.
3722
+ var ddReveal=ddCreateReveal(popup, function(){
3723
+ document.body.classList.add('dd-ready');
3724
+ emitJSON('ready');
3725
+ }, ${REVEAL_TIMEOUT_MS});
3726
+
3563
3727
  function setLoading(isLoading){
3564
3728
  spinner.style.display=isLoading?'flex':'none';
3565
- if(!isLoading){ formHost.style.visibility='visible'; }
3729
+ if(!isLoading){
3730
+ formHost.style.visibility='visible';
3731
+ // Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
3732
+ // revelaci\xF3n solo act\xFAa la primera vez.
3733
+ ddReveal.whenPainted();
3734
+ }
3566
3735
  footer.style.display=isLoading?'none':'flex';
3567
3736
  backBtn.disabled=isLoading; startBtn.disabled=isLoading; completeBtn.disabled=isLoading; submitBtn.disabled=isLoading;
3568
3737
  if(isLoading){ updateButtons(null); }
@@ -3605,6 +3774,10 @@ ${customCss}
3605
3774
  if(s.loadingBarColor){ progressBar.style.background=s.loadingBarColor; progressFollowUp.style.background=s.loadingBarColor; }
3606
3775
  if(chromeOn && s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
3607
3776
  if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
3777
+ // El survey pinta sus controles con --mf-primary, que sale de primaryColor; si la
3778
+ // integraci\xF3n solo configura el del bot\xF3n, el popup mezclar\xEDa el color de la marca
3779
+ // con el gris azulado por defecto del paquete.
3780
+ ddApplySurveyPrimaryColor(popup, s);
3608
3781
  if(s.buttonPrimaryColor){
3609
3782
  submitBtn.style.background=s.buttonPrimaryColor; submitBtn.style.border='none'; submitBtn.style.color='#fff';
3610
3783
  startBtn.style.background=s.buttonPrimaryColor; startBtn.style.border='none'; startBtn.style.color='#fff';
@@ -3696,7 +3869,9 @@ ${customCss}
3696
3869
  }
3697
3870
 
3698
3871
  // src/platform/react-native-renderer.ts
3872
+ init_reveal();
3699
3873
  init_logger();
3874
+ var NATIVE_READY_GRACE_MS = 500;
3700
3875
  var ReactNativePopupRenderer = class {
3701
3876
  constructor(options = {}) {
3702
3877
  this.options = options;
@@ -3704,6 +3879,8 @@ var ReactNativePopupRenderer = class {
3704
3879
  this.onCloseFn = null;
3705
3880
  this.currentSurveyId = null;
3706
3881
  this.partialEmitted = false;
3882
+ this.readyEmitted = false;
3883
+ this.readyTimer = null;
3707
3884
  }
3708
3885
  init() {
3709
3886
  }
@@ -3712,6 +3889,8 @@ var ReactNativePopupRenderer = class {
3712
3889
  this.onCloseFn = onClose;
3713
3890
  this.currentSurveyId = surveyId;
3714
3891
  this.partialEmitted = false;
3892
+ this.readyEmitted = false;
3893
+ this.armReadyFallback(surveyId);
3715
3894
  const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);
3716
3895
  const html = buildSurveyHtml({
3717
3896
  surveyId,
@@ -3739,9 +3918,32 @@ var ReactNativePopupRenderer = class {
3739
3918
  }
3740
3919
  }
3741
3920
  hide() {
3921
+ this.clearReadyFallback();
3742
3922
  this.options.onHide?.();
3743
3923
  this.currentSurveyId = null;
3744
3924
  }
3925
+ /** Respaldo del aviso de listo por si el WebView no llega a ejecutar el HTML. */
3926
+ armReadyFallback(surveyId) {
3927
+ this.clearReadyFallback();
3928
+ if (!this.options.onReady) return;
3929
+ this.readyTimer = setTimeout(() => {
3930
+ this.readyTimer = null;
3931
+ this.emitReady(surveyId);
3932
+ }, REVEAL_TIMEOUT_MS + NATIVE_READY_GRACE_MS);
3933
+ }
3934
+ clearReadyFallback() {
3935
+ if (this.readyTimer !== null) {
3936
+ clearTimeout(this.readyTimer);
3937
+ this.readyTimer = null;
3938
+ }
3939
+ }
3940
+ /** Idempotente: el WebView puede avisar a la vez que vence el respaldo. */
3941
+ emitReady(surveyId) {
3942
+ if (this.readyEmitted) return;
3943
+ this.readyEmitted = true;
3944
+ this.clearReadyFallback();
3945
+ this.options.onReady?.({ surveyId });
3946
+ }
3745
3947
  /**
3746
3948
  * El host la conecta al `onMessage` del WebView. Traduce los mensajes del survey a
3747
3949
  * eventos del SDK: primera interacción → `popup_clicked` (PARTIAL), completado →
@@ -3757,6 +3959,9 @@ var ReactNativePopupRenderer = class {
3757
3959
  name = raw;
3758
3960
  }
3759
3961
  switch (name) {
3962
+ case "ready":
3963
+ this.emitReady(surveyId);
3964
+ break;
3760
3965
  case "loaded":
3761
3966
  case "before_submit":
3762
3967
  case "after_submit":
@@ -3784,6 +3989,33 @@ function createReactNativeRenderer() {
3784
3989
  return new ReactNativePopupRenderer();
3785
3990
  }
3786
3991
 
3992
+ // src/ui/preload.ts
3993
+ var defaultLoader = () => Promise.resolve().then(() => (init_renderPopup(), renderPopup_exports));
3994
+ var pending = null;
3995
+ function preloadRenderPopup(loader = defaultLoader) {
3996
+ if (pending) return pending;
3997
+ try {
3998
+ pending = loader().then(() => void 0).catch(() => {
3999
+ pending = null;
4000
+ });
4001
+ } catch {
4002
+ pending = null;
4003
+ return Promise.resolve();
4004
+ }
4005
+ return pending;
4006
+ }
4007
+ function schedulePreloadRenderPopup(loader) {
4008
+ const run = () => {
4009
+ void preloadRenderPopup(loader);
4010
+ };
4011
+ const idle = globalThis.requestIdleCallback;
4012
+ if (typeof idle === "function") {
4013
+ idle(run, { timeout: 2e3 });
4014
+ return;
4015
+ }
4016
+ setTimeout(run, 0);
4017
+ }
4018
+
3787
4019
  // src/platform/renderer.ts
3788
4020
  var NoopPopupRenderer = class {
3789
4021
  show() {
@@ -3818,6 +4050,13 @@ var BrowserPopupRenderer = class {
3818
4050
  document.body.appendChild(this.container);
3819
4051
  }
3820
4052
  }
4053
+ /**
4054
+ * Descarga el chunk de `renderPopup` antes de que ningún trigger dispare, para que abrir el
4055
+ * popup no empiece por esperar ~238 KB de JS.
4056
+ */
4057
+ preload() {
4058
+ schedulePreloadRenderPopup();
4059
+ }
3821
4060
  show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome, options) {
3822
4061
  if (this.visible) return;
3823
4062
  if (!this.container || !document.body.contains(this.container)) this.init();
@@ -3832,6 +4071,8 @@ var BrowserPopupRenderer = class {
3832
4071
  this.visible = false;
3833
4072
  if (this.container) {
3834
4073
  this.container.style.display = "none";
4074
+ this.container.style.visibility = "";
4075
+ this.container.style.pointerEvents = "";
3835
4076
  this.container.innerHTML = "";
3836
4077
  }
3837
4078
  }
@@ -4908,6 +5149,7 @@ var DeepdotsPopups = class {
4908
5149
  this.popupDefinitions = this.validatePopupDefinitions(defs);
4909
5150
  this.popupsLoaded = true;
4910
5151
  this.log("Popups loaded from API", this.popupDefinitions);
5152
+ if (this.popupDefinitions.length) this.renderer.preload?.();
4911
5153
  this.configureTriggersFromDefinitions();
4912
5154
  this.processDeferredExitQueue();
4913
5155
  if (this.pendingAutoLaunch) {
@@ -5395,7 +5637,7 @@ var DeepdotsPopups = class {
5395
5637
  processDeferredExitQueue() {
5396
5638
  const queue = this.getDeferredExitQueue();
5397
5639
  if (!queue.length) return;
5398
- const pending = [];
5640
+ const pending2 = [];
5399
5641
  const now = Date.now();
5400
5642
  queue.forEach((item) => {
5401
5643
  const def = this.popupDefinitions.find((popup) => popup.id === item.id && popup.surveyId === item.surveyId);
@@ -5406,10 +5648,10 @@ var DeepdotsPopups = class {
5406
5648
  this.tryShowDeferredExitPopup(item);
5407
5649
  return;
5408
5650
  }
5409
- pending.push(item);
5651
+ pending2.push(item);
5410
5652
  this.scheduleDeferredExitPopup(item);
5411
5653
  });
5412
- this.setDeferredExitQueue(pending);
5654
+ this.setDeferredExitQueue(pending2);
5413
5655
  }
5414
5656
  scheduleDeferredExitPopup(item) {
5415
5657
  if (typeof window === "undefined") return;
@@ -5482,7 +5724,7 @@ var DeepdotsPopups = class {
5482
5724
  async fetchPopupsFromServer() {
5483
5725
  const apiKey = this.config?.apiKey;
5484
5726
  const baseUrl = this.baseUrl;
5485
- const userId = this.config?.userId;
5727
+ const userId = this.tracking?.getUserId() ?? this.config?.userId;
5486
5728
  if (!apiKey || !baseUrl) {
5487
5729
  this.log("Missing apiKey or baseUrl. Skipping popups fetch.");
5488
5730
  return [];
@@ -5907,16 +6149,24 @@ function DeepdotsProvider({ config, children }) {
5907
6149
  }, []);
5908
6150
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(DeepdotsContext.Provider, { value: sdk, children: [
5909
6151
  children,
5910
- survey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Modal, { visible: true, transparent: true, animationType: "slide", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5911
- import_react_native_webview.WebView,
5912
- {
5913
- style: { flex: 1 },
5914
- originWhitelist: ["*"],
5915
- javaScriptEnabled: true,
5916
- source: { html: survey.html, baseUrl: "https://sdk.deepdots.com/" },
5917
- onMessage: (e) => rendererRef.current?.handleMessage(e.nativeEvent.data)
5918
- }
5919
- ) }) }) : null
6152
+ survey ? (
6153
+ // El Modal se abre en cuanto hay popup, y es el HTML del WebView el que se revela solo
6154
+ // cuando el survey está pintado (apertura diferida): con el Modal transparente, hasta ese
6155
+ // momento no se ve nada, ni velo ni spinner. No se difiere el Modal con `onReady` porque
6156
+ // React Native no monta los hijos de un Modal cerrado: el WebView no llegaría a cargar y
6157
+ // el aviso no llegaría nunca. El host que quiera diferir su contenedor debe montar el
6158
+ // WebView invisible (ver el ejemplo de `ReactNativePopupRenderer`).
6159
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.Modal, { visible: true, transparent: true, animationType: "slide", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_native.View, { style: { flex: 1 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
6160
+ import_react_native_webview.WebView,
6161
+ {
6162
+ style: { flex: 1 },
6163
+ originWhitelist: ["*"],
6164
+ javaScriptEnabled: true,
6165
+ source: { html: survey.html, baseUrl: "https://sdk.deepdots.com/" },
6166
+ onMessage: (e) => rendererRef.current?.handleMessage(e.nativeEvent.data)
6167
+ }
6168
+ ) }) })
6169
+ ) : null
5920
6170
  ] });
5921
6171
  }
5922
6172
  //# sourceMappingURL=react-native.js.map