@magicfeedback/popup-sdk 1.15.0 → 1.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-C7VD3HY4.mjs → chunk-AFRHPS3K.mjs} +25 -13
- package/dist/chunk-AFRHPS3K.mjs.map +1 -0
- package/dist/{chunk-GUJSOVS6.mjs → chunk-VHTN5UXD.mjs} +25 -13
- package/dist/chunk-VHTN5UXD.mjs.map +1 -0
- package/dist/index.d.mts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +111 -22
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -8
- package/dist/index.mjs.map +1 -1
- package/dist/react-native.js +111 -22
- package/dist/react-native.js.map +1 -1
- package/dist/react-native.mjs +45 -8
- package/dist/react-native.mjs.map +1 -1
- package/dist/{renderPopup-RWSU5Y2H.mjs → renderPopup-S3RU3Y6J.mjs} +47 -7
- package/dist/renderPopup-S3RU3Y6J.mjs.map +1 -0
- package/dist/{renderPopup-CHWH3KHR.mjs → renderPopup-YCAQ3ODC.mjs} +47 -7
- package/dist/renderPopup-YCAQ3ODC.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-C7VD3HY4.mjs.map +0 -1
- package/dist/chunk-GUJSOVS6.mjs.map +0 -1
- package/dist/renderPopup-CHWH3KHR.mjs.map +0 -1
- package/dist/renderPopup-RWSU5Y2H.mjs.map +0 -1
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
sdkWarn,
|
|
14
14
|
setSurveyBusy,
|
|
15
15
|
style_default
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-VHTN5UXD.mjs";
|
|
17
17
|
|
|
18
18
|
// src/ui/logo.ts
|
|
19
19
|
var BASE_CSS = "max-height:40px; max-width:100%; object-fit:contain; display:block; margin:12px 0 0 0;";
|
|
@@ -251,10 +251,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
251
251
|
closeBtn.style.color = theme.textPrimary;
|
|
252
252
|
closeBtn.style.transform = "scale(1)";
|
|
253
253
|
};
|
|
254
|
-
|
|
254
|
+
const closeFromChrome = () => {
|
|
255
255
|
emit("popup_clicked", surveyId, { action: "close_icon" });
|
|
256
256
|
onClose();
|
|
257
257
|
};
|
|
258
|
+
closeBtn.onclick = closeFromChrome;
|
|
258
259
|
closeBtn.style.flex = "0 0 auto";
|
|
259
260
|
closeBtn.style.marginLeft = "auto";
|
|
260
261
|
header.appendChild(closeBtn);
|
|
@@ -362,6 +363,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
362
363
|
formWrapper.appendChild(spinnerEl);
|
|
363
364
|
formWrapper.appendChild(formHost);
|
|
364
365
|
main.appendChild(formWrapper);
|
|
366
|
+
const loadErrorEl = document.createElement("div");
|
|
367
|
+
loadErrorEl.id = "dd-load-error";
|
|
368
|
+
loadErrorEl.className = "deepdots-load-error";
|
|
369
|
+
loadErrorEl.setAttribute("role", "alert");
|
|
370
|
+
loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;
|
|
371
|
+
main.appendChild(loadErrorEl);
|
|
365
372
|
main.appendChild(errorHint);
|
|
366
373
|
const footer = document.createElement("div");
|
|
367
374
|
footer.id = "dd-footer";
|
|
@@ -477,6 +484,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
477
484
|
justify-content: center;
|
|
478
485
|
text-align: center;
|
|
479
486
|
`;
|
|
487
|
+
const cancelButton = document.createElement("button");
|
|
488
|
+
cancelButton.id = "dd-cancel";
|
|
489
|
+
cancelButton.type = "button";
|
|
490
|
+
cancelButton.className = "dd-nav-btn";
|
|
491
|
+
cancelButton.textContent = actionLabels.decline;
|
|
492
|
+
cancelButton.style.cssText = submitButton.style.cssText;
|
|
493
|
+
cancelButton.onclick = closeFromChrome;
|
|
480
494
|
submitButton.onclick = () => {
|
|
481
495
|
if (!surveyCompletedEmitted) {
|
|
482
496
|
emit("popup_clicked", surveyId, { action: "manual_send" });
|
|
@@ -490,6 +504,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
490
504
|
startButton.textContent = actionLabels.start;
|
|
491
505
|
closeButton.textContent = actionLabels.complete;
|
|
492
506
|
submitButton.textContent = actionLabels.accept;
|
|
507
|
+
cancelButton.textContent = actionLabels.decline;
|
|
508
|
+
loadErrorEl.textContent = labels.errorLoad;
|
|
493
509
|
closeBtn.setAttribute("aria-label", labels.closeAria);
|
|
494
510
|
spinnerEl.setAttribute("aria-label", labels.loadingAria);
|
|
495
511
|
progressFollowUp.textContent = labels.followUp;
|
|
@@ -500,10 +516,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
500
516
|
startButton.style.display = "none";
|
|
501
517
|
submitButton.style.display = "none";
|
|
502
518
|
closeButton.style.display = "none";
|
|
519
|
+
cancelButton.style.display = "none";
|
|
503
520
|
footer.appendChild(submitButton);
|
|
504
521
|
footer.appendChild(backButton);
|
|
505
522
|
footer.appendChild(closeButton);
|
|
506
523
|
footer.appendChild(startButton);
|
|
524
|
+
footer.appendChild(cancelButton);
|
|
507
525
|
containerContent.appendChild(main);
|
|
508
526
|
footer.style.flex = "0 0 auto";
|
|
509
527
|
containerContent.appendChild(footer);
|
|
@@ -512,6 +530,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
512
530
|
startButton.style.display = "none";
|
|
513
531
|
submitButton.style.display = "none";
|
|
514
532
|
closeButton.style.display = "none";
|
|
533
|
+
cancelButton.style.display = "none";
|
|
515
534
|
backButton.style.width = "";
|
|
516
535
|
startButton.style.width = "";
|
|
517
536
|
submitButton.style.width = "";
|
|
@@ -541,6 +560,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
541
560
|
case "error":
|
|
542
561
|
setLoading(false);
|
|
543
562
|
break;
|
|
563
|
+
case "load_error":
|
|
564
|
+
cancelButton.style.display = "inline-flex";
|
|
565
|
+
break;
|
|
544
566
|
}
|
|
545
567
|
}
|
|
546
568
|
function updateNavButtons() {
|
|
@@ -562,6 +584,20 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
562
584
|
img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
|
|
563
585
|
});
|
|
564
586
|
}
|
|
587
|
+
let loadFailed = false;
|
|
588
|
+
function showLoadError(reason) {
|
|
589
|
+
sdkWarn("[Deepdots] survey failed to load:", reason);
|
|
590
|
+
if (loadFailed) return;
|
|
591
|
+
loadFailed = true;
|
|
592
|
+
progressEl.style.display = "none";
|
|
593
|
+
formWrapper.style.display = "none";
|
|
594
|
+
errorHint.style.display = "none";
|
|
595
|
+
loadErrorEl.textContent = labels.errorLoad;
|
|
596
|
+
loadErrorEl.style.display = "block";
|
|
597
|
+
setLoading(false);
|
|
598
|
+
updateButtons("load_error");
|
|
599
|
+
reveal();
|
|
600
|
+
}
|
|
565
601
|
popup.appendChild(header);
|
|
566
602
|
popup.appendChild(progressEl);
|
|
567
603
|
popup.appendChild(containerContent);
|
|
@@ -626,7 +662,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
626
662
|
try {
|
|
627
663
|
if (!magicfeedback || typeof magicfeedback.form !== "function") {
|
|
628
664
|
sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
|
|
629
|
-
|
|
665
|
+
showLoadError("form() not available");
|
|
630
666
|
return;
|
|
631
667
|
}
|
|
632
668
|
magicfeedback.init(
|
|
@@ -652,7 +688,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
652
688
|
// quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
|
|
653
689
|
autofocus: "navigation"
|
|
654
690
|
};
|
|
655
|
-
generateOptions.onLoadedEvent = ({ formData, lang }) => {
|
|
691
|
+
generateOptions.onLoadedEvent = ({ formData, lang, error }) => {
|
|
692
|
+
if (error) {
|
|
693
|
+
showLoadError(error);
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
656
696
|
const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
|
|
657
697
|
const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
|
|
658
698
|
if (surveyLang) applyLabels(surveyLang);
|
|
@@ -751,11 +791,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
751
791
|
};
|
|
752
792
|
formInstance.generate(formDivId, generateOptions).catch((err) => {
|
|
753
793
|
sdkError("[MagicFeedback] Error generating form:", err);
|
|
754
|
-
|
|
794
|
+
showLoadError(err);
|
|
755
795
|
});
|
|
756
796
|
} catch (e) {
|
|
757
797
|
sdkError("[MagicFeedback] Exception initializing form:", e);
|
|
758
|
-
|
|
798
|
+
showLoadError(e);
|
|
759
799
|
}
|
|
760
800
|
}
|
|
761
801
|
export {
|
|
@@ -763,4 +803,4 @@ export {
|
|
|
763
803
|
SURVEY_ALIGNMENT_STYLE_ID,
|
|
764
804
|
renderPopup
|
|
765
805
|
};
|
|
766
|
-
//# sourceMappingURL=renderPopup-
|
|
806
|
+
//# sourceMappingURL=renderPopup-S3RU3Y6J.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ui/logo.ts","../src/ui/renderPopup.ts"],"sourcesContent":["/**\n * Logo del popup (viene en `formData.style` del survey, no del `init`).\n *\n * Vive encima de la barra de progreso, como hermano del header: la marca abre la tarjeta y el\n * progreso queda pegado a la pregunta. Antes se insertaba DENTRO del contenido (web) o del área\n * scrollable (WebView de RN), así que la barra quedaba por encima del logo y en RN el logo se\n * iba con el scroll.\n *\n * El hueco inferior lo pone el bloque siguiente (`.deepdots-progress` cuando se ve, o el\n * `padding-top` del contenido cuando no), así que aquí solo se separa del header.\n */\nexport interface PopupLogoStyle {\n logo?: string;\n logoSize?: string;\n logoPosition?: string;\n}\n\nconst BASE_CSS = 'max-height:40px; max-width:100%; object-fit:contain; display:block; margin:12px 0 0 0;';\n\nconst MAX_HEIGHT: Record<string, string> = {\n small: '30px',\n medium: '50px',\n large: '70px',\n};\n\n/**\n * Inserta el logo en `parent`, justo antes de `anchor` (la barra de progreso).\n * No-op si el estilo no trae logo o si ya existe uno con ese `id`.\n */\nexport function insertPopupLogo(\n parent: HTMLElement,\n anchor: HTMLElement | null,\n style: PopupLogoStyle | undefined,\n id: string,\n): HTMLImageElement | null {\n if (!style?.logo) return null;\n if (parent.ownerDocument.getElementById(id)) return null;\n\n const img = parent.ownerDocument.createElement('img');\n img.id = id;\n img.src = style.logo;\n img.alt = 'Logo';\n img.style.cssText = BASE_CSS;\n\n if (style.logoSize && MAX_HEIGHT[style.logoSize]) {\n img.style.maxHeight = MAX_HEIGHT[style.logoSize];\n }\n // Márgenes lógicos (inline-start/end) en vez de left/right: con un survey RTL (árabe) el\n // popup se pinta con dir=\"rtl\" y el logo tiene que acompañar al resto del chrome. En LTR\n // el resultado es idéntico al de antes.\n switch (style.logoPosition) {\n case 'left':\n img.style.margin = '12px 0 0 0';\n img.style.setProperty('margin-inline-start', '0');\n img.style.setProperty('margin-inline-end', '16px');\n break;\n case 'right':\n img.style.margin = '12px 0 0 0';\n img.style.setProperty('margin-inline-start', 'auto');\n img.style.setProperty('margin-inline-end', '0');\n break;\n case 'center':\n img.style.margin = '12px auto 0 auto';\n break;\n }\n\n parent.insertBefore(img, anchor);\n return img;\n}\n","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 { insertPopupLogo } from './logo';\nimport { applySurveyPrimaryColor } from './surveyPalette';\nimport { setSurveyBusy } from './busy';\nimport { sdkLog, sdkWarn, sdkError } from '../util/logger';\nimport { directionFor, getLabels, resolveActionLabels } from '../i18n/labels';\nimport { REVEAL_TIMEOUT_MS, revealWhenPainted as revealWhenImagesLoaded } from './reveal';\nimport magicfeedback from \"@magicfeedback/native\";\nimport magicfeedbackCss from '../assets/style.css';\n\n// Reexportado desde aquí porque este era su sitio original: la constante se mudó a `reveal.ts`\n// para que la ruta del WebView pueda leerla sin arrastrar `@magicfeedback/native`.\nexport { REVEAL_TIMEOUT_MS } from './reveal';\n\n/**\n * `<style>` del SDK en el `<head>`, identificado por `id` e inyectado una sola vez. Con `nonce`\n * (la CSP del host, ver `DeepdotsInitParams.cspNonce`) lo lleva puesto desde que se inserta:\n * el navegador solo comprueba el nonce al insertar, así que una hoja que ya estaba sin él (o con\n * otro) se recrea en vez de parchearse.\n */\nfunction ensureStyle(id: string, css: string, nonce?: string): HTMLStyleElement {\n let el = document.getElementById(id) as HTMLStyleElement | null;\n if (el && nonce && el.getAttribute('nonce') !== nonce) {\n el.remove();\n el = null;\n }\n if (!el) {\n el = document.createElement('style');\n el.id = id;\n if (nonce) el.setAttribute('nonce', nonce);\n el.textContent = css;\n document.head.appendChild(el);\n } else if (el.textContent !== css) {\n el.textContent = css;\n }\n return el;\n}\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(nonce?: string) {\n ensureStyle('magicfeedback-sdk-styles', magicfeedbackCss, nonce);\n}\n\n/**\n * Id del bloque que alinea el contenido del survey con el chrome del popup. Exportado para que\n * los tests puedan localizarlo sin depender de la posición en el `<head>`.\n */\nexport const SURVEY_ALIGNMENT_STYLE_ID = 'deepdots-survey-alignment';\n\n/**\n * El margen lateral lo pone la tarjeta del popup, pero el CSS de `@magicfeedback/native` añade el\n * suyo en tres capas: el contenedor, el formulario (hasta 28px) y el bloque de cada pregunta\n * (12px, con fondo igual al de la tarjeta, así que ni se ve). Sumados, dejan el enunciado y las\n * opciones bastante más adentro que el logo, el título y la barra de progreso, que sí llegan al\n * borde. Aquí se anula solo el lateral: el vertical es el que separa las preguntas.\n *\n * Va acotado a `.deepdots-popup` porque esta hoja se inyecta en el `<head>` del host y sin acotar\n * repintaría sus propios formularios; de paso, la especificidad extra hace que gane sobre el CSS\n * del paquete sin depender del orden.\n */\nfunction ensureSurveyAlignmentStyles(nonce?: string) {\n ensureStyle(SURVEY_ALIGNMENT_STYLE_ID, `\n .deepdots-popup .magicfeedback-container,\n .deepdots-popup .magicfeedback-form,\n .deepdots-popup .magicfeedback-div { padding-left: 0; padding-right: 0; }\n `, nonce);\n}\n\n// Añade estilos de spinner si no existen\nfunction ensureSpinnerStyles(nonce?: string) {\n ensureStyle('deepdots-spinner-styles', `\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 `, nonce);\n}\n\nfunction ensureResponsiveStyles(nonce?: string) {\n ensureStyle('deepdots-responsive-styles', `\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 `, nonce);\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, nonce?: string) {\n const STYLE_ID = 'deepdots-custom-css';\n if (!css) {\n document.getElementById(STYLE_ID)?.remove();\n return;\n }\n // Reanexar mueve el nodo al final del head: garantiza que va tras las hojas del SDK.\n document.head.appendChild(ensureStyle(STYLE_ID, css, nonce));\n}\n\n// Inyecta (una sola vez) el @font-face de la fuente personalizada.\nfunction ensureFontFace(family: string, url: string, nonce?: string) {\n const css = buildFontFaceCss(family, url);\n if (!css) return;\n ensureStyle('deepdots-font-face', css, nonce);\n}\n\n/** Ratón o trackpad. Un iPad sin trackpad cuenta como táctil. */\nfunction hasFinePointer(): boolean {\n return typeof window !== 'undefined'\n && typeof window.matchMedia === 'function'\n && window.matchMedia('(pointer: fine)').matches;\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 // Nonce de la CSP del host para cada `<style>` que se inyecta (en blanco = sin nonce).\n const cspNonce = options?.cspNonce?.trim() || undefined;\n\n // Textos del chrome (botones, progreso, aria-labels, errores). Arrancan con el idioma que\n // conoce el init y se re-resuelven en `onLoadedEvent` con el idioma en el que se muestra el\n // survey (`lang` del evento; `formData.lang[0]`, el idioma por defecto, en native < 2.3),\n // que es el que manda: un survey en danés debe traer también su footer en danés.\n let labels = getLabels(options?.language);\n let actionLabels = resolveActionLabels(actions, options?.language);\n // Dirección del chrome. Desde `@magicfeedback/native` 2.2.22 el survey se voltea solo\n // (`dir=\"rtl\"` en SU contenedor) para los idiomas RTL; si el header, el footer y la barra\n // de progreso se quedaran en LTR, media tarjeta miraría a cada lado. Se marca siempre,\n // también en `ltr`, para no heredar la dirección de la página del host.\n let direction = directionFor(options?.language);\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 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.setAttribute('dir', direction);\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, cspNonce);\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:start; 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', labels.closeAria);\n closeBtn.innerHTML = `\n <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M5 5L19 19M5 19L19 5\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"butt\"/>\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;\n box-shadow: none !important;\n `;\n closeBtn.onmouseenter = () => {\n closeBtn.style.color = theme.closeBtnHoverColor;\n closeBtn.style.transform = 'scale(1.06)';\n };\n closeBtn.onmouseleave = () => {\n closeBtn.style.color = theme.textPrimary;\n closeBtn.style.transform = 'scale(1)';\n };\n // La X y el Cancel del survey que no carga cierran por el mismo camino.\n const closeFromChrome = () => {\n emit('popup_clicked', surveyId, {action: 'close_icon'});\n onClose();\n };\n closeBtn.onclick = closeFromChrome;\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 = labels.followUp;\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 = `${labels.question} ${label}`;\n progressTotal.textContent = ` ${labels.of} ${total}`;\n }\n progressFollowUp.style.display = p.followup ? 'inline-block' : 'none';\n }\n\n ensureMagicFeedbackStyles(cspNonce);\n // Después del CSS del paquete y antes del CSS del host, que sigue teniendo la última palabra.\n ensureSurveyAlignmentStyles(cspNonce);\n ensureSpinnerStyles(cspNonce);\n ensureResponsiveStyles(cspNonce);\n // El último en entrar: el CSS del host gana sobre todo lo anterior.\n ensureCustomCss(options?.surveyCss, cspNonce);\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', labels.loadingAria);\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\n // Aviso de survey que no carga (clave o producto inexistentes, sin red). Ocupa el sitio del\n // survey; `textContent`, nunca innerHTML.\n const loadErrorEl = document.createElement('div');\n loadErrorEl.id = 'dd-load-error';\n loadErrorEl.className = 'deepdots-load-error';\n loadErrorEl.setAttribute('role', 'alert');\n loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;\n main.appendChild(loadErrorEl);\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 = actionLabels.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: 999px; /* píldora, como el Button de Material3 en KMP */\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 = actionLabels.start;\n startButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 999px; /* píldora, como el Button de Material3 en KMP */\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 = actionLabels.complete;\n closeButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 999px; /* píldora, como el Button de Material3 en KMP */\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 = actionLabels.accept;\n submitButton.style.cssText = `\n background: #1E293B;\n color: #fff;\n border: none;\n min-height: 44px;\n padding: 12px 24px;\n border-radius: 999px; /* píldora, como el Button de Material3 en KMP */\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 // Única acción del survey que no carga. Cierra igual que la X: no depende de que la API\n // mande `actions.decline` (solo su `label`, si viene).\n const cancelButton = document.createElement('button');\n cancelButton.id = 'dd-cancel';\n cancelButton.type = 'button';\n cancelButton.className = 'dd-nav-btn';\n cancelButton.textContent = actionLabels.decline;\n cancelButton.style.cssText = submitButton.style.cssText;\n cancelButton.onclick = closeFromChrome;\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 /**\n * Re-resuelve todos los textos del chrome para `lang`. Se llama cuando el survey ya cargó\n * y se conoce su idioma. Las etiquetas que configure la plataforma por popup\n * (`actions.*.label`) siguen ganando: `resolveActionLabels` solo rellena las que falten.\n * El contador de progreso no se toca aquí porque `updateProgress` lo repinta justo después\n * leyendo `labels`.\n */\n function applyLabels(lang: string): void {\n labels = getLabels(lang);\n actionLabels = resolveActionLabels(actions, lang);\n backButton.textContent = actionLabels.back;\n startButton.textContent = actionLabels.start;\n closeButton.textContent = actionLabels.complete;\n submitButton.textContent = actionLabels.accept;\n cancelButton.textContent = actionLabels.decline;\n loadErrorEl.textContent = labels.errorLoad;\n closeBtn.setAttribute('aria-label', labels.closeAria);\n spinnerEl.setAttribute('aria-label', labels.loadingAria);\n progressFollowUp.textContent = labels.followUp;\n // El survey ya se ha volteado a sí mismo: el chrome tiene que acompañarlo.\n direction = directionFor(lang);\n popup.setAttribute('dir', direction);\n }\n\n backButton.style.display = 'none';\n startButton.style.display = 'none';\n submitButton.style.display = 'none';\n closeButton.style.display = 'none';\n cancelButton.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 footer.appendChild(cancelButton);\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' | 'load_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 cancelButton.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 case 'load_error':\n // Survey que no ha cargado: solo se puede cerrar.\n cancelButton.style.display = 'inline-flex';\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 /**\n * Survey que no ha cargado. `@magicfeedback/native` lo avisa con `onLoadedEvent({error})`\n * y antes se trataba como una carga normal: Send sobre un survey vacío. Se enseña el aviso\n * traducido, la X sigue y el footer queda en Cancel. Se revela ya: no hay nada más que\n * esperar. No es interacción del usuario, así que no llega al host como `popup_clicked`.\n */\n let loadFailed = false;\n function showLoadError(reason: unknown) {\n sdkWarn('[Deepdots] survey failed to load:', reason);\n if (loadFailed) return;\n loadFailed = true;\n progressEl.style.display = 'none';\n formWrapper.style.display = 'none';\n errorHint.style.display = 'none';\n loadErrorEl.textContent = labels.errorLoad;\n loadErrorEl.style.display = 'block';\n setLoading(false);\n updateButtons('load_error');\n reveal();\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 // Apertura diferida: el contenedor entra en el layout (el survey necesita medidas reales\n // para pintarse) pero invisible y sin capturar clics, hasta que `reveal()` lo enseñe.\n container.style.visibility = 'hidden';\n container.style.pointerEvents = 'none';\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 // ── Apertura diferida ────────────────────────────────────────────────────────────────\n // El spinner de apertura es lo que el cliente reporta como mala sensación: medido, está\n // ~310 ms en pantalla esperando el JSON del survey. En vez de enseñar la espera, se espera\n // en silencio y se abre el popup ya montado.\n let revealed = false;\n let revealTimer: ReturnType<typeof setTimeout> | null = null;\n // Foco en la primera pregunta si es de escribir. Al navegar lo pone @magicfeedback/native\n // (`autofocus: 'navigation'`); la apertura la decide el popup, porque native no sabe cuándo\n // se revela y un campo con `visibility:hidden` no acepta foco. Necesita el popup visible y\n // el survey cargado, que llegan en cualquier orden (el techo de la revelación puede vencer\n // antes que el survey). Solo con ratón o trackpad: en táctil, enfocar al abrir sacaría el\n // teclado sin que el usuario haya tocado nada.\n let surveyLoaded = false;\n let openFocusDone = false;\n\n function autofocusOnOpen() {\n if (openFocusDone || !revealed || !surveyLoaded) return;\n openFocusDone = true;\n if (hasFinePointer()) formInstance?.focusFirstQuestion?.();\n }\n\n function reveal() {\n if (revealed) return;\n revealed = true;\n if (revealTimer !== null) {\n clearTimeout(revealTimer);\n revealTimer = null;\n }\n // Si el popup se cerró durante la espera, este contenedor ya no es suyo: `hide()` lo ha\n // vaciado y puede estar sirviendo a otra apertura. Revelarlo aquí la sacaría antes de\n // tiempo.\n if (!container.contains(popup)) return;\n container.style.visibility = 'visible';\n container.style.pointerEvents = '';\n autofocusOnOpen();\n }\n\n /**\n * Revela en cuanto el popup está pintado del todo. Se mira el popup entero, no solo el\n * formulario, porque el logo del survey se inserta sobre la barra de progreso y es el que más\n * desplaza el contenido al entrar.\n */\n function revealWhenPainted() {\n if (revealed) return;\n revealWhenImagesLoaded(popup, reveal);\n }\n\n revealTimer = setTimeout(reveal, REVEAL_TIMEOUT_MS);\n\n // Gestión dinámica de loading\n function setLoading(isLoading: boolean) {\n spinnerEl.style.display = isLoading ? 'flex' : 'none';\n // El spinner no tapa el survey (28px en posición absoluta), así que sin esto el usuario\n // puede seguir cambiando de opción mientras se envía la página — y ese cambio ya no\n // viaja con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo\n // sustituye `@magicfeedback/native` por su propio contenedor al generar el survey.\n setSurveyBusy(formWrapper, isLoading);\n if (!isLoading) {\n formHost.style.visibility = 'visible';\n // Fin de la carga, sea por survey cargado o por error: el popup ya puede verse.\n // Entre páginas del survey es no-op, porque `reveal` solo actúa la primera vez.\n revealWhenPainted();\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 showLoadError('form() not available');\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 lang?: string;\n onLoadedEvent?: (args: {\n formData: FormData,\n loading?: boolean,\n // Presente cuando el survey NO ha cargado (406 de `/info`, sin red).\n error?: unknown,\n lang?: string,\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 autofocus?: 'always' | 'navigation' | false;\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 // Surveys multi-idioma: native pide a la API este idioma (el del init o el del\n // dispositivo) y, si el survey no lo tiene, la API sirve el idioma por defecto.\n lang: options?.language,\n // Foco en la primera pregunta de escribir tras Start, Siguiente o Atrás. Native lo\n // aplica después de afterSubmitEvent/onBackEvent, cuando `setLoading(false)` ya ha\n // quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).\n autofocus: 'navigation',\n };\n generateOptions.onLoadedEvent = ({formData, lang, error}) => {\n if (error) {\n showLoadError(error);\n return;\n }\n // Idioma en el que native muestra el survey. Manda sobre el del init para que el\n // chrome no se quede en inglés delante de un survey traducido. Native < 2.3 no lo\n // envía: entonces el primero de `formData.lang` (el idioma por defecto).\n const shownLang = typeof lang === 'string' && lang.trim().length > 0 ? lang : undefined;\n const surveyLang = shownLang ?? (Array.isArray(formData?.lang)\n ? formData.lang.find((l) => typeof l === 'string' && l.trim().length > 0)\n : undefined);\n if (surveyLang) applyLabels(surveyLang);\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 // El survey pinta sus controles con `--mf-primary`, que sale de `primaryColor`;\n // si la integración solo configura el del botón, el popup mezclaría el color de\n // la marca con el gris azulado por defecto del paquete.\n applySurveyPrimaryColor(popup, s);\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 // El logo va encima de la barra de progreso, como hermano del header.\n insertPopupLogo(popup, progressEl, s, 'deepdots-popup-logo');\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 // Cargar no es interacción: no llega al host como popup_clicked (paridad con KMP).\n setLoading(false); // hace visible el formulario y oculta el spinner\n // La primera carga es la apertura; las siguientes (tras Start) las enfoca native.\n if (!surveyLoaded) {\n surveyLoaded = true;\n autofocusOnOpen();\n }\n };\n generateOptions.beforeSubmitEvent = () => {\n setLoading(true);\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 = labels.errorRequired;\n errorHint.style.display = 'block';\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 = labels.errorSubmit;\n errorHint.style.display = 'block';\n // Antes el texto viajaba al host en un popup_clicked `submit_error`.\n sdkWarn('[Deepdots] survey submit 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 // El `back` para el host ya lo emitió el botón: aquí solo se actualiza la navegación.\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 showLoadError(err);\n });\n } catch (e) {\n sdkError('[MagicFeedback] Exception initializing form:', e);\n showLoadError(e);\n }\n}\n"],"mappings":"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","names":["revealWhenPainted"]}
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
sdkWarn,
|
|
16
16
|
setSurveyBusy,
|
|
17
17
|
style_default
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-AFRHPS3K.mjs";
|
|
19
19
|
|
|
20
20
|
// node_modules/@magicfeedback/native/dist/magicfeedback-sdk.browser.js
|
|
21
21
|
var require_magicfeedback_sdk_browser = __commonJS({
|
|
@@ -3065,10 +3065,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3065
3065
|
closeBtn.style.color = theme.textPrimary;
|
|
3066
3066
|
closeBtn.style.transform = "scale(1)";
|
|
3067
3067
|
};
|
|
3068
|
-
|
|
3068
|
+
const closeFromChrome = () => {
|
|
3069
3069
|
emit("popup_clicked", surveyId, { action: "close_icon" });
|
|
3070
3070
|
onClose();
|
|
3071
3071
|
};
|
|
3072
|
+
closeBtn.onclick = closeFromChrome;
|
|
3072
3073
|
closeBtn.style.flex = "0 0 auto";
|
|
3073
3074
|
closeBtn.style.marginLeft = "auto";
|
|
3074
3075
|
header.appendChild(closeBtn);
|
|
@@ -3176,6 +3177,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3176
3177
|
formWrapper.appendChild(spinnerEl);
|
|
3177
3178
|
formWrapper.appendChild(formHost);
|
|
3178
3179
|
main.appendChild(formWrapper);
|
|
3180
|
+
const loadErrorEl = document.createElement("div");
|
|
3181
|
+
loadErrorEl.id = "dd-load-error";
|
|
3182
|
+
loadErrorEl.className = "deepdots-load-error";
|
|
3183
|
+
loadErrorEl.setAttribute("role", "alert");
|
|
3184
|
+
loadErrorEl.style.cssText = `display:none; width:100%; text-align:center; padding:24px 0; font-size:15px; color:${theme.textPrimary};`;
|
|
3185
|
+
main.appendChild(loadErrorEl);
|
|
3179
3186
|
main.appendChild(errorHint);
|
|
3180
3187
|
const footer = document.createElement("div");
|
|
3181
3188
|
footer.id = "dd-footer";
|
|
@@ -3291,6 +3298,13 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3291
3298
|
justify-content: center;
|
|
3292
3299
|
text-align: center;
|
|
3293
3300
|
`;
|
|
3301
|
+
const cancelButton = document.createElement("button");
|
|
3302
|
+
cancelButton.id = "dd-cancel";
|
|
3303
|
+
cancelButton.type = "button";
|
|
3304
|
+
cancelButton.className = "dd-nav-btn";
|
|
3305
|
+
cancelButton.textContent = actionLabels.decline;
|
|
3306
|
+
cancelButton.style.cssText = submitButton.style.cssText;
|
|
3307
|
+
cancelButton.onclick = closeFromChrome;
|
|
3294
3308
|
submitButton.onclick = () => {
|
|
3295
3309
|
if (!surveyCompletedEmitted) {
|
|
3296
3310
|
emit("popup_clicked", surveyId, { action: "manual_send" });
|
|
@@ -3304,6 +3318,8 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3304
3318
|
startButton.textContent = actionLabels.start;
|
|
3305
3319
|
closeButton.textContent = actionLabels.complete;
|
|
3306
3320
|
submitButton.textContent = actionLabels.accept;
|
|
3321
|
+
cancelButton.textContent = actionLabels.decline;
|
|
3322
|
+
loadErrorEl.textContent = labels.errorLoad;
|
|
3307
3323
|
closeBtn.setAttribute("aria-label", labels.closeAria);
|
|
3308
3324
|
spinnerEl.setAttribute("aria-label", labels.loadingAria);
|
|
3309
3325
|
progressFollowUp.textContent = labels.followUp;
|
|
@@ -3314,10 +3330,12 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3314
3330
|
startButton.style.display = "none";
|
|
3315
3331
|
submitButton.style.display = "none";
|
|
3316
3332
|
closeButton.style.display = "none";
|
|
3333
|
+
cancelButton.style.display = "none";
|
|
3317
3334
|
footer.appendChild(submitButton);
|
|
3318
3335
|
footer.appendChild(backButton);
|
|
3319
3336
|
footer.appendChild(closeButton);
|
|
3320
3337
|
footer.appendChild(startButton);
|
|
3338
|
+
footer.appendChild(cancelButton);
|
|
3321
3339
|
containerContent.appendChild(main);
|
|
3322
3340
|
footer.style.flex = "0 0 auto";
|
|
3323
3341
|
containerContent.appendChild(footer);
|
|
@@ -3326,6 +3344,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3326
3344
|
startButton.style.display = "none";
|
|
3327
3345
|
submitButton.style.display = "none";
|
|
3328
3346
|
closeButton.style.display = "none";
|
|
3347
|
+
cancelButton.style.display = "none";
|
|
3329
3348
|
backButton.style.width = "";
|
|
3330
3349
|
startButton.style.width = "";
|
|
3331
3350
|
submitButton.style.width = "";
|
|
@@ -3355,6 +3374,9 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3355
3374
|
case "error":
|
|
3356
3375
|
setLoading(false);
|
|
3357
3376
|
break;
|
|
3377
|
+
case "load_error":
|
|
3378
|
+
cancelButton.style.display = "inline-flex";
|
|
3379
|
+
break;
|
|
3358
3380
|
}
|
|
3359
3381
|
}
|
|
3360
3382
|
function updateNavButtons() {
|
|
@@ -3376,6 +3398,20 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3376
3398
|
img.style.cssText = "max-width:100%; height:auto; margin:0 auto 16px auto; display:block;";
|
|
3377
3399
|
});
|
|
3378
3400
|
}
|
|
3401
|
+
let loadFailed = false;
|
|
3402
|
+
function showLoadError(reason) {
|
|
3403
|
+
sdkWarn("[Deepdots] survey failed to load:", reason);
|
|
3404
|
+
if (loadFailed) return;
|
|
3405
|
+
loadFailed = true;
|
|
3406
|
+
progressEl.style.display = "none";
|
|
3407
|
+
formWrapper.style.display = "none";
|
|
3408
|
+
errorHint.style.display = "none";
|
|
3409
|
+
loadErrorEl.textContent = labels.errorLoad;
|
|
3410
|
+
loadErrorEl.style.display = "block";
|
|
3411
|
+
setLoading(false);
|
|
3412
|
+
updateButtons("load_error");
|
|
3413
|
+
reveal();
|
|
3414
|
+
}
|
|
3379
3415
|
popup.appendChild(header);
|
|
3380
3416
|
popup.appendChild(progressEl);
|
|
3381
3417
|
popup.appendChild(containerContent);
|
|
@@ -3440,7 +3476,7 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3440
3476
|
try {
|
|
3441
3477
|
if (!import_native.default || typeof import_native.default.form !== "function") {
|
|
3442
3478
|
sdkWarn("[MagicFeedback] form() no disponible. Fallback manual.");
|
|
3443
|
-
|
|
3479
|
+
showLoadError("form() not available");
|
|
3444
3480
|
return;
|
|
3445
3481
|
}
|
|
3446
3482
|
import_native.default.init(
|
|
@@ -3466,7 +3502,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3466
3502
|
// quitado el `inert`. La apertura va aparte (ver autofocusOnOpen).
|
|
3467
3503
|
autofocus: "navigation"
|
|
3468
3504
|
};
|
|
3469
|
-
generateOptions.onLoadedEvent = ({ formData, lang }) => {
|
|
3505
|
+
generateOptions.onLoadedEvent = ({ formData, lang, error }) => {
|
|
3506
|
+
if (error) {
|
|
3507
|
+
showLoadError(error);
|
|
3508
|
+
return;
|
|
3509
|
+
}
|
|
3470
3510
|
const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
|
|
3471
3511
|
const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
|
|
3472
3512
|
if (surveyLang) applyLabels(surveyLang);
|
|
@@ -3565,11 +3605,11 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
|
|
|
3565
3605
|
};
|
|
3566
3606
|
formInstance.generate(formDivId, generateOptions).catch((err) => {
|
|
3567
3607
|
sdkError("[MagicFeedback] Error generating form:", err);
|
|
3568
|
-
|
|
3608
|
+
showLoadError(err);
|
|
3569
3609
|
});
|
|
3570
3610
|
} catch (e) {
|
|
3571
3611
|
sdkError("[MagicFeedback] Exception initializing form:", e);
|
|
3572
|
-
|
|
3612
|
+
showLoadError(e);
|
|
3573
3613
|
}
|
|
3574
3614
|
}
|
|
3575
3615
|
export {
|
|
@@ -3577,4 +3617,4 @@ export {
|
|
|
3577
3617
|
SURVEY_ALIGNMENT_STYLE_ID,
|
|
3578
3618
|
renderPopup
|
|
3579
3619
|
};
|
|
3580
|
-
//# sourceMappingURL=renderPopup-
|
|
3620
|
+
//# sourceMappingURL=renderPopup-YCAQ3ODC.mjs.map
|