@magicfeedback/popup-sdk 1.7.2 → 1.8.1

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.
@@ -1,4 +1,5 @@
1
1
  import {
2
+ BUSY_JS,
2
3
  LABELS,
3
4
  PRIMARY_COLOR_JS,
4
5
  RESOLVE_LOCALE_JS,
@@ -17,7 +18,7 @@ import {
17
18
  sdkWarn,
18
19
  setLogger,
19
20
  style_default
20
- } from "./chunk-322DDMX6.mjs";
21
+ } from "./chunk-EBJZDVC6.mjs";
21
22
 
22
23
  // src/react-native/index.tsx
23
24
  import { createContext, useContext, useEffect, useRef, useState } from "react";
@@ -431,6 +432,7 @@ ${customCss}
431
432
  var footer=document.getElementById('dd-footer');
432
433
  var spinner=document.getElementById('dd-spinner');
433
434
  var formHost=document.getElementById('mf');
435
+ var formWrapper=document.getElementById('dd-form-wrapper');
434
436
  var errorHint=document.getElementById('dd-error');
435
437
  var backBtn=document.getElementById('dd-back');
436
438
  var startBtn=document.getElementById('dd-start');
@@ -590,6 +592,7 @@ ${customCss}
590
592
  }
591
593
 
592
594
  ${PRIMARY_COLOR_JS}
595
+ ${BUSY_JS}
593
596
  ${REVEAL_JS}
594
597
  // El popup se ense\xF1a cuando el survey est\xE1 pintado; \`ready\` deja al host abrir su propio
595
598
  // Modal en ese momento en vez de montar un WebView en blanco.
@@ -600,6 +603,11 @@ ${REVEAL_JS}
600
603
 
