@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.
@@ -6,7 +6,7 @@ import {
6
6
  sdkLog,
7
7
  sdkWarn,
8
8
  style_default
9
- } from "./chunk-R4B3K2ZW.mjs";
9
+ } from "./chunk-D7QAGWGC.mjs";
10
10
 
11
11
  // src/ui/renderPopup.ts
12
12
  import magicfeedback from "@magicfeedback/native";
@@ -23,6 +23,10 @@ function ensureSpinnerStyles(_popup) {
23
23
  const style = document.createElement("style");
24
24
  style.id = "deepdots-spinner-styles";
25
25
  style.textContent = `
26
+ /* El enunciado trae margin-top:20px del CSS de surveys, que sumaba al hueco de la barra
27
+ de progreso y descompensaba la simetr\xEDa. Solo se anula en el primero: entre preguntas
28
+ de la misma p\xE1gina sigue separando. */
29
+ .deepdots-popup .magicfeedback-questions .magicfeedback-div:first-child .magicfeedback-label { margin-top: 0; }
26
30
  @keyframes ddspin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
27
31
  .mf-spinner { display:flex; justify-content:center; align-items:center; padding:8px 0; }
28
32
  .mf-spinner-circle { width:28px; height:28px; border:3px solid #e0e6ed; border-top-color:#1E293B; border-radius:50%; animation: ddspin 0.9s linear infinite; }
@@ -49,7 +53,9 @@ function ensureResponsiveStyles(_popup) {
49
53
  .deepdots-popup button { font-size: 16px !important; }
50
54
  .deepdots-popup-header button { width:48px; height:48px; }
51
55
  .deepdots-popup-header button svg { width:26px; height:26px; }
52
- .deepdots-popup-footer { flex-direction: column-reverse !important; gap: 12px !important; }
56
+ /* Apilados en el orden del DOM: la acci\xF3n principal (Send/Start/Complete) arriba y
57
+ Back debajo. */
58
+ .deepdots-popup-footer { flex-direction: column !important; gap: 4px !important; }
53
59
  .deepdots-popup-footer button { width: 100%; }
54
60
  }
55
61
  @media (max-width: 400px) {
@@ -67,6 +73,18 @@ function ensureResponsiveStyles(_popup) {
67
73
  `;
68
74
  document.head.appendChild(style);
69
75
  }
76
+ function ensureCustomCss(css) {
77
+ const STYLE_ID = "deepdots-custom-css";
78
+ const existing = document.getElementById(STYLE_ID);
79
+ if (!css) {
80
+ existing?.remove();
81
+ return;
82
+ }
83
+ const el = existing ?? document.createElement("style");
84
+ el.id = STYLE_ID;
85
+ if (el.textContent !== css) el.textContent = css;
86
+ document.head.appendChild(el);
87
+ }
70
88
  function ensureFontFace(family, url) {
71
89
  const css = buildFontFaceCss(family, url);
72
90
  if (!css) return;
@@ -79,9 +97,11 @@ function ensureFontFace(family, url) {
79
97
  }
80
98
  if (el.textContent !== css) el.textContent = css;
81
99
  }
