@magicfeedback/popup-sdk 1.4.0 → 1.5.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.
@@ -9,7 +9,7 @@ import {
9
9
  sdkWarn,
10
10
  setLogger,
11
11
  style_default
12
- } from "./chunk-R4B3K2ZW.mjs";
12
+ } from "./chunk-D7QAGWGC.mjs";
13
13
 
14
14
  // src/react-native/index.tsx
15
15
  import { createContext, useContext, useEffect, useRef, useState } from "react";
@@ -214,14 +214,17 @@ var POSITION_MAP = {
214
214
  "top-right": { justifyContent: "flex-end", alignItems: "flex-start", padding: "16px", background: "transparent" },
215
215
  "top-left": { justifyContent: "flex-start", alignItems: "flex-start", padding: "16px", background: "transparent" }
216
216
  };
217
+ function jsonForScript(value) {
218
+ return JSON.stringify(value ?? null).replace(/</g, "\\u003C").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
219
+ }
217
220
  function buildSurveyHtml(opts) {
218
221
  const env = opts.env === "development" ? "dev" : "prod";
219
222
  const version = opts.version ?? "2.2.4";
220
223
  const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;
221
- const sid = JSON.stringify(opts.surveyId);
222
- const pid = JSON.stringify(opts.productId);
223
- const profileJson = JSON.stringify(opts.profile ?? []);
224
- const metaJson = JSON.stringify(opts.metadata ?? []);
224
+ const sid = jsonForScript(opts.surveyId);
225
+ const pid = jsonForScript(opts.productId);
226
+ const profileJson = jsonForScript(opts.profile ?? []);
227
+ const metaJson = jsonForScript(opts.metadata ?? []);
225
228
  const fontFaceCss = opts.font ? buildFontFaceCss(opts.font.family, opts.font.url) : "";
226
229
  const fontFamilyValue = opts.font ? buildFontFamilyValue(opts.font.family) : "-apple-system,system-ui,sans-serif";
227
230
  const popupClass = opts.font?.family ? "deepdots-popup deepdots-has-font" : "deepdots-popup";
@@ -229,6 +232,8 @@ function buildSurveyHtml(opts) {
229
232
  const popupBg = isDark ? "#1e1e1e" : "#fff";
230
233
  const colorScheme = isDark ? "dark" : "light";
231
234
  const textPrimary = isDark ? "#f0f0f0" : "#111";
235
+ const textMuted = isDark ? "#9ca3af" : "#6b7280";
236
+ const trackBg = isDark ? "#3f3f46" : "#e5e7eb";
232
237
  const pos = POSITION_MAP[opts.position ?? "center"] ?? POSITION_MAP.center;
233
238
  const chrome = opts.chrome !== false;
234
239
  const bodyBg = chrome ? pos.background : "transparent";
@@ -240,10 +245,16 @@ function buildSurveyHtml(opts) {
240
245
  const cardMaxWidth = chrome ? "600px" : "none";
241
246
  const cardWidth = chrome ? "90%" : "100%";
242
247
  const cardMinHeight = chrome ? "200px" : "0";
243
- const backLabel = JSON.stringify(opts.actions?.back?.label ?? "Back");
244
- const startLabel = JSON.stringify(opts.actions?.start?.label ?? "Start survey");
245
- const completeLabel = JSON.stringify(opts.actions?.complete?.label ?? "Complete survey");
246
- const submitLabel = JSON.stringify(opts.actions?.accept?.label ?? "Send");
248
+ const cardHeight = chrome ? "auto" : "100%";
249
+ const cardMaxHeight = chrome ? "90vh" : "100%";
250
+ const mobileCard = chrome ? `.deepdots-popup{width:calc(100% - 24px);max-width:calc(100% - 24px);border-radius:12px;padding:calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom)) 16px}` : `.deepdots-popup{width:100%;max-width:100%;border-radius:0;padding:16px}`;
251
+ const titleJson = jsonForScript(opts.title ?? "");
252
+ const customCss = (opts.surveyCss ?? "").replace(/<\/\s*style/gi, "<\\/style");
253
+ const progressPref = opts.showProgressBar === void 0 ? "null" : String(opts.showProgressBar);
254
+ const backLabel = jsonForScript(opts.actions?.back?.label ?? "Back");
255
+ const startLabel = jsonForScript(opts.actions?.start?.label ?? "Start survey");
256
+ const completeLabel = jsonForScript(opts.actions?.complete?.label ?? "Complete survey");
257
+ const submitLabel = jsonForScript(opts.actions?.accept?.label ?? "Send");
247
258
  return `<!DOCTYPE html><html><head>
248
259
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
249
260
  <meta name="color-scheme" content="${colorScheme}"/>
@@ -252,56 +263,97 @@ ${style_default}
252
263
  ${fontFaceCss}
253
264
  html,body{margin:0;padding:0;height:100%;font-family:${fontFamilyValue}}
254
265
  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}
256
- .deepdots-popup-header{display:flex;justify-content:flex-end;align-items:center;width:100%}
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}
258
- .deepdots-popup-container-content{display:flex;flex-direction:column;padding:0 20px 12px 20px;max-height:80vh;overflow:hidden;box-sizing:border-box}
259
- .deepdots-popup-main{display:flex;flex-direction:column;width:100%;max-height:80vh;overflow-y:auto;overflow-x:hidden;position:relative}
266
+ .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}
267
+ .deepdots-popup-header{display:flex;justify-content:space-between;align-items:center;gap:12px;width:100%;flex:0 0 auto}
268
+ /* Neutraliza la regla \`.deepdots-popup h2\` del CSS del survey (uppercase, centrado y
269
+ margin-bottom 40px), pensada para los enunciados y no para el t\xEDtulo de la cabecera. */
270
+ #dd-title{margin:0;font-family:var(--deepdots-font,'Montserrat',inherit);font-size:17px;font-weight:600;line-height:1.3;color:${textPrimary};text-transform:none;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
271
+ #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;flex:0 0 auto;margin-left:auto}
272
+ /* Progreso alineado con la cabecera: sin padding horizontal propio, lo marca la tarjeta.
273
+ El vertical es el mismo arriba y abajo, y del mismo valor que el padding de la tarjeta. */
274
+ .deepdots-progress{display:none;flex-direction:column;gap:8px;width:100%;flex:0 0 auto;padding:16px 0;box-sizing:border-box}
275
+ .deepdots-progress-head{display:flex;flex-direction:row;justify-content:space-between;align-items:center;width:100%;gap:8px}
276
+ #dd-progress-label{font-size:13px;line-height:1.2}
277
+ /* "Question 1" en negrita y "of 3" en regular gris, como el mockup. */
278
+ #dd-progress-current{font-weight:700;color:${textPrimary}}
279
+ #dd-progress-total{font-weight:400;color:${textMuted}}
280
+ #dd-progress-followup{display:none;font-size:12px;font-weight:600;color:#fff;background:rgba(59,130,246,0.44);border-radius:999px;padding:2px 10px}
281
+ .deepdots-progress-track{width:100%;height:4px;border-radius:999px;background:${trackBg};overflow:hidden}
282
+ #dd-progress-bar{height:100%;width:0%;background:#22C55E;border-radius:999px;transition:width 450ms ease}
283
+ /* El popup es una columna: solo el bloque de preguntas hace scroll, para que el footer
284
+ con las acciones quede siempre visible en el borde inferior (antes viv\xEDa dentro del
285
+ \xE1rea scrollable y hab\xEDa que bajar hasta el final para ver "Send"). */
286
+ /* Sin sangrado extra: el contenido se alinea con el t\xEDtulo y con la barra de progreso (antes
287
+ sumaba 20px a los lados sobre el padding de la tarjeta y quedaba m\xE1s adentro que la cabecera). */
288
+ /* Sin barra de progreso, este padding-top es el que separa la cabecera de la pregunta. Con
289
+ barra, el JS lo pone a 0 y el hueco lo aporta el padding inferior del bloque de progreso,
290
+ de modo que el espacio por arriba y por abajo de la barra es id\xE9ntico. */
291
+ .deepdots-popup-container-content{display:flex;flex-direction:column;flex:1 1 auto;min-height:0;padding:16px 0 0 0;overflow:hidden;box-sizing:border-box}
292
+ /* El enunciado trae margin-top:20px del CSS de surveys, que sumaba al hueco de la barra y
293
+ descompensaba la simetr\xEDa. Solo se anula en el primero: entre preguntas sigue separando. */
294
+ .deepdots-popup .magicfeedback-questions .magicfeedback-div:first-child .magicfeedback-label{margin-top:0}
295
+ .deepdots-popup-main{display:flex;flex-direction:column;width:100%;flex:1 1 auto;min-height:0;overflow-y:auto;overflow-x:hidden;position:relative}
260
296
  #dd-form-wrapper{width:100%;flex:1 1 auto}
261
297
  #mf{width:100%;box-sizing:border-box;visibility:hidden}
262
298
  .deepdots-popup-main *{max-width:100%;box-sizing:border-box}
263
299
  #dd-logo{max-height:40px;max-width:100%;object-fit:contain}
264
300
  .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}
265
- .deepdots-popup-footer{display:flex;flex-direction:row-reverse;justify-content:space-between;align-items:center;gap:8px;margin-top:auto;width:100%;padding-top:16px}
266
- .dd-nav-btn{display:none;border:none;padding:12px 24px;border-radius:4px;cursor:pointer;font-size:14px;box-shadow:0 2px 4px rgba(0,0,0,0.1);align-items:center;justify-content:center;text-align:center}
267
- #dd-back{background:transparent;color:#333;border:1px solid #999}
301
+ /* Pantalla final: el HTML del editor de la plataforma (imagen + texto), centrado. */
302
+ .deepdots-success{display:none;width:100%;text-align:center;padding:24px 0;color:${textPrimary}}
303
+ .deepdots-success img{max-width:100%;height:auto;margin:0 auto 16px auto;display:block}
304
+ .deepdots-success p{margin:0;font-size:16px;font-weight:600;line-height:1.4}
305
+ .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}
306
+ .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}
307
+ /* Secundario como bot\xF3n de texto: sin borde ni fondo, para que el primario sea la \xFAnica CTA. */
308
+ #dd-back{background:transparent;color:${textMuted};border:none}
268
309
  #dd-start,#dd-complete,#dd-submit{background:#1E293B;color:#fff;border:none}
269
310
  #dd-submit:disabled,#dd-start:disabled,#dd-back:disabled,#dd-complete:disabled{opacity:0.6;cursor:not-allowed}
270
311
  .mf-spinner{display:none;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)}
271
312
  .mf-spinner-circle{width:28px;height:28px;border:3px solid #e0e6ed;border-top-color:#1E293B;border-radius:50%;animation:ddspin 0.9s linear infinite}
272
313
  @keyframes ddspin{0%{transform:rotate(0deg)}100%{transform:rotate(360deg)}}
273
314
  @media (max-width:640px){
274
- .deepdots-popup{width:calc(100% - 24px);max-width:calc(100% - 24px);border-radius:12px}
275
- .deepdots-popup-footer{flex-direction:column-reverse;gap:12px}
315
+ ${mobileCard}
316
+ /* Apilados: la acci\xF3n principal (Send/Start/Complete) arriba y Back debajo. El orden del
317
+ DOM ya es ese, as\xED que \`column\` (no \`column-reverse\`) es lo que lo respeta. */
318
+ .deepdots-popup-footer{flex-direction:column;gap:4px}
276
319
  .dd-nav-btn{width:100%}
277
320
  .magicfeedback-checkbox-container,.magicfeedback-radio-container{margin:4px 0;padding:4px 8px}
278
321
  .magicfeedback-checkbox label,.magicfeedback-radio-container label{font-size:15px}
279
322
  .deepdots-popup input[type="radio"]{margin:6px 0 6px 8px}
280
323
  .deepdots-popup input[type="checkbox"]{width:16px;height:16px}
281
324
  }
325
+ ${customCss}
282
326
  </style>
283
327
  </head><body>
284
328
  <div class="${popupClass}" id="dd-popup">
285
329
  <div class="deepdots-popup-header">
330
+ <h2 id="dd-title" class="deepdots-popup-title" hidden></h2>
286
331
  <button type="button" id="dd-close" aria-label="Close popup">
287
332
  <svg width="28" height="28" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
288
333
  <path d="M6 6L18 18M6 18L18 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
289
334
  </svg>
290
335
  </button>
291
336
  </div>
292
- <div class="deepdots-popup-container-content">
337
+ <div class="deepdots-progress" id="dd-progress">
338
+ <div class="deepdots-progress-head">
339
+ <span id="dd-progress-label"><span id="dd-progress-current"></span><span id="dd-progress-total"></span></span>
340
+ <span id="dd-progress-followup">Follow-up</span>
341
+ </div>
342
+ <div class="deepdots-progress-track"><div id="dd-progress-bar"></div></div>
343
+ </div>
344
+ <div class="deepdots-popup-container-content" id="dd-content">
293
345
  <div class="deepdots-popup-main" id="dd-main">
294
346
  <div id="dd-form-wrapper">
295
347
  <div class="mf-spinner" id="dd-spinner" role="status" aria-label="Loading survey"><div class="mf-spinner-circle"></div></div>
296
348
  <div id="mf"></div>
297
349
  </div>
298
350
  <div class="deepdots-error-hint" id="dd-error" role="alert" aria-live="polite"></div>
299
- <div class="deepdots-popup-footer" id="dd-footer">
300
- <button type="button" class="dd-nav-btn" id="dd-submit">Send</button>
301
- <button type="button" class="dd-nav-btn" id="dd-back">Back</button>
302
- <button type="button" class="dd-nav-btn" id="dd-complete">Complete survey</button>
303
- <button type="button" class="dd-nav-btn" id="dd-start">Start survey</button>
304
- </div>
351
+ </div>
352
+ <div class="deepdots-popup-footer" id="dd-footer">
353
+ <button type="button" class="dd-nav-btn" id="dd-submit">Send</button>
354
+ <button type="button" class="dd-nav-btn" id="dd-back">Back</button>
355
+ <button type="button" class="dd-nav-btn" id="dd-complete">Complete survey</button>
356
+ <button type="button" class="dd-nav-btn" id="dd-start">Start survey</button>
305
357
  </div>
306
358
  </div>
307
359
  </div>
@@ -314,6 +366,7 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
314
366
 
315
367
  var popup=document.getElementById('dd-popup');
316
368
  var main=document.getElementById('dd-main');
369
+ var content=document.getElementById('dd-content');
317
370
  var footer=document.getElementById('dd-footer');
318
371
  var spinner=document.getElementById('dd-spinner');
319
372
  var formHost=document.getElementById('mf');
@@ -322,6 +375,58 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
322
375
  var startBtn=document.getElementById('dd-start');
323
376
  var completeBtn=document.getElementById('dd-complete');
324
377
  var submitBtn=document.getElementById('dd-submit');
378
+ var titleEl=document.getElementById('dd-title');
379
+ var progressEl=document.getElementById('dd-progress');
380
+ var progressLabel=document.getElementById('dd-progress-label');
381
+ var progressCurrent=document.getElementById('dd-progress-current');
382
+ var progressTotal=document.getElementById('dd-progress-total');
383
+ var progressFollowUp=document.getElementById('dd-progress-followup');
384
+ var progressBar=document.getElementById('dd-progress-bar');
385
+
386
+ function setTitle(t){
387
+ if(!t) return;
388
+ titleEl.textContent=t;
389
+ titleEl.hidden=false;
390
+ }
391
+ setTitle(${titleJson});
392
+
393
+ // Estado de la barra de progreso. \`progressPref\` null = manda la plataforma (style).
394
+ var progressPref=${progressPref};
395
+ var progressEnabled=progressPref===true;
396
+ var progressShowUnit=true;
397
+ var progressUnit='fraction';
398
+ var onStartPage=false;
399
+
400
+ function updateProgress(p){
401
+ var total=p&&typeof p.total==='number'?p.total:0;
402
+ var progress=p&&typeof p.progress==='number'?p.progress:0;
403
+ // Solo tiene sentido con m\xE1s de una p\xE1gina, fuera de la pantalla de inicio y sin completar.
404
+ if(!progressEnabled||onStartPage||p&&p.completed||total<=1){
405
+ progressEl.style.display='none';
406
+ // Sin barra, el hueco cabecera\u2192pregunta lo pone el contenido.
407
+ content.style.paddingTop='16px';
408
+ return;
409
+ }
410
+ progressEl.style.display='flex';
411
+ // Con barra, el hueco inferior ya lo pone el bloque de progreso: no duplicarlo.
412
+ content.style.paddingTop='0';
413
+ // Espejo de LineProgressQuestion (MagicSurvey): la barra usa el valor real (las follow-up
414
+ // suman +0.5 y avanzan media casilla) y la etiqueta redondea hacia abajo, porque una
415
+ // follow-up es un paso DENTRO de la misma pregunta, no la siguiente.
416
+ var current=Math.min(total,Math.max(1,progress+1));
417
+ var pct=Math.min(100,Math.max(0,(current/total)*100));
418
+ progressBar.style.width=pct+'%';
419
+ var label=Math.min(total,Math.max(1,Math.floor(progress)+1));
420
+ progressLabel.style.display=progressShowUnit?'block':'none';
421
+ if(progressUnit==='percentage'){
422
+ progressCurrent.textContent=Math.round(pct)+'%';
423
+ progressTotal.textContent='';
424
+ } else {
425
+ progressCurrent.textContent='Question '+label;
426
+ progressTotal.textContent=' of '+total;
427
+ }
428
+ progressFollowUp.style.display=p&&p.followup?'inline-block':'none';
429
+ }
325
430
 
326
431
  backBtn.textContent=${backLabel};
327
432
  startBtn.textContent=${startLabel};
@@ -330,7 +435,13 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
330
435
 
331
436
  document.getElementById('dd-close').onclick=function(){ emitJSON('popup_close'); };
332
437
  backBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.back){ window.DeepdotsForm.back(); } };
333
- startBtn.onclick=function(){ if(window.DeepdotsForm && window.DeepdotsForm.startForm){ window.DeepdotsForm.startForm(); } };
438
+ startBtn.onclick=function(){
439
+ if(window.DeepdotsForm && window.DeepdotsForm.startForm){
440
+ window.DeepdotsForm.startForm();
441
+ onStartPage=false;
442
+ updateProgress({ progress:window.DeepdotsForm.progress, total:window.DeepdotsForm.total, completed:false, followup:false });
443
+ }
444
+ };
334
445
  completeBtn.onclick=function(){ emitJSON('popup_close'); };
335
446
  var surveyCompletedEmitted=false;
336
447
  submitBtn.onclick=function(){
@@ -338,6 +449,36 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
338
449
  if(window.DeepdotsForm && window.DeepdotsForm.send){ window.DeepdotsForm.send(); }
339
450
  };
340
451
 
452
+ // Mensaje final configurado en la plataforma (\`style.successMessage\`). Se guarda al cargar
453
+ // y se pinta al completar. Va por innerHTML porque es HTML del editor de la plataforma
454
+ // (imagen + texto), igual que ya hace \`renderStartMessage\` con el mensaje de inicio.
455
+ var successMessageHtml='';
456
+ function showSuccessScreen(){
457
+ progressEl.style.display='none';
458
+ var wrapper=document.getElementById('dd-form-wrapper');
459
+ if(wrapper){ wrapper.style.display='none'; }
460
+ var done=document.getElementById('dd-success');
461
+ if(!done){
462
+ done=document.createElement('div');
463
+ done.id='dd-success';
464
+ done.className='deepdots-success';
465
+ main.insertBefore(done, errorHint);
466
+ }
467
+ done.innerHTML=successMessageHtml || '<p>Thank you for your feedback!</p>';
468
+ done.style.display='block';
469
+ }
470
+
471
+ // Profundidad de navegaci\xF3n DENTRO del survey: +1 por p\xE1gina avanzada, -1 al volver.
472
+ // Sustituye a la condici\xF3n \`total>1 && progress>0 && progress<total\`, que escond\xEDa el
473
+ // Back siempre que la siguiente pantalla era una follow-up din\xE1mica: las follow-up no
474
+ // entran en el grafo (suman +0.5 al progress y no tocan el total), as\xED que un survey de
475
+ // una pregunta con follow-up tiene total=1 y nunca cumpl\xEDa \`total>1\`.
476
+ var pageDepth=0;
477
+
478
+ function updateNavButtons(){
479
+ updateButtons(pageDepth>0?'in_progress_next':'in_progress_first');
480
+ }
481
+
341
482
  function updateButtons(state){
342
483
  backBtn.style.display='none';
343
484
  startBtn.style.display='none';
@@ -382,10 +523,20 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
382
523
  form.generate('mf', {
383
524
  addButton:false,
384
525
  getMetaData:true,
526
+ // La pantalla final la pinta el popup: renderSuccess de @magicfeedback/native usa
527
+ // textContent, as\xED que el mensaje de la plataforma (HTML con imagen) no se ver\xEDa, y su
528
+ // fallback es un literal gen\xE9rico que ignora style.successMessage.
529
+ addSuccessScreen:false,
385
530
  onLoadedEvent:function(args){
386
531
  var s=args && args.formData ? args.formData.style : null;
387
532
  if(s && !stylesInjected){
388
533
  stylesInjected=true;
534
+ if(s.successMessage){ successMessageHtml=s.successMessage; }
535
+ // La barra de progreso la decide el host (init) y, si no se pronuncia, la plataforma.
536
+ if(progressPref===null){ progressEnabled=s.showProgressBar===true; }
537
+ if(s.showProgressUnit!==undefined){ progressShowUnit=s.showProgressUnit!==false; }
538
+ if(s.progressUnit){ progressUnit=s.progressUnit; }
539
+ if(s.loadingBarColor){ progressBar.style.background=s.loadingBarColor; progressFollowUp.style.background=s.loadingBarColor; }
389
540
  if(chromeOn && s.boxBackgroundColor){ popup.style.background=s.boxBackgroundColor; }
390
541
  if(s.contentAlign){ main.style.justifyContent = s.contentAlign==='center' ? 'center' : 'flex-start'; }
391
542
  if(s.buttonPrimaryColor){
@@ -394,7 +545,8 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
394
545
  completeBtn.style.background=s.buttonPrimaryColor; completeBtn.style.border='none'; completeBtn.style.color='#fff';
395
546
  }
396
547
  if(s.buttonSecondaryColor){
397
- backBtn.style.background='#fff'; backBtn.style.color=s.buttonSecondaryColor; backBtn.style.border='1px solid '+s.buttonSecondaryColor;
548
+ // Bot\xF3n de texto: solo ti\xF1e la etiqueta, sin recuperar fondo ni borde.
549
+ backBtn.style.background='transparent'; backBtn.style.color=s.buttonSecondaryColor; backBtn.style.border='none';
398
550
  }
399
551
  if(s.logo && !document.getElementById('dd-logo')){
400
552
  var logoImg=document.createElement('img');
@@ -407,11 +559,13 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
407
559
  else if(s.logoPosition==='center'){ logoImg.style.margin='0 auto 20px auto'; logoImg.style.display='block'; }
408
560
  main.insertBefore(logoImg, document.getElementById('dd-form-wrapper'));
409
561
  }
410
- if(s.startMessage && s.startMessage!==''){ updateButtons('start'); }
562
+ if(s.startMessage && s.startMessage!==''){ onStartPage=true; updateButtons('start'); }
411
563
  else { updateButtons('in_progress_first'); }
412
564
  } else {
413
565
  updateButtons('in_progress_first');
414
566
  }
567
+ // El total solo se conoce una vez montado el form; sin \xE9l no hay barra que pintar.
568
+ updateProgress({ progress:form.progress, total:form.total, completed:false, followup:false });
415
569
  emitJSON('loaded');
416
570
  setLoading(false);
417
571
  },
@@ -424,9 +578,10 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
424
578
  if(error){
425
579
  setLoading(false);
426
580
  if(error.toLowerCase().indexOf('no response')!==-1){
581
+ // La p\xE1gina no ha cambiado: el estado de navegaci\xF3n se queda como estaba.
427
582
  errorHint.textContent='Please answer the required question to continue.';
428
583
  errorHint.style.display='block';
429
- updateButtons('in_progress_next');
584
+ updateNavButtons();
430
585
  return;
431
586
  }
432
587
  errorHint.textContent='An error occurred while submitting. Please try again or close the popup.';
@@ -437,17 +592,23 @@ body{display:${bodyDisplay};justify-content:${pos.justifyContent};align-items:${
437
592
  setLoading(false);
438
593
  if(p && p.completed){
439
594
  surveyCompletedEmitted=true;
595
+ showSuccessScreen();
440
596
  updateButtons('completed');
597
+ updateProgress(p);
441
598
  emitJSON('survey_completed', p);
442
599
  return;
443
600
  }
444
- if(p && p.total>1 && p.progress>0 && p.progress<p.total){ updateButtons('in_progress_next'); }
445
- else { updateButtons('in_progress_first'); }
601
+ pageDepth++;
602
+ updateNavButtons();
603
+ updateProgress(p);
446
604
  emitJSON('after_submit', p);
447
605
  },
448
606
  onBackEvent:function(p){
449
- if(p && p.progress===0){ updateButtons('in_progress_first'); }
450
- else { updateButtons('in_progress_next'); }
607
+ // Con error ("No page found") no hubo navegaci\xF3n: la profundidad no se toca.
608
+ if(!(p&&p.error)){ pageDepth=Math.max(0,pageDepth-1); }
609
+ updateNavButtons();
610
+ // onBackEvent no trae \`total\`: se lee del form, que es su due\xF1o.
611
+ updateProgress({ progress:p&&p.progress, total:form.total, completed:false, followup:p&&p.followup });
451
612
  emitJSON('back', p);
452
613
  }
453
614
  }).catch(function(e){ setLoading(false); emit('error:init'); });
@@ -475,7 +636,7 @@ var ReactNativePopupRenderer = class {
475
636
  }
476
637
  init() {
477
638
  }
478
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, renderChrome) {
639
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, renderChrome, options) {
479
640
  this.emitFn = emit;
480
641
  this.onCloseFn = onClose;
481
642
  this.currentSurveyId = surveyId;
@@ -492,7 +653,10 @@ var ReactNativePopupRenderer = class {
492
653
  position: style?.position,
493
654
  actions,
494
655
  // renderChrome:false (init) → HTML sin tarjeta/overlay: el host lo envuelve en su Modal.
495
- chrome: renderChrome
656
+ chrome: renderChrome,
657
+ title: options?.title,
658
+ showProgressBar: options?.showProgressBar,
659
+ surveyCss: options?.surveyCss
496
660
  });
497
661
  if (this.options.onShow) {
498
662
  this.options.onShow({ surveyId, productId, html });
@@ -532,7 +696,6 @@ var ReactNativePopupRenderer = class {
532
696
  break;
533
697
  case "survey_completed":
534
698
  this.emitFn("survey_completed", surveyId);
535
- this.onCloseFn?.();
536
699
  break;
537
700
  case "popup_close":
538
701
  this.onCloseFn?.();
@@ -583,14 +746,14 @@ var BrowserPopupRenderer = class {
583
746
  document.body.appendChild(this.container);
584
747
  }
585
748
  }
586
- show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome) {
749
+ show(surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, _renderChrome, options) {
587
750
  if (this.visible) return;
588
751
  if (!this.container || !document.body.contains(this.container)) this.init();
589
752
  if (!this.container) return;
590
753
  this.visible = true;
591
754
  const container = this.container;
592
- void import("./renderPopup-UYMGEBSI.mjs").then(({ renderPopup }) => {
593
- renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId);
755
+ void import("./renderPopup-4WZ7V435.mjs").then(({ renderPopup }) => {
756
+ renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
594
757
  });
595
758
  }
596
759
  hide() {
@@ -1439,6 +1602,10 @@ var DeepdotsPopups = class {
1439
1602
  this.env = "production";
1440
1603
  /** Si el SDK pinta el "modal" del popup (RN). Default true; false = el host lo envuelve. */
1441
1604
  this.renderChrome = true;
1605
+ /** Barra de progreso. undefined = la decide la plataforma (style.showProgressBar). */
1606
+ this.showProgressBar = void 0;
1607
+ /** CSS del host inyectado al final de los estilos del popup. */
1608
+ this.surveyCss = void 0;
1442
1609
  /** Identidad + sesión (Fase 1 tracking). Null hasta init(). */
1443
1610
  this.tracking = null;
1444
1611
  /** Capa de analytics (canal separado del feedback). Null hasta init(). */
@@ -1490,6 +1657,8 @@ var DeepdotsPopups = class {
1490
1657
  this.baseUrl = env.apiBaseUrl;
1491
1658
  this.env = config.nodeEnv || "production";
1492
1659
  this.renderChrome = config.renderChrome ?? true;
1660
+ this.showProgressBar = config.showProgressBar;
1661
+ this.surveyCss = config.surveyCss;
1493
1662
  this.config = {
1494
1663
  apiKey: config.apiKey || void 0,
1495
1664
  debug: config.debug || false,
@@ -1935,7 +2104,7 @@ var DeepdotsPopups = class {
1935
2104
  this.log("Showing popup (definition)", def);
1936
2105
  this.surveyToPopupId.set(def.surveyId, def.id);
1937
2106
  this.lastShown.set(def.id, Date.now());
1938
- this.renderPopup(def.surveyId, def.productId, def.actions, def.style);
2107
+ this.renderPopup(def.surveyId, def.productId, def.actions, def.style, def.title);
1939
2108
  this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
1940
2109
  }
1941
2110
  shouldShow(def, pathUrl, skipPathCheck = false) {
@@ -2273,7 +2442,7 @@ var DeepdotsPopups = class {
2273
2442
  document.body.appendChild(this.popupContainer);
2274
2443
  }
2275
2444
  /** Render the popup UI */
2276
- renderPopup(surveyId, productId, actions, style) {
2445
+ renderPopup(surveyId, productId, actions, style, title) {
2277
2446
  const userId = this.tracking?.getUserId() ?? this.config?.userId;
2278
2447
  const sessionId = this.tracking?.getSessionId() ?? void 0;
2279
2448
  const miniService = this.analytics?.getMiniService() ?? void 0;
@@ -2292,13 +2461,14 @@ var DeepdotsPopups = class {
2292
2461
  sessionId,
2293
2462
  miniService,
2294
2463
  analyticsFeedbackSessionId,
2295
- this.renderChrome
2464
+ this.renderChrome,
2465
+ { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss }
2296
2466
  );
2297
2467
  return;
2298
2468
  }
2299
2469
  if (!this.popupContainer) return;
2300
2470
  const container = this.popupContainer;
2301
- void import("./renderPopup-UYMGEBSI.mjs").then(({ renderPopup }) => {
2471
+ void import("./renderPopup-4WZ7V435.mjs").then(({ renderPopup }) => {
2302
2472
  renderPopup(
2303
2473
  container,
2304
2474
  surveyId,
@@ -2311,7 +2481,8 @@ var DeepdotsPopups = class {
2311
2481
  style,
2312
2482
  sessionId,
2313
2483
  miniService,
2314
- analyticsFeedbackSessionId
2484
+ analyticsFeedbackSessionId,
2485
+ { title, showProgressBar: this.showProgressBar, surveyCss: this.surveyCss }
2315
2486
  );
2316
2487
  });
2317
2488
  }