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