82
- async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId) {
100
+ async function renderPopup(container, surveyId, productId, actions, emit, onClose, env = "production", userId, style, sessionId, miniService, analyticsFeedbackSessionId, options) {
83
101
  let surveyCompletedEmitted = false;
84
102
  let stylesInjected = false;
103
+ let pageDepth = 0;
104
+ let onStartPage = false;
85
105
  const isDark = style?.theme === "dark";
86
106
  const theme = {
87
107
  popupBg: isDark ? "#1e1e1e" : "#fff",
@@ -92,7 +112,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
92
112
  colorScheme: isDark ? "dark" : "light",
93
113
  textPrimary: isDark ? "#f0f0f0" : "#111",
94
114
  closeBtnHoverBg: isDark ? "rgba(255,255,255,0.10)" : "rgba(0,0,0,0.06)",
95
- closeBtnHoverColor: isDark ? "#fff" : "#000000"
115
+ closeBtnHoverColor: isDark ? "#fff" : "#000000",
116
+ textMuted: isDark ? "#9ca3af" : "#6b7280",
117
+ progressTrack: isDark ? "#3f3f46" : "#e5e7eb"
96
118
  };
97
119
  const positionMap = {
98
120
  "center": { justifyContent: "center", alignItems: "center", background: "rgba(0,0,0,0.5)" },
@@ -105,6 +127,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
105
127
  };
106
128
  const pos = positionMap[style?.position ?? "center"] ?? positionMap["center"];
107
129
  const popup = document.createElement("div");
130
+ popup.id = "dd-popup";
108
131
  popup.className = "deepdots-popup";
109
132
  popup.style.cssText = `
110
133
  position: relative;
@@ -118,6 +141,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
118
141
  box-shadow: 0 4px 6px rgba(0,0,0,0.1);
119
142
  max-width: 600px;
120
143
  width: 90%;
144
+ max-height: 90vh;
121
145
  min-height: 200px;
122
146
  `;
123
147
  const font = style?.font;
@@ -130,8 +154,21 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
130
154
  }
131
155
  const header = document.createElement("div");
132
156
  header.className = "deepdots-popup-header";
133
- header.style.cssText = "display:flex; justify-content:flex-end; align-items:center; width:100%;";
157
+ header.style.cssText = "display:flex; justify-content:space-between; align-items:center; gap:12px; width:100%; flex:0 0 auto;";
158
+ const titleEl = document.createElement("h2");
159
+ titleEl.id = "dd-title";
160
+ titleEl.className = "deepdots-popup-title";
161
+ titleEl.style.cssText = `margin:0; font-size:17px; font-weight:600; line-height:1.3; color:${theme.textPrimary}; text-transform:none; text-align:left; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;`;
162
+ titleEl.hidden = true;
163
+ const setTitle = (value) => {
164
+ if (!value) return;
165
+ titleEl.textContent = value;
166
+ titleEl.hidden = false;
167
+ };
168
+ setTitle(options?.title);
169
+ header.appendChild(titleEl);
134
170
  const closeBtn = document.createElement("button");
171
+ closeBtn.id = "dd-close";
135
172
  closeBtn.type = "button";
136
173
  closeBtn.setAttribute("aria-label", "Close popup");
137
174
  closeBtn.innerHTML = `
@@ -168,25 +205,86 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
168
205
  emit("popup_clicked", surveyId, { action: "close_icon" });
169
206
  onClose();
170
207
  };
208
+ closeBtn.style.flex = "0 0 auto";
209
+ closeBtn.style.marginLeft = "auto";
171
210
  header.appendChild(closeBtn);
211
+ let progressEnabled = options?.showProgressBar === true;
212
+ let progressShowUnit = true;
213
+ let progressUnit = "fraction";
214
+ const progressEl = document.createElement("div");
215
+ progressEl.id = "dd-progress";
216
+ progressEl.className = "deepdots-progress";
217
+ progressEl.style.cssText = "display:none; flex-direction:column; gap:8px; width:100%; flex:0 0 auto; padding:16px 0; box-sizing:border-box;";
218
+ const progressHead = document.createElement("div");
219
+ progressHead.className = "deepdots-progress-head";
220
+ progressHead.style.cssText = "display:flex; flex-direction:row; justify-content:space-between; align-items:center; width:100%; gap:8px;";
221
+ const progressLabel = document.createElement("span");
222
+ progressLabel.id = "dd-progress-label";
223
+ progressLabel.style.cssText = "font-size:13px; line-height:1.2;";
224
+ const progressCurrent = document.createElement("span");
225
+ progressCurrent.id = "dd-progress-current";
226
+ progressCurrent.style.cssText = `font-weight:700; color:${theme.textPrimary};`;
227
+ const progressTotal = document.createElement("span");
228
+ progressTotal.id = "dd-progress-total";
229
+ progressTotal.style.cssText = `font-weight:400; color:${theme.textMuted};`;
230
+ progressLabel.appendChild(progressCurrent);
231
+ progressLabel.appendChild(progressTotal);
232
+ const progressFollowUp = document.createElement("span");
233
+ progressFollowUp.id = "dd-progress-followup";
234
+ progressFollowUp.textContent = "Follow-up";
235
+ progressFollowUp.style.cssText = "display:none; font-size:12px; font-weight:600; color:#fff; background:rgba(59,130,246,0.44); border-radius:999px; padding:2px 10px;";
236
+ progressHead.appendChild(progressLabel);
237
+ progressHead.appendChild(progressFollowUp);
238
+ const progressTrack = document.createElement("div");
239
+ progressTrack.className = "deepdots-progress-track";
240
+ progressTrack.style.cssText = `width:100%; height:4px; border-radius:999px; background:${theme.progressTrack}; overflow:hidden;`;
241
+ const progressBar = document.createElement("div");
242
+ progressBar.id = "dd-progress-bar";
243
+ progressBar.style.cssText = "height:100%; width:0%; background:#22C55E; border-radius:999px; transition:width 450ms ease;";
244
+ progressTrack.appendChild(progressBar);
245
+ progressEl.appendChild(progressHead);
246
+ progressEl.appendChild(progressTrack);
247
+ function updateProgress(p) {
248
+ const total = typeof p.total === "number" ? p.total : 0;
249
+ const progress = typeof p.progress === "number" ? p.progress : 0;
250
+ if (!progressEnabled || onStartPage || p.completed || total <= 1) {
251
+ progressEl.style.display = "none";
252
+ containerContent.style.paddingTop = "16px";
253
+ return;
254
+ }
255
+ progressEl.style.display = "flex";
256
+ containerContent.style.paddingTop = "0";
257
+ const current = Math.min(total, Math.max(1, progress + 1));
258
+ const pct = Math.min(100, Math.max(0, current / total * 100));
259
+ progressBar.style.width = `${pct}%`;
260
+ const label = Math.min(total, Math.max(1, Math.floor(progress) + 1));
261
+ progressLabel.style.display = progressShowUnit ? "block" : "none";
262
+ if (progressUnit === "percentage") {
263
+ progressCurrent.textContent = `${Math.round(pct)}%`;
264
+ progressTotal.textContent = "";
265
+ } else {
266
+ progressCurrent.textContent = `Question ${label}`;
267
+ progressTotal.textContent = ` of ${total}`;
268
+ }
269
+ progressFollowUp.style.display = p.followup ? "inline-block" : "none";
270
+ }
172
271
  ensureMagicFeedbackStyles(popup);
173
272
  ensureSpinnerStyles(popup);
174
273
  ensureResponsiveStyles(popup);
274
+ ensureCustomCss(options?.surveyCss);
175
275
  const containerContent = document.createElement("div");
176
- containerContent.className = "deepdots-popup-container-conetent";
177
- containerContent.style.cssText = `
178
- display:flex;
179
- flex-direction:column;
180
- padding: 0 20px 12px 20px;
181
- max-height: 80vh; /* l\xEDmite general del popup */
182
- overflow: hidden; /* quita scroll del contenedor principal */
183
- `;
276
+ containerContent.id = "dd-content";
277
+ containerContent.className = "deepdots-popup-container-content";
278
+ containerContent.style.cssText = "display:flex; flex-direction:column; flex:1 1 auto; min-height:0; padding:16px 0 0 0; overflow:hidden;";
184
279
  const main = document.createElement("div");
280
+ main.id = "dd-main";
185
281
  main.className = "deepdots-popup-main";
186
- main.style.cssText = "display:flex; flex-direction:column; width:100%; max-height:80vh; overflow-y:auto;";
282
+ main.style.cssText = "display:flex; flex-direction:column; width:100%; flex:1 1 auto; min-height:0; overflow-y:auto;";
187
283
  const formWrapper = document.createElement("div");
284
+ formWrapper.id = "dd-form-wrapper";
188
285
  formWrapper.style.cssText = "width:100%; flex: 1 1 auto;";
189
286
  const errorHint = document.createElement("div");
287
+ errorHint.id = "dd-error";
190
288
  errorHint.className = "deepdots-error-hint";
191
289
  errorHint.style.cssText = `
192
290
  display: none;
@@ -215,20 +313,24 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
215
313
  main.appendChild(formWrapper);
216
314
  main.appendChild(errorHint);
217
315
  const footer = document.createElement("div");
316
+ footer.id = "dd-footer";
218
317
  footer.className = "deepdots-popup-footer";
219
318
  footer.setAttribute("data-actions-wrapper", "true");
220
- footer.style.cssText = "display:flex; flex-direction: row-reverse ;justify-content:space-between; align-items:center; gap:8px; margin-top:auto; width:100%; padding-top:16px;";
319
+ footer.style.cssText = "display:flex; flex-direction: row-reverse ;justify-content:space-between; align-items:center; gap:4px; margin-top:auto; width:100%; padding-top:12px;";
221
320
  const backButton = document.createElement("button");
321
+ backButton.id = "dd-back";
322
+ backButton.className = "dd-nav-btn";
222
323
  backButton.textContent = actions?.back ? actions.back.label : "Back";
223
324
  backButton.style.cssText = `
224
325
  background: transparent;
225
- color: #333;
226
- border: 1px solid #999;
326
+ color: ${theme.textMuted};
327
+ border: none;
328
+ min-height: 44px;
227
329
  padding: 12px 24px;
228
- border-radius: 4px;
330
+ border-radius: 6px;
229
331
  cursor: pointer;
230
- font-size: 14px;
231
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
332
+ font-size: 15px;
333
+ font-weight: 600;
232
334
  transition: filter .15s ease;
233
335
  display: inline-flex;
234
336
  align-items: center;
@@ -246,15 +348,19 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
246
348
  formInstance?.back?.();
247
349
  };
248
350
  const startButton = document.createElement("button");
351
+ startButton.id = "dd-start";
352
+ startButton.className = "dd-nav-btn";
249
353
  startButton.textContent = actions?.start ? actions.start.label : "Start survey";
250
354
  startButton.style.cssText = `
251
355
  background: #1E293B;
252
356
  color: #fff;
253
357
  border: none;
358
+ min-height: 44px;
254
359
  padding: 12px 24px;
255
- border-radius: 4px;
360
+ border-radius: 6px;
256
361
  cursor: pointer;
257
- font-size: 14px;
362
+ font-size: 15px;
363
+ font-weight: 600;
258
364
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
259
365
  transition: filter .15s ease;
260
366
  display: inline-flex;
@@ -265,17 +371,23 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
265
371
  startButton.onclick = () => {
266
372
  emit("popup_clicked", surveyId, { action: "start_survey" });
267
373
  formInstance?.startForm?.();
374
+ onStartPage = false;
375
+ updateProgress({ progress: formInstance?.progress, total: formInstance?.total });
268
376
  };
269
377
  const closeButton = document.createElement("button");
378
+ closeButton.id = "dd-complete";
379
+ closeButton.className = "dd-nav-btn";
270
380
  closeButton.textContent = actions?.complete ? actions.complete.label : "Complete survey";
271
381
  closeButton.style.cssText = `
272
382
  background: #1E293B;
273
383
  color: #fff;
274
384
  border: none;
385
+ min-height: 44px;
275
386
  padding: 12px 24px;
276
- border-radius: 4px;
387
+ border-radius: 6px;
277
388
  cursor: pointer;
278
- font-size: 14px;
389
+ font-size: 15px;
390
+ font-weight: 600;
279
391
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
280
392
  transition: filter .15s ease;
281
393
  display: inline-flex;
@@ -294,15 +406,19 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
294
406
  onClose();
295
407
  };
296
408
  const submitButton = document.createElement("button");
409
+ submitButton.id = "dd-submit";
410
+ submitButton.className = "dd-nav-btn";
297
411
  submitButton.textContent = actions?.accept ? actions.accept.label : "Send";
298
412
  submitButton.style.cssText = `
299
413
  background: #1E293B;
300
414
  color: #fff;
301
415
  border: none;
416
+ min-height: 44px;
302
417
  padding: 12px 24px;
303
- border-radius: 4px;
418
+ border-radius: 6px;
304
419
  cursor: pointer;
305
- font-size: 14px;
420
+ font-size: 15px;
421
+ font-weight: 600;
306
422
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
307
423
  transition: filter .15s ease;
308
424
  display: inline-flex;
@@ -324,8 +440,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
324
440
  footer.appendChild(backButton);
325
441
  footer.appendChild(closeButton);
326
442
  footer.appendChild(startButton);
327
- main.appendChild(footer);
328
443
  containerContent.appendChild(main);
444
+ footer.style.flex = "0 0 auto";
445
+ containerContent.appendChild(footer);
329
446
  function updateButtons(state) {
330
447
  backButton.style.display = "none";
331
448
  startButton.style.display = "none";
@@ -362,7 +479,27 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
362
479
  break;
363
480
  }
364
481
  }