601
604
  function setLoading(isLoading){
602
605
  spinner.style.display=isLoading?'flex':'none';
606
+ // El spinner no tapa el survey (28px en posicion absoluta), asi que sin esto el usuario
607
+ // puede seguir cambiando de opcion mientras se envia la pagina, y ese cambio ya no viaja
608
+ // con ella. Se bloquea el wrapper, que es el elemento estable: el div de dentro lo
609
+ // sustituye @magicfeedback/native por su propio contenedor al generar el survey.
610
+ ddSetSurveyBusy(formWrapper, isLoading);
603
611
  if(!isLoading){
604
612
  formHost.style.visibility='visible';
605
613
  // Fin de la carga, sea por survey cargado o por error. Entre p\xE1ginas es no-op: la
@@ -862,7 +870,7 @@ function createReactNativeRenderer() {
862
870
  }
863
871
 
864
872
  // src/ui/preload.ts
865
- var defaultLoader = () => import("./renderPopup-SNDPX5RX.mjs");
873
+ var defaultLoader = () => import("./renderPopup-E2OLIXIQ.mjs");
866
874
  var pending = null;
867
875
  function preloadRenderPopup(loader = defaultLoader) {
868
876
  if (pending) return pending;
@@ -935,7 +943,7 @@ var BrowserPopupRenderer = class {
935
943
  if (!this.container) return;
936
944
  this.visible = true;
937
945
  const container = this.container;
938
- void import("./renderPopup-SNDPX5RX.mjs").then(({ renderPopup }) => {
946
+ void import("./renderPopup-E2OLIXIQ.mjs").then(({ renderPopup }) => {
939
947
  renderPopup(container, surveyId, productId, actions, emit, onClose, env, userId, style, sessionId, miniService, analyticsFeedbackSessionId, options);
940
948
  });
941
949
  }
@@ -2373,6 +2381,9 @@ var DeepdotsPopups = class {
2373
2381
  this.emitEvent("popup_shown", def.surveyId, { popupId: def.id });
2374
2382
  }
2375
2383
  shouldShow(def, pathUrl, skipPathCheck = false) {
2384
+ if (this.isPathExcluded(def, pathUrl)) {
2385
+ return false;
2386
+ }
2376
2387
  if (!skipPathCheck && !this.matchesSegmentsPath(def, pathUrl)) {
2377
2388
  return false;
2378
2389
  }
@@ -2403,40 +2414,85 @@ var DeepdotsPopups = class {
2403
2414
  }
2404
2415
  return matches;
2405
2416
  }
2417
+ /** `segments.path`: rutas donde el popup PUEDE mostrarse. Sin lista, en todas. */
2406
2418
  matchesSegmentsPath(def, pathUrl) {
2407
2419
  const paths = def.segments?.path;
2408
- if (!paths || paths.length === 0) return true;
2409
- if (typeof window === "undefined" || !window.location) {
2420
+ if (!Array.isArray(paths) || paths.length === 0) return true;
2421
+ const location = this.resolveCurrentLocation(pathUrl);
2422
+ if (!location) {
2410
2423
  this.debug("No window.location available for path comparison", { popupId: def.id, paths });
2411
2424
  return true;
2412
2425
  }
2413
- const normalizedHref = this.normalizeUrl(pathUrl || window.location.href || "");
2414
- const currentUrl = this.safeParseUrl(normalizedHref);
2415
- const normalizedPath = this.normalizeUrl(currentUrl?.pathname || window.location.pathname || "");
2416
2426
  const matches = paths.some((rawCandidate) => {
2417
- const candidate = this.normalizeUrl(rawCandidate);
2418
- let match = false;
2419
- if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
2420
- match = normalizedHref === candidate;
2421
- } else if (candidate.startsWith("/")) {
2422
- match = normalizedHref.includes(candidate);
2423
- } else {
2424
- match = normalizedPath === candidate;
2425
- }
2427
+ const match = this.matchesPathCandidate(rawCandidate, location);
2426
2428
  this.debug("Path comparison", {
2427
2429
  popupId: def.id,
2428
- candidate,
2429
- currentPath: normalizedPath,
2430
- currentHref: normalizedHref,
2430
+ candidate: rawCandidate,
2431
+ currentPath: location.path,
2432
+ currentHref: location.href,
2431
2433
  match
2432
2434
  });
2433
2435
  return match;
2434
2436
  });
2435
2437
  if (!matches) {
2436
- this.debug("No path match for popup", { popupId: def.id, paths, currentPath: normalizedPath });
2438
+ this.debug("No path match for popup", { popupId: def.id, paths, currentPath: location.path });
2437
2439
  }
2438
2440
  return matches;
2439
2441
  }
2442
+ /**
2443
+ * `segments.excludedPaths`: rutas donde el popup NO debe mostrarse. Gana sobre
2444
+ * `segments.path` (`path: ['/']` + `excludedPaths: ['/cart']` = en todas menos el
2445
+ * carrito) y se aplica también cuando no hay `path` (= en todas menos las excluidas).
2446
+ */
2447
+ isPathExcluded(def, pathUrl) {
2448
+ const excluded = def.segments?.excludedPaths;
2449
+ if (!Array.isArray(excluded) || excluded.length === 0) return false;
2450
+ const location = this.resolveCurrentLocation(pathUrl);
2451
+ if (!location) {
2452
+ this.debug("No window.location available for excluded path comparison", { popupId: def.id, excluded });
2453
+ return false;
2454
+ }
2455
+ const hit = excluded.find((rawCandidate) => this.matchesPathCandidate(rawCandidate, location));
2456
+ if (hit !== void 0) {
2457
+ this.debug("Popup excluded by path", {
2458
+ popupId: def.id,
2459
+ candidate: hit,
2460
+ currentPath: location.path,
2461
+ currentHref: location.href
2462
+ });
2463
+ return true;
2464
+ }
2465
+ return false;
2466
+ }
2467
+ /**
2468
+ * Compara UN candidato de `segments.path`/`segments.excludedPaths` con la ruta
2469
+ * actual. Mismas reglas para incluir y para excluir:
2470
+ * - URL absoluta → href completo exacto
2471
+ * - empieza por / → subcadena del href (así entran las rutas con hash: `/#/home`)
2472
+ * - resto → pathname exacto
2473
+ * Ignora entradas que no sean string: el backend puede colar un `null` en la lista
2474
+ * y no puede tumbar la evaluación del trigger entero (mismo fallo que hubo con
2475
+ * `segments.lang`).
2476
+ */
2477
+ matchesPathCandidate(rawCandidate, location) {
2478
+ if (typeof rawCandidate !== "string") return false;
2479
+ const candidate = this.normalizeUrl(rawCandidate);
2480
+ if (!candidate) return false;
2481
+ if (candidate.startsWith("http://") || candidate.startsWith("https://")) {
2482
+ return location.href === candidate;
2483
+ }
2484
+ if (candidate.startsWith("/")) {
2485
+ return location.href.includes(candidate);
2486
+ }
2487
+ return location.path === candidate;
2488
+ }
2489
+ /** href + pathname normalizados de la ruta a evaluar, o null si no hay `window.location`. */
2490
+ resolveCurrentLocation(pathUrl) {
2491
+ if (typeof window === "undefined" || !window.location) return null;
2492
+ const href = this.normalizeUrl(pathUrl || window.location.href || "");
2493
+ const parsed = this.safeParseUrl(href);
2494
+ return { href, path: this.normalizeUrl(parsed?.pathname || window.location.pathname || "") };
2495
+ }
2440
2496
  evaluateCooldown(def, condition) {
2441
2497
  switch (condition.answered) {
2442
2498
  case "SHOWED":
@@ -2733,7 +2789,7 @@ var DeepdotsPopups = class {
2733
2789
  }
2734
2790
  if (!this.popupContainer) return;
2735
2791
  const container = this.popupContainer;
2736
- void import("./renderPopup-SNDPX5RX.mjs").then(({ renderPopup }) => {
2792
+ void import("./renderPopup-E2OLIXIQ.mjs").then(({ renderPopup }) => {
2737
2793
  renderPopup(
2738
2794
  container,
2739
2795
  surveyId,