482
+ function updateNavButtons() {
483
+ updateButtons(pageDepth > 0 ? "in_progress_next" : "in_progress_first");
484
+ }
485
+ let successMessageHtml = "";
486
+ function showSuccessScreen() {
487
+ progressEl.style.display = "none";
488
+ formWrapper.style.display = "none";
489
+ let done = main.querySelector(".deepdots-success");
490
+ if (!done) {
491
+ done = document.createElement("div");
492
+ done.className = "deepdots-success";
493
+ done.style.cssText = `width:100%; text-align:center; padding:24px 0; color:${theme.textPrimary};`;
494
+ main.insertBefore(done, errorHint);
495
+ }
496
+ done.innerHTML = successMessageHtml || "<p>Thank you for your feedback!</p>";
497
+ done.querySelectorAll("img").forEach((img) => {
498
+ img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
499
+ });
500
+ }
365
501
  popup.appendChild(header);
502
+ popup.appendChild(progressEl);
366
503
  popup.appendChild(containerContent);
367
504
  container.innerHTML = "";
368
505
  container.appendChild(popup);
@@ -408,7 +545,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
408
545
  formInstance = magicfeedback.form(surveyId, productId, profile, metadata);
409
546
  const generateOptions = {
410
547
  addButton: false,
411
- getMetaData: true
548
+ getMetaData: true,
549
+ // La pantalla final la pinta el popup: `renderSuccess` de @magicfeedback/native usa
550
+ // textContent, así que el mensaje de la plataforma (HTML con imagen) no se vería, y
551
+ // su fallback es un literal genérico que ignora `style.successMessage`.
552
+ addSuccessScreen: false
412
553
  };
413
554
  generateOptions.onLoadedEvent = ({ formData }) => {
414
555
  try {
@@ -417,6 +558,14 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
417
558
  const s = formData?.style;
418
559
  if (s && !stylesInjected) {
419
560
  stylesInjected = true;
561
+ if (s.successMessage) successMessageHtml = s.successMessage;
562
+ if (options?.showProgressBar === void 0) progressEnabled = s.showProgressBar === true;
563
+ if (s.showProgressUnit !== void 0) progressShowUnit = s.showProgressUnit !== false;
564
+ if (s.progressUnit) progressUnit = s.progressUnit;
565
+ if (s.loadingBarColor) {
566
+ progressBar.style.background = s.loadingBarColor;
567
+ progressFollowUp.style.background = s.loadingBarColor;
568
+ }
420
569
  if (s.boxBackgroundColor) {
421
570
  popup.style.background = s.boxBackgroundColor;
422
571
  }
@@ -435,9 +584,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
435
584
  closeButton.style.color = "#fff";
436
585
  }
437
586
  if (s.buttonSecondaryColor) {
438
- backButton.style.background = "#fff";
587
+ backButton.style.background = "transparent";
439
588
  backButton.style.color = s.buttonSecondaryColor;
440
- backButton.style.border = `1px solid ${s.buttonSecondaryColor}`;
589
+ backButton.style.border = "none";
441
590
  }
442
591
  if (s.logo) {
443
592
  if (!document.getElementById("deepdots-popup-logo")) {
@@ -482,6 +631,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
482
631
  }
483
632
  if (s.startMessage && s.startMessage !== "") {
484
633
  sdkLog(s.startMessage);
634
+ onStartPage = true;
485
635
  updateButtons("start");
486
636
  } else {
487
637
  updateButtons("in_progress_first");
@@ -489,6 +639,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
489
639
  } else {
490
640
  updateButtons("in_progress_first");
491
641
  }
642
+ updateProgress({ progress: formInstance.progress, total: formInstance.total });
492
643
  emit("popup_clicked", surveyId, { action: "loaded" });
493
644
  setLoading(false);
494
645
  };
@@ -496,7 +647,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
496
647
  setLoading(true);
497
648
  emit("popup_clicked", surveyId, { action: "before_submit" });
498
649
  };
499
- generateOptions.afterSubmitEvent = ({ error, completed, total, progress }) => {
650
+ generateOptions.afterSubmitEvent = ({ error, completed, total, progress, followup }) => {
500
651
  const errText = error ? typeof error === "string" ? error : error.message ?? String(error) : "";
501
652
  if (errText) {
502
653
  setLoading(false);
@@ -504,7 +655,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
504
655
  errorHint.textContent = "Please answer the required question to continue.";
505
656
  errorHint.style.display = "block";
506
657
  emit("popup_clicked", surveyId, { action: "validation_error_required" });
507
- updateButtons("in_progress_next");
658
+ updateNavButtons();
508
659
  return;
509
660
  }
510
661
  errorHint.textContent = "An error occurred while submitting. Please try again or close the popup.";
@@ -517,23 +668,21 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
517
668
  if (completed) {
518
669
  emit("survey_completed", surveyId, { action: "completed" });
519
670
  surveyCompletedEmitted = true;
671
+ showSuccessScreen();
520
672
  updateButtons("completed");
673
+ updateProgress({ progress, total, completed: true });
521
674
  return;
522
675
  }
523
676
  emit("popup_clicked", surveyId, { action: "partial" });
524
- if (total > 1 && progress > 0 && progress < total) {
525
- updateButtons("in_progress_next");
526
- } else {
527
- updateButtons("in_progress_first");
528
- }
677
+ pageDepth++;
678
+ updateNavButtons();
679
+ updateProgress({ progress, total, followup });
529
680
  };
530
- generateOptions.onBackEvent = ({ progress }) => {
681
+ generateOptions.onBackEvent = ({ progress, error, followup }) => {
531
682
  emit("popup_clicked", surveyId, { action: "back" });
532
- if (progress === 0) {
533
- updateButtons("in_progress_first");
534
- } else {
535
- updateButtons("in_progress_next");
536
- }
683
+ if (!error) pageDepth = Math.max(0, pageDepth - 1);
684
+ updateNavButtons();
685
+ updateProgress({ progress, total: formInstance.total, followup });
537
686
  };
538
687
  formInstance.generate(formDivId, generateOptions).catch((err) => {
539
688
  sdkError("[MagicFeedback] Error generating form:", err);
@@ -547,4 +696,4 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
547
696
  export {
548
697
  renderPopup
549
698
  };
550
- //# sourceMappingURL=renderPopup-UYMGEBSI.mjs.map
699
+ //# sourceMappingURL=renderPopup-4WZ7V435.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ui/renderPopup.ts"],"sourcesContent":["import {DeepdotsEventType, PopupActions, PopupStyle, FormData} from '../types';\nimport { buildSurveyIdentity } from '../tracking/tracking-manager';\nimport type { PopupRenderOptions } from '../platform/renderer';\nimport { buildFontFaceCss, buildFontFamilyValue } from './font';\nimport { sdkLog, sdkWarn, sdkError } from '../util/logger';\nimport magicfeedback from \"@magicfeedback/native\";\nimport magicfeedbackCss from '../assets/style.css';\n\n// Inserta la hoja de estilos de MagicFeedback directamente en el popup para garantizar estilos incluso si el bundler no la inyecta globalmente.\nfunction ensureMagicFeedbackStyles(_popup: HTMLElement) {\n const STYLE_ID = 'magicfeedback-sdk-styles';\n if (document.getElementById(STYLE_ID)) return;\n const style = document.createElement('style');\n style.id = STYLE_ID;\n style.textContent = magicfeedbackCss;\n document.head.appendChild(style);\n}\n\n// Añade estilos de spinner si no existen\nfunction ensureSpinnerStyles(_popup: HTMLElement) {\n if (document.getElementById('deepdots-spinner-styles')) return;\n const style = document.createElement('style');\n style.id = 'deepdots-spinner-styles';\n style.textContent = `\n /* El enunciado trae margin-top:20px del CSS de surveys, que sumaba al hueco de la barra\n de progreso y descompensaba la simetría. Solo se anula en el primero: entre preguntas\n de la misma página sigue separando. */\n .deepdots-popup .magicfeedback-questions .magicfeedback-div:first-child .magicfeedback-label { margin-top: 0; }\n @keyframes ddspin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }\n .mf-spinner { display:flex; justify-content:center; align-items:center; padding:8px 0; }\n .mf-spinner-circle { width:28px; height:28px; border:3px solid #e0e6ed; border-top-color:#1E293B; border-radius:50%; animation: ddspin 0.9s linear infinite; }\n `;\n document.head.appendChild(style);\n}\n\nfunction ensureResponsiveStyles(_popup: HTMLElement) {\n if (document.getElementById('deepdots-responsive-styles')) return;\n const style = document.createElement('style');\n style.id = 'deepdots-responsive-styles';\n style.textContent = `\n /* Responsive adjustments */\n @media (max-width: 640px) {\n .deepdots-popup {\n width: calc(100% - 24px) !important;\n max-width: calc(100% - 24px) !important;\n height: auto !important;\n max-height: 90vh !important;\n border-radius: 12px !important;\n padding: calc(16px + env(safe-area-inset-top)) 16px calc(16px + env(safe-area-inset-bottom)) 16px !important;\n box-sizing: border-box;\n }\n .deepdots-popup .mf-spinner-circle { width: 32px; height: 32px; border-width: 4px; }\n .deepdots-popup button { font-size: 16px !important; }\n .deepdots-popup-header button { width:48px; height:48px; }\n .deepdots-popup-header button svg { width:26px; height:26px; }\n /* Apilados en el orden del DOM: la acción principal (Send/Start/Complete) arriba y\n Back debajo. */\n .deepdots-popup-footer { flex-direction: column !important; gap: 4px !important; }\n .deepdots-popup-footer button { width: 100%; }\n }\n @media (max-width: 400px) {\n .deepdots-popup { padding: calc(12px + env(safe-area-inset-top)) 12px calc(12px + env(safe-area-inset-bottom)) 12px !important; }\n .deepdots-popup-header button { width:48px; height:48px; }\n .deepdots-popup-header button svg { width:26px; height:26px; }\n }\n @media (orientation: landscape) and (max-height: 480px) {\n .deepdots-popup {\n height: 100vh !important;\n max-height: 100vh !important;\n overflow-y: auto !important;\n }\n }\n `;\n document.head.appendChild(style);\n}\n\n/**\n * CSS del host, en un <style> propio añadido DESPUÉS de los del SDK y de\n * `@magicfeedback/native` para que gane en cascada sin tocar ninguno de los dos.\n * Se reemplaza en cada render por si cambia entre popups.\n */\nfunction ensureCustomCss(css?: string) {\n const STYLE_ID = 'deepdots-custom-css';\n const existing = document.getElementById(STYLE_ID);\n if (!css) {\n existing?.remove();\n return;\n }\n const el = (existing as HTMLStyleElement | null) ?? document.createElement('style');\n el.id = STYLE_ID;\n if (el.textContent !== css) el.textContent = css;\n // Reanexar mueve el nodo al final del head: garantiza que va tras las hojas del SDK.\n document.head.appendChild(el);\n}\n\n// Inyecta (una sola vez) el @font-face de la fuente personalizada.\nfunction ensureFontFace(family: string, url: string) {\n const css = buildFontFaceCss(family, url);\n if (!css) return;\n const STYLE_ID = 'deepdots-font-face';\n let el = document.getElementById(STYLE_ID) as HTMLStyleElement | null;\n if (!el) {\n el = document.createElement('style');\n el.id = STYLE_ID;\n document.head.appendChild(el);\n }\n if (el.textContent !== css) el.textContent = css;\n}\n\n/**\n * Renderiza el popup dentro del contenedor dado usando MagicFeedback para la encuesta.\n */\nexport async function renderPopup(\n container: HTMLElement,\n surveyId: string,\n productId: string,\n actions: PopupActions | undefined,\n emit: (type: DeepdotsEventType, surveyId: string, data?: Record<string, unknown>) => void,\n onClose: () => void,\n env: string = 'production',\n userId?: string,\n style?: PopupStyle,\n sessionId?: string,\n miniService?: string,\n analyticsFeedbackSessionId?: string,\n options?: PopupRenderOptions,\n): Promise<void> {\n let surveyCompletedEmitted = false;\n let stylesInjected = false;\n // Profundidad de navegación dentro del survey: +1 al avanzar, -1 al volver. Sustituye a\n // `total > 1 && progress > 0 && progress < total`, que escondía el Back cuando la pantalla\n // siguiente era una follow-up dinámica (no entran en el grafo: suman +0.5 al progress y\n // dejan el total igual, así que un survey de una pregunta con follow-up nunca lo mostraba).\n let pageDepth = 0;\n let onStartPage = false;\n\n const isDark = style?.theme === 'dark';\n const theme = {\n popupBg: isDark ? '#1e1e1e' : '#fff',\n // El popup fija su propio fondo, así que debe fijar también el esquema de color.\n // Sin esto los controles de formulario (input/textarea/select) caen al estilo por\n // defecto del navegador: en un host con `prefers-color-scheme: dark` se pintan\n // oscuros con texto blanco sobre el fondo claro del popup.\n colorScheme: isDark ? 'dark' : 'light',\n textPrimary: isDark ? '#f0f0f0' : '#111',\n closeBtnHoverBg: isDark ? 'rgba(255,255,255,0.10)' : 'rgba(0,0,0,0.06)',\n closeBtnHoverColor: isDark ? '#fff' : '#000000',\n textMuted: isDark ? '#9ca3af' : '#6b7280',\n progressTrack: isDark ? '#3f3f46' : '#e5e7eb',\n };\n\n const positionMap: Record<string, { justifyContent: string; alignItems: string; padding?: string; background: string }> = {\n 'center': { justifyContent: 'center', alignItems: 'center', background: 'rgba(0,0,0,0.5)' },\n 'bottom': { justifyContent: 'center', alignItems: 'flex-end', padding: '16px', background: 'transparent' },\n 'bottom-right': { justifyContent: 'flex-end', alignItems: 'flex-end', padding: '16px', background: 'transparent' },\n 'bottom-left': { justifyContent: 'flex-start', alignItems: 'flex-end', padding: '16px', background: 'transparent' },\n 'top': { justifyContent: 'center', alignItems: 'flex-start', padding: '16px', background: 'transparent' },\n 'top-right': { justifyContent: 'flex-end', alignItems: 'flex-start', padding: '16px', background: 'transparent' },\n 'top-left': { justifyContent: 'flex-start', alignItems: 'flex-start', padding: '16px', background: 'transparent' },\n };\n const pos = positionMap[style?.position ?? 'center'] ?? positionMap['center'];\n\n // Crear popup base\n const popup = document.createElement('div');\n popup.id = 'dd-popup';\n popup.className = 'deepdots-popup';\n popup.style.cssText = `\n position: relative;\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n background: ${theme.popupBg};\n color-scheme: ${theme.colorScheme};\n border-radius: 8px;\n padding: 24px;\n box-shadow: 0 4px 6px rgba(0,0,0,0.1);\n max-width: 600px;\n width: 90%;\n max-height: 90vh;\n min-height: 200px;\n `;\n\n const font = style?.font;\n if (font?.family) {\n const familyValue = buildFontFamilyValue(font.family);\n // El survey vive dentro del contenedor y hereda esta fuente.\n popup.style.fontFamily = familyValue;\n // La variable habilita que el <h2> (Montserrat por defecto) también la use.\n popup.style.setProperty('--deepdots-font', familyValue);\n // Los controles de formulario (button/input/textarea/select) NO heredan\n // font-family por defecto; esta clase activa la regla que los fuerza a heredar.\n popup.classList.add('deepdots-has-font');\n if (font.url) ensureFontFace(font.family, font.url);\n }\n\n // Sección header (título + botón cerrar)\n const header = document.createElement('div');\n header.className = 'deepdots-popup-header';\n header.style.cssText = 'display:flex; justify-content:space-between; align-items:center; gap:12px; width:100%; flex:0 0 auto;';\n\n // Título: `textContent`, nunca innerHTML — el valor viene de la API.\n const titleEl = document.createElement('h2');\n titleEl.id = 'dd-title';\n titleEl.className = 'deepdots-popup-title';\n // text-transform/text-align/margin neutralizan la regla `.deepdots-popup h2` del CSS del\n // survey (uppercase, centrado, margin-bottom 40px), pensada para los enunciados.\n titleEl.style.cssText = `margin:0; font-size:17px; font-weight:600; line-height:1.3; color:${theme.textPrimary}; text-transform:none; text-align:left; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;`;\n titleEl.hidden = true;\n const setTitle = (value?: string) => {\n if (!value) return;\n titleEl.textContent = value;\n titleEl.hidden = false;\n };\n setTitle(options?.title);\n header.appendChild(titleEl);\n\n // Botón de cierre (X)\n const closeBtn = document.createElement('button');\n closeBtn.id = 'dd-close';\n closeBtn.type = 'button';\n closeBtn.setAttribute('aria-label', 'Close popup');\n closeBtn.innerHTML = `\n <svg width=\"28\" height=\"28\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M6 6L18 18M6 18L18 6\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n `;\n closeBtn.style.cssText = `\n background:transparent;\n border:none;\n width:32px;\n height:32px;\n display:flex;\n align-items:center;\n justify-content:center;\n border-radius: 8px;\n cursor:pointer;\n color:${theme.textPrimary};\n padding:4px;\n transition: color .15s ease, transform .15s ease, background .15s ease;\n box-shadow: none !important;\n `;\n closeBtn.onmouseenter = () => {\n closeBtn.style.color = theme.closeBtnHoverColor;\n closeBtn.style.background = theme.closeBtnHoverBg;\n closeBtn.style.transform = 'scale(1.06)';\n };\n closeBtn.onmouseleave = () => {\n closeBtn.style.color = theme.textPrimary;\n closeBtn.style.background = 'transparent';\n closeBtn.style.transform = 'scale(1)';\n };\n closeBtn.onclick = () => {\n emit('popup_clicked', surveyId, {action: 'close_icon'});\n onClose();\n };\n closeBtn.style.flex = '0 0 auto';\n closeBtn.style.marginLeft = 'auto';\n header.appendChild(closeBtn);\n\n // Barra de progreso (\"Question X of Y\" + barra), espejo de LineProgressQuestion (MagicSurvey).\n // `undefined` en el init deja decidir a la plataforma (style.showProgressBar).\n let progressEnabled = options?.showProgressBar === true;\n let progressShowUnit = true;\n let progressUnit: 'percentage' | 'fraction' = 'fraction';\n\n const progressEl = document.createElement('div');\n progressEl.id = 'dd-progress';\n progressEl.className = 'deepdots-progress';\n // Sin padding horizontal propio: se alinea con el título y con el contenido. El vertical es\n // el mismo arriba y abajo, y del mismo valor que el padding de la tarjeta.\n progressEl.style.cssText = 'display:none; flex-direction:column; gap:8px; width:100%; flex:0 0 auto; padding:16px 0; box-sizing:border-box;';\n const progressHead = document.createElement('div');\n progressHead.className = 'deepdots-progress-head';\n progressHead.style.cssText = 'display:flex; flex-direction:row; justify-content:space-between; align-items:center; width:100%; gap:8px;';\n const progressLabel = document.createElement('span');\n progressLabel.id = 'dd-progress-label';\n progressLabel.style.cssText = 'font-size:13px; line-height:1.2;';\n // \"Question 1\" en negrita y \"of 3\" en regular gris, como el mockup.\n const progressCurrent = document.createElement('span');\n progressCurrent.id = 'dd-progress-current';\n progressCurrent.style.cssText = `font-weight:700; color:${theme.textPrimary};`;\n const progressTotal = document.createElement('span');\n progressTotal.id = 'dd-progress-total';\n progressTotal.style.cssText = `font-weight:400; color:${theme.textMuted};`;\n progressLabel.appendChild(progressCurrent);\n progressLabel.appendChild(progressTotal);\n const progressFollowUp = document.createElement('span');\n progressFollowUp.id = 'dd-progress-followup';\n progressFollowUp.textContent = 'Follow-up';\n progressFollowUp.style.cssText = 'display:none; font-size:12px; font-weight:600; color:#fff; background:rgba(59,130,246,0.44); border-radius:999px; padding:2px 10px;';\n progressHead.appendChild(progressLabel);\n progressHead.appendChild(progressFollowUp);\n const progressTrack = document.createElement('div');\n progressTrack.className = 'deepdots-progress-track';\n progressTrack.style.cssText = `width:100%; height:4px; border-radius:999px; background:${theme.progressTrack}; overflow:hidden;`;\n const progressBar = document.createElement('div');\n progressBar.id = 'dd-progress-bar';\n progressBar.style.cssText = 'height:100%; width:0%; background:#22C55E; border-radius:999px; transition:width 450ms ease;';\n progressTrack.appendChild(progressBar);\n progressEl.appendChild(progressHead);\n progressEl.appendChild(progressTrack);\n\n function updateProgress(p: { progress?: number; total?: number; completed?: boolean; followup?: boolean }) {\n const total = typeof p.total === 'number' ? p.total : 0;\n const progress = typeof p.progress === 'number' ? p.progress : 0;\n if (!progressEnabled || onStartPage || p.completed || total <= 1) {\n progressEl.style.display = 'none';\n // Sin barra, el hueco cabecera→pregunta lo pone el contenido.\n containerContent.style.paddingTop = '16px';\n return;\n }\n progressEl.style.display = 'flex';\n // Con barra, el hueco inferior ya lo pone el bloque de progreso: no duplicarlo.\n containerContent.style.paddingTop = '0';\n // La barra usa el valor real (las follow-up suman +0.5 y avanzan media casilla); la\n // etiqueta redondea hacia abajo, porque una follow-up es un paso DENTRO de la misma\n // pregunta y no la siguiente.\n const current = Math.min(total, Math.max(1, progress + 1));\n const pct = Math.min(100, Math.max(0, (current / total) * 100));\n progressBar.style.width = `${pct}%`;\n const label = Math.min(total, Math.max(1, Math.floor(progress) + 1));\n progressLabel.style.display = progressShowUnit ? 'block' : 'none';\n if (progressUnit === 'percentage') {\n progressCurrent.textContent = `${Math.round(pct)}%`;\n progressTotal.textContent = '';\n } else {\n progressCurrent.textContent = `Question ${label}`;\n progressTotal.textContent = ` of ${total}`;\n }\n progressFollowUp.style.display = p.followup ? 'inline-block' : 'none';\n }\n\n ensureMagicFeedbackStyles(popup);\n ensureSpinnerStyles(popup);\n ensureResponsiveStyles(popup);\n // El último en entrar: el CSS del host gana sobre todo lo anterior.\n ensureCustomCss(options?.surveyCss);\n\n const containerContent = document.createElement('div');\n containerContent.id = 'dd-content';\n // Estaba escrito 'conetent'; el HTML del WebView siempre usó la forma correcta. Con\n // `surveyCss` esta clase pasa a ser superficie pública, así que las dos rutas deben coincidir.\n containerContent.className = 'deepdots-popup-container-content';\n // El popup es una columna: solo el bloque de preguntas hace scroll, para que el footer con\n // las acciones quede siempre pegado al borde inferior (antes vivía dentro del área\n // scrollable y en un survey largo había que bajar hasta el final para ver \"Send\").\n // Sin sangrado extra: el contenido se alinea con el título y con la barra de progreso\n // (antes sumaba 20px a los lados sobre el padding de la tarjeta). `overflow:hidden` deja\n // el scroll en `main`, no en el contenedor.\n containerContent.style.cssText = 'display:flex; flex-direction:column; flex:1 1 auto; min-height:0; padding:16px 0 0 0; overflow:hidden;'\n\n // Sección principal (main) - Contenedor formulario + spinner\n const main = document.createElement('div');\n main.id = 'dd-main';\n main.className = 'deepdots-popup-main';\n main.style.cssText = 'display:flex; flex-direction:column; width:100%; flex:1 1 auto; min-height:0; overflow-y:auto;';\n\n const formWrapper = document.createElement('div');\n formWrapper.id = 'dd-form-wrapper';\n formWrapper.style.cssText = 'width:100%; flex: 1 1 auto;';\n\n // Contenedor de aviso de error de validación\n const errorHint = document.createElement('div');\n errorHint.id = 'dd-error';\n errorHint.className = 'deepdots-error-hint';\n errorHint.style.cssText = `\n display: none;\n margin: 12px 0 0 0;\n padding: 10px 12px;\n border-radius: 6px;\n background: #FEF3C7; /* amber-100 */\n color: #92400E; /* amber-700 */\n border: 1px solid #FCD34D; /* amber-300 */\n font-size: 13px;\n `;\n errorHint.setAttribute('role', 'alert');\n errorHint.setAttribute('aria-live', 'polite');\n\n const spinnerEl = document.createElement('div');\n spinnerEl.className = 'mf-spinner';\n spinnerEl.setAttribute('role', 'status');\n spinnerEl.setAttribute('aria-label', 'Loading survey');\n spinnerEl.innerHTML = '<div class=\"mf-spinner-circle\"></div>';\n spinnerEl.style.cssText = 'position:absolute; top:50%; left:50%; transform:translate(-50%,-50%);';\n\n const formDivId = `magicfeedback-form-${surveyId}`;\n const formHost = document.createElement('div');\n formHost.id = formDivId;\n formHost.style.cssText = 'width:100%; visibility:hidden;';\n\n formWrapper.appendChild(spinnerEl);\n formWrapper.appendChild(formHost);\n main.appendChild(formWrapper);\n // Insertar el hint justo antes del footer\n main.appendChild(errorHint);\n\n // Sección footer (acciones) - botones en extremos\n const footer = document.createElement('div');\n footer.id = 'dd-footer';\n footer.className = 'deepdots-popup-footer';\n footer.setAttribute('data-actions-wrapper', 'true');\n footer.style.cssText = 'display:flex; flex-direction: row-reverse ;justify-content:space-between; align-items:center; gap:4px; margin-top:auto; width:100%; padding-top:12px;';\n\n // Botones\n const backButton = document.createElement('button');\n backButton.id = 'dd-back';\n backButton.className = 'dd-nav-btn';\n backButton.textContent = actions?.back ? actions.back.label : 'Back';\n // Secundario como botón de texto: sin borde, fondo ni sombra, para que el primario sea\n // la única CTA con peso visual.\n backButton.style.cssText = `\n background: transparent;\n color: ${theme.textMuted};\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 15px;\n font-weight: 600;\n transition: filter .15s ease;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n `;\n backButton.onmouseenter = () => {\n backButton.style.filter = 'brightness(0.9)';\n }\n backButton.onmouseleave = () => {\n backButton.style.filter = 'brightness(1)';\n }\n backButton.onclick = () => {\n emit('popup_clicked', surveyId, {action: 'back'});\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (formInstance as any)?.back?.();\n };\n\n // Boton start survyes, solo aprece cuando la encuesta empieza con mensaje de inicio\n // Width de 100% para que ocupe todo el espacio disponible\n const startButton = document.createElement('button');\n startButton.id = 'dd-start';\n startButton.className = 'dd-nav-btn';\n startButton.textContent = actions?.start ? actions.start.label : 'Start survey';\n startButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 15px;\n font-weight: 600;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n transition: filter .15s ease;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n `;\n startButton.onclick = () => {\n emit('popup_clicked', surveyId, {action: 'start_survey'});\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (formInstance as any)?.startForm?.();\n onStartPage = false;\n updateProgress({progress: formInstance?.progress, total: formInstance?.total});\n };\n\n // Botón cerrar popup, solo aparece al terminar la encuesta\n // Width de 100% para que ocupe todo el espacio disponible\n const closeButton = document.createElement('button');\n closeButton.id = 'dd-complete';\n closeButton.className = 'dd-nav-btn';\n closeButton.textContent = actions?.complete ? actions.complete.label : 'Complete survey';\n closeButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 15px;\n font-weight: 600;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n transition: filter .15s ease;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n `;\n closeButton.onmouseenter = () => {\n closeButton.style.filter = 'brightness(0.9)';\n }\n closeButton.onmouseleave = () => {\n closeButton.style.filter = 'brightness(1)';\n }\n closeButton.onclick = () => {\n emit('popup_clicked', surveyId, {action: 'complete'});\n onClose();\n };\n\n // Botón send, si es primera pagina ocupara el espacio completo pero si no estara al lado derecho\n const submitButton = document.createElement('button');\n submitButton.id = 'dd-submit';\n submitButton.className = 'dd-nav-btn';\n submitButton.textContent = actions?.accept ? actions.accept.label : 'Send';\n submitButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 6px;\n cursor: pointer;\n font-size: 15px;\n font-weight: 600;\n box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n transition: filter .15s ease;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n text-align: center;\n `;\n submitButton.onclick = () => {\n if (!surveyCompletedEmitted) {\n emit('popup_clicked', surveyId, {action: 'manual_send'});\n // Dispara envío nativo si existe\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (formInstance as any)?.send?.();\n }\n };\n\n backButton.style.display = 'none';\n startButton.style.display = 'none';\n submitButton.style.display = 'none';\n closeButton.style.display = 'none';\n\n // Insertar botones dentro del footer en orden visual (row-reverse deja primario a la derecha)\n footer.appendChild(submitButton);\n footer.appendChild(backButton);\n footer.appendChild(closeButton);\n footer.appendChild(startButton);\n\n // Añadir footer al main y main al containerContent\n containerContent.appendChild(main);\n // El footer va fuera del área scrollable, como hermano de `main`.\n footer.style.flex = '0 0 auto';\n containerContent.appendChild(footer);\n\n // Helper para controlar visibilidad de botones según estado\n type ViewState = 'loading' | 'start' | 'in_progress_first' | 'in_progress_next' | 'completed' | 'error';\n\n function updateButtons(state: ViewState) {\n // Por defecto, ocultar todos\n backButton.style.display = 'none';\n startButton.style.display = 'none';\n submitButton.style.display = 'none';\n closeButton.style.display = 'none';\n // Reset widths por estado\n backButton.style.width = '';\n startButton.style.width = '';\n submitButton.style.width = '';\n closeButton.style.width = '';\n\n switch (state) {\n case 'loading':\n // Footer se ocultará desde setLoading\n break;\n case 'start':\n // Solo botón Start a ancho completo\n startButton.style.display = 'inline-flex';\n startButton.style.width = '100%';\n break;\n case 'in_progress_first':\n // Solo botón Send (lado derecho), ancho auto\n submitButton.style.display = 'inline-flex';\n submitButton.style.width = '';\n setLoading(false);\n break;\n case 'in_progress_next':\n // Mostrar Back (izquierda) + Send (derecha)\n backButton.style.display = 'inline-flex';\n submitButton.style.display = 'inline-flex';\n setLoading(false);\n break;\n case 'completed':\n // Mostrar Close/Complete a ancho completo como acción principal\n closeButton.style.display = 'inline-flex';\n closeButton.style.width = '100%';\n setLoading(false);\n break;\n case 'error':\n // En error, permitir cerrar (ancho auto)\n // closeButton.style.display = 'inline-flex';\n setLoading(false);\n break;\n }\n }\n\n /** Estado de navegación según la profundidad recorrida (no según `total`). */\n function updateNavButtons() {\n updateButtons(pageDepth > 0 ? 'in_progress_next' : 'in_progress_first');\n }\n\n /**\n * Pantalla final con el `successMessage` configurado en la plataforma. La pinta el popup y\n * no `@magicfeedback/native`, cuyo `renderSuccess` usa `textContent` (el mensaje es HTML del\n * editor: imagen + texto) y cuyo fallback es un literal que ignora el estilo del survey.\n * Va por innerHTML igual que `renderStartMessage` con el mensaje de inicio.\n */\n let successMessageHtml = '';\n function showSuccessScreen() {\n progressEl.style.display = 'none';\n formWrapper.style.display = 'none';\n let done = main.querySelector('.deepdots-success') as HTMLElement | null;\n if (!done) {\n done = document.createElement('div');\n done.className = 'deepdots-success';\n done.style.cssText = `width:100%; text-align:center; padding:24px 0; color:${theme.textPrimary};`;\n main.insertBefore(done, errorHint);\n }\n done.innerHTML = successMessageHtml || '<p>Thank you for your feedback!</p>';\n done.querySelectorAll('img').forEach((img) => {\n img.style.cssText = 'max-width:100%; height:auto; margin:0 auto 16px auto; display:block;';\n });\n }\n\n // Ensamblar popup\n popup.appendChild(header);\n popup.appendChild(progressEl);\n popup.appendChild(containerContent);\n\n container.innerHTML = '';\n container.appendChild(popup);\n container.style.display = 'flex';\n container.style.justifyContent = pos.justifyContent;\n container.style.alignItems = pos.alignItems;\n container.style.background = pos.background;\n if (pos.padding) container.style.padding = pos.padding;\n\n // Gestión dinámica de loading\n function setLoading(isLoading: boolean) {\n spinnerEl.style.display = isLoading ? 'flex' : 'none';\n if (!isLoading) {\n formHost.style.visibility = 'visible';\n }\n // Ocultar totalmente los botones cuando está cargando\n footer.style.display = isLoading ? 'none' : 'flex';\n // Ajustar estados de los botones por si se muestran\n backButton.disabled = isLoading;\n startButton.disabled = isLoading;\n closeButton.disabled = isLoading;\n submitButton.disabled = isLoading;\n submitButton.style.opacity = isLoading ? '0.6' : '1';\n submitButton.style.cursor = isLoading ? 'not-allowed' : 'pointer';\n // No sobrescribir el estado de botones al finalizar la carga.\n if (isLoading) {\n updateButtons('loading');\n }\n }\n\n // Estado inicial\n setLoading(true);\n\n // Entorno navegador\n if (typeof window === 'undefined' || typeof document === 'undefined') {\n return;\n }\n\n // Referencia instancia formulario\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let formInstance: any = null;\n\n try {\n if (!magicfeedback || typeof magicfeedback.form !== 'function') {\n sdkWarn('[MagicFeedback] form() no disponible. Fallback manual.');\n setLoading(false);\n return;\n }\n magicfeedback.init({\n debug: true,\n env: env === 'production' ? 'prod' : 'dev'}\n );\n // Contrato Fase 1 §5: profile = identidad del usuario; metadata = contexto de tracking (session_id + user_id)\n const { profile, metadata } = buildSurveyIdentity(userId ?? null, sessionId ?? null, miniService ?? null, analyticsFeedbackSessionId ?? null);\n formInstance = magicfeedback.form(surveyId, productId, profile, metadata);\n\n interface TypedGenerateOptions {\n addButton: boolean;\n getMetaData: boolean;\n addSuccessScreen: boolean;\n onLoadedEvent?: (args: {\n formData: FormData,\n progress?: number, total?: number\n }) => void;\n beforeSubmitEvent?: () => void;\n afterSubmitEvent?: (args: { error?: string, completed: boolean, progress: number, total: number, followup?: boolean }) => void;\n onBackEvent?: (args: { error?: string, progress: number, total: number, followup: boolean }) => void;\n }\n\n const generateOptions: TypedGenerateOptions = {\n addButton: false,\n getMetaData: true,\n // La pantalla final la pinta el popup: `renderSuccess` de @magicfeedback/native usa\n // textContent, así que el mensaje de la plataforma (HTML con imagen) no se vería, y\n // su fallback es un literal genérico que ignora `style.successMessage`.\n addSuccessScreen: false,\n };\n generateOptions.onLoadedEvent = ({formData}) => {\n // Calcular altura disponible y aplicarla al main (restando header + footer + paddings)\n try {\n /*\n const headerHeight = header.getBoundingClientRect().height;\n const footerHeight = footer.getBoundingClientRect().height;\n const paddingY = 48; // 24px top + 24px bottom\n const viewportLimit = window.innerHeight * 0.8; // coincide con max-height del popup\n const available = viewportLimit - headerHeight - footerHeight - paddingY;\n if (available > 120) { // asegurar un mínimo razonable\n main.style.maxHeight = available + 'px';\n } */\n } catch {\n // silencioso\n }\n // Personalización del popup basada en formData.style\n const s = formData?.style;\n if (s && !stylesInjected) {\n stylesInjected = true;\n if (s.successMessage) successMessageHtml = s.successMessage;\n // La barra de progreso la decide el host (init) y, si no se pronuncia, la plataforma.\n if (options?.showProgressBar === undefined) progressEnabled = s.showProgressBar === true;\n if (s.showProgressUnit !== undefined) progressShowUnit = s.showProgressUnit !== false;\n if (s.progressUnit) progressUnit = s.progressUnit;\n if (s.loadingBarColor) {\n progressBar.style.background = s.loadingBarColor;\n progressFollowUp.style.background = s.loadingBarColor;\n }\n // Fondo del contenedor popup\n if (s.boxBackgroundColor) {\n popup.style.background = s.boxBackgroundColor;\n }\n // Alineación del contenido principal\n if (s.contentAlign) {\n // 'top' => start, 'center' => center\n main.style.justifyContent = s.contentAlign === 'center' ? 'center' : 'flex-start';\n }\n // Botón primario (submit, start)\n if (s.buttonPrimaryColor) {\n submitButton.style.background = s.buttonPrimaryColor;\n submitButton.style.border = 'none';\n submitButton.style.color = '#fff';\n\n startButton.style.background = s.buttonPrimaryColor;\n startButton.style.border = 'none';\n startButton.style.color = '#fff';\n\n closeButton.style.background = s.buttonPrimaryColor;\n closeButton.style.border = 'none';\n closeButton.style.color = '#fff';\n }\n // Botón secundario (back) — outlined: fondo blanco, letra y borde del color secundario\n if (s.buttonSecondaryColor) {\n // Botón de texto: solo tiñe la etiqueta, sin recuperar fondo ni borde.\n backButton.style.background = 'transparent';\n backButton.style.color = s.buttonSecondaryColor;\n backButton.style.border = 'none';\n }\n if (s.logo) {\n if (!document.getElementById('deepdots-popup-logo')) {\n // Insertar logo si existe\n const logoImg = document.createElement('img');\n logoImg.id = 'deepdots-popup-logo';\n logoImg.src = s.logo;\n logoImg.alt = 'Logo';\n logoImg.style.cssText = 'max-height:40px; max-width:100%; object-fit:contain;';\n if (s.logoSize) {\n switch (s.logoSize) {\n case 'small':\n logoImg.style.maxHeight = '30px';\n break;\n case 'medium':\n logoImg.style.maxHeight = '50px';\n break;\n case 'large':\n logoImg.style.maxHeight = '70px';\n break;\n }\n }\n if (s.logoPosition) {\n switch (s.logoPosition) {\n case 'left':\n logoImg.style.margin = '0 16px 42px 0';\n logoImg.style.display = 'block';\n logoImg.style.marginLeft = '0';\n break;\n case 'right':\n logoImg.style.margin = '0 0 42px 16px';\n logoImg.style.display = 'block';\n logoImg.style.marginLeft = 'auto';\n break;\n case 'center':\n logoImg.style.margin = '0 auto 42px auto';\n logoImg.style.display = 'block';\n break;\n }\n }\n // Insertar antes del main si no existe ya\n containerContent.insertBefore(logoImg, main);\n }\n }\n\n if (s.startMessage && s.startMessage !== '') {\n // Si hay mensaje de inicio, mostrar botón Start inicialmente\n sdkLog(s.startMessage);\n onStartPage = true;\n updateButtons('start');\n } else {\n // Si no hay mensaje de inicio, mostrar estado de primera página (solo Send)\n updateButtons('in_progress_first');\n }\n } else {\n // Sin estilos, asumir primera página normal\n updateButtons('in_progress_first');\n }\n\n // El total solo se conoce con el form ya montado.\n updateProgress({progress: formInstance.progress, total: formInstance.total});\n emit('popup_clicked', surveyId, {action: 'loaded'});\n setLoading(false); // hace visible el formulario y oculta el spinner\n };\n generateOptions.beforeSubmitEvent = () => {\n setLoading(true);\n emit('popup_clicked', surveyId, {action: 'before_submit'});\n };\n generateOptions.afterSubmitEvent = ({error, completed, total, progress, followup}) => {\n // No cambiar estado de loading aquí; lo gestiona cada transición\n // Normalizar el error a texto seguro\n const errText = error ? (typeof error === 'string' ? error : ((error as unknown as {message?: string}).message ?? String(error))) : '';\n if (errText) {\n // Desactivar loading para que se vean los botones\n setLoading(false);\n // Caso específico: error de pregunta obligatoria\n if (errText.toLowerCase().includes('no response')) {\n errorHint.textContent = 'Please answer the required question to continue.';\n errorHint.style.display = 'block';\n emit('popup_clicked', surveyId, {action: 'validation_error_required'});\n // La página no ha cambiado: el estado de navegación se queda como estaba.\n updateNavButtons();\n return;\n }\n // Otros errores: mostrar mensaje genérico y permitir cerrar\n errorHint.textContent = 'An error occurred while submitting. Please try again or close the popup.';\n errorHint.style.display = 'block';\n emit('popup_clicked', surveyId, {action: 'submit_error', error: errText});\n // updateButtons('error');\n return;\n }\n // Limpiar hint si no hay error\n errorHint.style.display = 'none';\n setLoading(false);\n if (completed) {\n emit('survey_completed', surveyId, {action: 'completed'});\n surveyCompletedEmitted = true;\n showSuccessScreen();\n updateButtons('completed');\n updateProgress({progress, total, completed: true});\n return;\n }\n emit('popup_clicked', surveyId, {action: 'partial'});\n pageDepth++;\n updateNavButtons();\n updateProgress({progress, total, followup});\n };\n generateOptions.onBackEvent = ({progress, error, followup}) => {\n emit('popup_clicked', surveyId, {action: 'back'});\n // Con error (\"No page found\") no hubo navegación: la profundidad no se toca.\n if (!error) pageDepth = Math.max(0, pageDepth - 1);\n updateNavButtons();\n // onBackEvent no trae `total`: se lee del form, que es su dueño.\n updateProgress({progress, total: formInstance.total, followup});\n };\n\n // Ejecutar generación con opciones tipadas\n formInstance.generate(formDivId, generateOptions)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n .catch((err: any) => {\n sdkError('[MagicFeedback] Error generating form:', err);\n setLoading(false);\n });\n } catch (e) {\n sdkError('[MagicFeedback] Exception initializing form:', e);\n setLoading(false);\n }\n}\n"],"mappings":"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","names":[]}