@magicfeedback/popup-sdk 1.8.5 → 1.10.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.
@@ -552,7 +552,7 @@ var init_style = __esm({
552
552
  style_default = `/**
553
553
  * NO EDITAR A MANO \u2014 generado por scripts/vendor-survey-css.mjs
554
554
  *
555
- * Copia de @magicfeedback/native@2.2.22 \u2192 dist/styles/magicfeedback-default.css
555
+ * Copia de @magicfeedback/native@2.2.31 \u2192 dist/styles/magicfeedback-default.css
556
556
  * (la hoja can\xF3nica del paquete, la que apunta su campo "style"), con estos cambios locales:
557
557
  *
558
558
  * 1. Sin el @import de Google Fonts (Nunito): no queremos una petici\xF3n externa desde la
@@ -563,7 +563,9 @@ var init_style = __esm({
563
563
  * repintar\xEDa su web.
564
564
  * 4. El grupo .magicfeedback-radio se cualifica como div.magicfeedback-radio (la clase acaba
565
565
  * tambi\xE9n en el <input> del boolean).
566
- * 5. Al final, un bloque de reglas propias del popup (fuente en los controles y padding del
566
+ * 5. Todo :hover va dentro de @media (hover: hover): en t\xE1ctil WebKit deja el hover pegado
567
+ * en el \xFAltimo punto tocado y la opci\xF3n de la p\xE1gina siguiente parec\xEDa seleccionada.
568
+ * 6. Al final, un bloque de reglas propias del popup (fuente en los controles y padding del
567
569
  * radio nativo).
568
570
  *
569
571
  * Las variables --mf-* se dejan en :root a prop\xF3sito: el modal del priority-list se portea a
@@ -801,9 +803,11 @@ var init_style = __esm({
801
803
  text-underline-offset: 2px;
802
804
  }
803
805
 
804
- .magicfeedback-info-message a:hover,
805
- .magicfeedback-start-message a:hover {
806
- color: var(--mf-primary-hover);
806
+ @media (hover: hover) {
807
+ .magicfeedback-info-message a:hover,
808
+ .magicfeedback-start-message a:hover {
809
+ color: var(--mf-primary-hover);
810
+ }
807
811
  }
808
812
 
809
813
  .magicfeedback-info-message ul,
@@ -1029,10 +1033,12 @@ div.magicfeedback-radio {
1029
1033
  transition: var(--mf-transition-fast);
1030
1034
  }
1031
1035
 
1032
- .magicfeedback-checkbox-container:hover,
1033
- .magicfeedback-radio-container:hover {
1034
- background-color: var(--mf-bg-hover);
1035
- border-color: var(--mf-primary);
1036
+ @media (hover: hover) {
1037
+ .magicfeedback-checkbox-container:hover,
1038
+ .magicfeedback-radio-container:hover {
1039
+ background-color: var(--mf-bg-hover);
1040
+ border-color: var(--mf-primary);
1041
+ }
1036
1042
  }
1037
1043
 
1038
1044
  .magicfeedback-checkbox-container:has(input:checked),
@@ -1099,9 +1105,11 @@ div.magicfeedback-radio {
1099
1105
  height: inherit;
1100
1106
  }
1101
1107
 
1102
- .magicfeedback-boolean-option label:hover {
1103
- background-color: var(--mf-bg-hover);
1104
- border-color: var(--mf-primary);
1108
+ @media (hover: hover) {
1109
+ .magicfeedback-boolean-option label:hover {
1110
+ background-color: var(--mf-bg-hover);
1111
+ border-color: var(--mf-primary);
1112
+ }
1105
1113
  }
1106
1114
 
1107
1115
  .magicfeedback-boolean-option input[type="radio"]:checked + label {
@@ -1172,9 +1180,11 @@ div.magicfeedback-radio {
1172
1180
  border: 2px solid transparent;
1173
1181
  }
1174
1182
 
1175
- .magicfeedback-rating-option-label-container img:hover {
1176
- transform: scale(1.1);
1177
- border-color: var(--mf-primary);
1183
+ @media (hover: hover) {
1184
+ .magicfeedback-rating-option-label-container img:hover {
1185
+ transform: scale(1.1);
1186
+ border-color: var(--mf-primary);
1187
+ }
1178
1188
  }
1179
1189
 
1180
1190
  .magicfeedback-rating-option-label-container input[type="radio"]:checked + img {
@@ -1326,9 +1336,11 @@ div.magicfeedback-radio {
1326
1336
  font-size: 0.8rem;
1327
1337
  }
1328
1338
 
1329
- .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
1330
- .magicfeedback-rating-number-option-row-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1331
- background: var(--mf-primary-light);
1339
+ @media (hover: hover) {
1340
+ .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
1341
+ .magicfeedback-rating-number-option-row-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1342
+ background: var(--mf-primary-light);
1343
+ }
1332
1344
  }
1333
1345
 
1334
1346
  .magicfeedback-rating-number-option-row .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
@@ -1365,8 +1377,10 @@ div.magicfeedback-radio {
1365
1377
  font-size: var(--mf-font-size-sm);
1366
1378
  }
1367
1379
 
1368
- .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1369
- background: var(--mf-primary-light);
1380
+ @media (hover: hover) {
1381
+ .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1382
+ background: var(--mf-primary-light);
1383
+ }
1370
1384
  }
1371
1385
 
1372
1386
  .magicfeedback-rating-number-option--extra .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value {
@@ -1399,9 +1413,11 @@ div.magicfeedback-radio {
1399
1413
  color: var(--mf-primary);
1400
1414
  }
1401
1415
 
1402
- .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
1403
- .magicfeedback-rating-number-option-column-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1404
- background: var(--mf-primary-light);
1416
+ @media (hover: hover) {
1417
+ .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value,
1418
+ .magicfeedback-rating-number-option-column-reverse .magicfeedback-rating-number-option-label-container:hover .magicfeedback-rating-number-value {
1419
+ background: var(--mf-primary-light);
1420
+ }
1405
1421
  }
1406
1422
 
1407
1423
  .magicfeedback-rating-number-option-column .magicfeedback-rating-number-option-label-container input[type="radio"]:checked + .magicfeedback-rating-number-value,
@@ -1455,10 +1471,12 @@ div.magicfeedback-radio {
1455
1471
  transition: var(--mf-transition-fast);
1456
1472
  }
1457
1473
 
1458
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
1459
- .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
1460
- color: #fbbf24;
1461
- transform: scale(1.1);
1474
+ @media (hover: hover) {
1475
+ .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover,
1476
+ .magicfeedback-rating-star-option:not(:checked) > .rating__star:hover ~ span {
1477
+ color: #fbbf24;
1478
+ transform: scale(1.1);
1479
+ }
1462
1480
  }
1463
1481
 
1464
1482
  .magicfeedback-rating-star-selected {
@@ -1499,9 +1517,11 @@ div.magicfeedback-radio {
1499
1517
  transition: var(--mf-transition-fast);
1500
1518
  }
1501
1519
 
1502
- .magicfeedback-image-option-label-container:hover img {
1503
- border-color: var(--mf-primary);
1504
- box-shadow: var(--mf-shadow-md);
1520
+ @media (hover: hover) {
1521
+ .magicfeedback-image-option-label-container:hover img {
1522
+ border-color: var(--mf-primary);
1523
+ box-shadow: var(--mf-shadow-md);
1524
+ }
1505
1525
  }
1506
1526
 
1507
1527
  .magicfeedback-image-option-label-container input:checked + img {
@@ -1516,6 +1536,9 @@ div.magicfeedback-radio {
1516
1536
  width: 100%;
1517
1537
  overflow-x: auto;
1518
1538
  border-radius: var(--mf-radius-md);
1539
+ /* the layout follows the space the survey gets (a pop-in, a sidebar),
1540
+ not the size of the window */
1541
+ container-type: inline-size;
1519
1542
  }
1520
1543
 
1521
1544
  .magicfeedback-multi-question-matrix-table {
@@ -1542,9 +1565,11 @@ div.magicfeedback-radio {
1542
1565
  background-color: var(--mf-bg-secondary);
1543
1566
  }
1544
1567
 
1545
- .magicfeedback-multi-question-matrix-row-tr:hover {
1546
- background-color: var(--mf-bg-hover);
1547
- box-shadow: var(--mf-shadow-sm);
1568
+ @media (hover: hover) {
1569
+ .magicfeedback-multi-question-matrix-row-tr:hover {
1570
+ background-color: var(--mf-bg-hover);
1571
+ box-shadow: var(--mf-shadow-sm);
1572
+ }
1548
1573
  }
1549
1574
 
1550
1575
  .magicfeedback-multi-question-matrix-table td {
@@ -1565,6 +1590,296 @@ div.magicfeedback-radio {
1565
1590
  accent-color: var(--mf-primary);
1566
1591
  }
1567
1592
 
1593
+ .magicfeedback-multi-question-matrix-question {
1594
+ min-width: 200px;
1595
+ padding: 10px;
1596
+ }
1597
+
1598
+ /* the column header names the option in a table; the per-cell label is for
1599
+ screen readers until the rows stack */
1600
+ .magicfeedback-multi-question-matrix-option-label {
1601
+ position: absolute;
1602
+ width: 1px;
1603
+ height: 1px;
1604
+ overflow: hidden;
1605
+ clip: rect(0, 0, 0, 0);
1606
+ white-space: nowrap;
1607
+ }
1608
+
1609
+ /* narrow container: every row becomes its own question with the options
1610
+ stacked underneath, instead of a table cut off at the side */
1611
+ @container (max-width: 599px) {
1612
+ .magicfeedback-multi-question-matrix-table,
1613
+ .magicfeedback-multi-question-matrix-table tbody,
1614
+ .magicfeedback-multi-question-matrix-table tr,
1615
+ .magicfeedback-multi-question-matrix-table td {
1616
+ display: block;
1617
+ width: 100%;
1618
+ }
1619
+
1620
+ .magicfeedback-multi-question-matrix-table thead {
1621
+ display: none;
1622
+ }
1623
+
1624
+ .magicfeedback-multi-question-matrix-row-tr,
1625
+ .magicfeedback-multi-question-matrix-row-tr:nth-child(even) {
1626
+ margin-bottom: var(--mf-space-lg);
1627
+ background: transparent;
1628
+ box-shadow: none;
1629
+ }
1630
+
1631
+ @media (hover: hover) {
1632
+ .magicfeedback-multi-question-matrix-row-tr:hover {
1633
+ margin-bottom: var(--mf-space-lg);
1634
+ background: transparent;
1635
+ box-shadow: none;
1636
+ }
1637
+ }
1638
+
1639
+ .magicfeedback-multi-question-matrix-table td.magicfeedback-multi-question-matrix-question {
1640
+ min-width: 0;
1641
+ padding: 0 0 var(--mf-space-sm);
1642
+ text-align: start;
1643
+ }
1644
+
1645
+ .magicfeedback-multi-question-matrix-question .magicfeedback-multi-question-matrix-label {
1646
+ font-weight: 600;
1647
+ }
1648
+
1649
+ .magicfeedback-multi-question-matrix-table td.magicfeedback-multi-question-matrix-option {
1650
+ display: flex;
1651
+ align-items: center;
1652
+ gap: var(--mf-space-sm);
1653
+ margin-bottom: var(--mf-space-sm);
1654
+ padding: 0 var(--mf-space-md);
1655
+ text-align: start;
1656
+ background: var(--mf-bg-secondary);
1657
+ border: 2px solid var(--mf-border);
1658
+ border-radius: var(--mf-radius-md);
1659
+ cursor: pointer;
1660
+ transition: var(--mf-transition-fast);
1661
+ }
1662
+
1663
+ @media (hover: hover) {
1664
+ .magicfeedback-multi-question-matrix-option:hover {
1665
+ border-color: var(--mf-primary-border);
1666
+ }
1667
+ }
1668
+
1669
+ .magicfeedback-multi-question-matrix-option:has(input:checked) {
1670
+ border-color: var(--mf-primary);
1671
+ background: var(--mf-primary-light);
1672
+ }
1673
+
1674
+ .magicfeedback-multi-question-matrix-table .magicfeedback-multi-question-matrix-option input[type="radio"] {
1675
+ flex-shrink: 0;
1676
+ }
1677
+
1678
+ /* the whole card is the click target */
1679
+ .magicfeedback-multi-question-matrix-option-label {
1680
+ position: static;
1681
+ flex: 1;
1682
+ width: auto;
1683
+ height: auto;
1684
+ padding: var(--mf-space-md) 0;
1685
+ overflow: visible;
1686
+ clip: auto;
1687
+ white-space: normal;
1688
+ font-size: var(--mf-font-size-base);
1689
+ color: var(--mf-text-primary);
1690
+ cursor: pointer;
1691
+ }
1692
+ }
1693
+
1694
+ /* ========================================
1695
+ MAXDIFF (BEST\u2013WORST)
1696
+ ======================================== */
1697
+ .magicfeedback-max-diff {
1698
+ /* "least" gets its own hue, apart from the brand color "most" uses; dark
1699
+ enough to read as header text and to hold the white center dot */
1700
+ --mf-max-diff-worst: #b45309;
1701
+ width: 100%;
1702
+ }
1703
+
1704
+ .magicfeedback-max-diff-container {
1705
+ width: 100%;
1706
+ /* the layout follows the space the survey gets (a pop-in, a sidebar),
1707
+ not the size of the window */
1708
+ container-type: inline-size;
1709
+ }
1710
+
1711
+ .magicfeedback-max-diff-progress {
1712
+ display: flex;
1713
+ align-items: center;
1714
+ gap: var(--mf-space-md);
1715
+ margin-bottom: var(--mf-space-lg);
1716
+ }
1717
+
1718
+ .magicfeedback-max-diff-progress-label {
1719
+ font-size: 0.8125rem;
1720
+ font-weight: var(--mf-font-weight-bold);
1721
+ letter-spacing: 0.04em;
1722
+ text-transform: uppercase;
1723
+ white-space: nowrap;
1724
+ color: var(--mf-text-secondary);
1725
+ }
1726
+
1727
+ .magicfeedback-max-diff-progress-track {
1728
+ flex: 1;
1729
+ height: 6px;
1730
+ overflow: hidden;
1731
+ border-radius: var(--mf-radius-full);
1732
+ background: var(--mf-border);
1733
+ }
1734
+
1735
+ .magicfeedback-max-diff-progress-fill {
1736
+ display: block;
1737
+ height: 100%;
1738
+ border-radius: var(--mf-radius-full);
1739
+ background: var(--mf-primary);
1740
+ }
1741
+
1742
+ .magicfeedback-max-diff-table {
1743
+ width: 100%;
1744
+ border-collapse: collapse;
1745
+ }
1746
+
1747
+ .magicfeedback-max-diff-head,
1748
+ .magicfeedback-max-diff-head-spacer {
1749
+ border-bottom: 1px solid var(--mf-border);
1750
+ }
1751
+
1752
+ .magicfeedback-max-diff-head {
1753
+ width: 11rem;
1754
+ padding: 0 var(--mf-space-sm) var(--mf-space-sm);
1755
+ font-size: 0.8125rem;
1756
+ font-weight: var(--mf-font-weight-extrabold);
1757
+ letter-spacing: 0.04em;
1758
+ text-transform: uppercase;
1759
+ text-align: center;
1760
+ vertical-align: bottom;
1761
+ }
1762
+
1763
+ .magicfeedback-max-diff-head-best {
1764
+ color: var(--mf-primary);
1765
+ }
1766
+
1767
+ .magicfeedback-max-diff-head-worst {
1768
+ color: var(--mf-max-diff-worst);
1769
+ }
1770
+
1771
+ .magicfeedback-max-diff-head-label {
1772
+ display: inline-flex;
1773
+ align-items: center;
1774
+ gap: 6px;
1775
+ }
1776
+
1777
+ .magicfeedback-max-diff-row {
1778
+ transition: background-color 0.15s ease;
1779
+ }
1780
+
1781
+ .magicfeedback-max-diff-row > * {
1782
+ border-bottom: 1px solid var(--mf-border);
1783
+ }
1784
+
1785
+ .magicfeedback-max-diff-row:has(.magicfeedback-max-diff-best:checked) {
1786
+ background-color: color-mix(in srgb, var(--mf-primary) 8%, transparent);
1787
+ }
1788
+
1789
+ .magicfeedback-max-diff-row:has(.magicfeedback-max-diff-worst:checked) {
1790
+ background-color: color-mix(in srgb, var(--mf-max-diff-worst) 8%, transparent);
1791
+ }
1792
+
1793
+ .magicfeedback-max-diff-item {
1794
+ padding: var(--mf-space-md) var(--mf-space-sm);
1795
+ font-size: 1.0625rem;
1796
+ font-weight: var(--mf-font-weight-semibold);
1797
+ line-height: 1.4;
1798
+ text-align: center;
1799
+ color: var(--mf-text-primary);
1800
+ }
1801
+
1802
+ .magicfeedback-max-diff-row:has(input:checked) .magicfeedback-max-diff-item {
1803
+ font-weight: var(--mf-font-weight-extrabold);
1804
+ }
1805
+
1806
+ .magicfeedback-max-diff-cell {
1807
+ padding: 0;
1808
+ }
1809
+
1810
+ /* the whole cell is the click target */
1811
+ .magicfeedback-max-diff-pick {
1812
+ display: flex;
1813
+ align-items: center;
1814
+ justify-content: center;
1815
+ min-height: 64px;
1816
+ cursor: pointer;
1817
+ }
1818
+
1819
+ .magicfeedback-max-diff-input {
1820
+ appearance: none;
1821
+ -webkit-appearance: none;
1822
+ flex-shrink: 0;
1823
+ width: 26px;
1824
+ height: 26px;
1825
+ margin: 0;
1826
+ border: 2px solid color-mix(in srgb, var(--mf-text-secondary) 80%, white);
1827
+ border-radius: var(--mf-radius-full);
1828
+ background: var(--mf-bg-secondary);
1829
+ cursor: pointer;
1830
+ transition: var(--mf-transition-fast);
1831
+ }
1832
+
1833
+ @media (hover: hover) {
1834
+ .magicfeedback-max-diff-pick:hover .magicfeedback-max-diff-input {
1835
+ transform: scale(1.08);
1836
+ }
1837
+ }
1838
+
1839
+ .magicfeedback-max-diff-best:checked {
1840
+ border-color: var(--mf-primary);
1841
+ background: radial-gradient(circle, #FFFFFF 0 4px, var(--mf-primary) 4.5px);
1842
+ }
1843
+
1844
+ .magicfeedback-max-diff-worst:checked {
1845
+ border-color: var(--mf-max-diff-worst);
1846
+ background: radial-gradient(circle, #FFFFFF 0 4px, var(--mf-max-diff-worst) 4.5px);
1847
+ }
1848
+
1849
+ .magicfeedback-max-diff-input:focus-visible {
1850
+ outline: 3px solid var(--mf-primary-border);
1851
+ outline-offset: 3px;
1852
+ }
1853
+
1854
+ /* narrow container: same table, slimmer pick columns and the header icon
1855
+ stacked over its label */
1856
+ @container (max-width: 599px) {
1857
+ .magicfeedback-max-diff-head {
1858
+ width: 4.75rem;
1859
+ padding-inline: 2px;
1860
+ font-size: 0.6875rem;
1861
+ }
1862
+
1863
+ .magicfeedback-max-diff-head-label {
1864
+ flex-direction: column;
1865
+ gap: 2px;
1866
+ }
1867
+
1868
+ .magicfeedback-max-diff-item {
1869
+ padding-inline: 4px;
1870
+ font-size: var(--mf-font-size-base);
1871
+ }
1872
+
1873
+ .magicfeedback-max-diff-pick {
1874
+ min-height: 68px;
1875
+ }
1876
+
1877
+ .magicfeedback-max-diff-input {
1878
+ width: 28px;
1879
+ height: 28px;
1880
+ }
1881
+ }
1882
+
1568
1883
  /* ========================================
1569
1884
  PRIORITY LIST
1570
1885
  ======================================== */
@@ -1580,9 +1895,11 @@ div.magicfeedback-radio {
1580
1895
  transition: var(--mf-transition-fast);
1581
1896
  }
1582
1897
 
1583
- .magicfeedback-priority-list-item:hover {
1584
- box-shadow: var(--mf-shadow-sm);
1585
- border-color: var(--mf-primary);
1898
+ @media (hover: hover) {
1899
+ .magicfeedback-priority-list-item:hover {
1900
+ box-shadow: var(--mf-shadow-sm);
1901
+ border-color: var(--mf-primary);
1902
+ }
1586
1903
  }
1587
1904
 
1588
1905
  .magicfeedback-priority-list-item-label {
@@ -1603,10 +1920,12 @@ div.magicfeedback-radio {
1603
1920
  user-select: none;
1604
1921
  }
1605
1922
 
1606
- .magicfeedback-priority-list-arrow-up:hover,
1607
- .magicfeedback-priority-list-arrow-down:hover {
1608
- color: var(--mf-primary);
1609
- transform: scale(1.2);
1923
+ @media (hover: hover) {
1924
+ .magicfeedback-priority-list-arrow-up:hover,
1925
+ .magicfeedback-priority-list-arrow-down:hover {
1926
+ color: var(--mf-primary);
1927
+ transform: scale(1.2);
1928
+ }
1610
1929
  }
1611
1930
 
1612
1931
  /* ========================================
@@ -1713,10 +2032,12 @@ div.magicfeedback-radio {
1713
2032
  box-shadow: var(--mf-shadow-md);
1714
2033
  }
1715
2034
 
1716
- .magicfeedback-submit:hover {
1717
- background-color: var(--mf-primary-hover);
1718
- box-shadow: var(--mf-shadow-lg);
1719
- transform: translateY(-2px);
2035
+ @media (hover: hover) {
2036
+ .magicfeedback-submit:hover {
2037
+ background-color: var(--mf-primary-hover);
2038
+ box-shadow: var(--mf-shadow-lg);
2039
+ transform: translateY(-2px);
2040
+ }
1720
2041
  }
1721
2042
 
1722
2043
  .magicfeedback-submit:active {
@@ -1742,9 +2063,20 @@ div.magicfeedback-radio {
1742
2063
  transition: var(--mf-transition-fast);
1743
2064
  }
1744
2065
 
1745
- .magicfeedback-back:hover {
1746
- border-color: var(--mf-primary);
1747
- background-color: var(--mf-bg-hover);
2066
+ @media (hover: hover) {
2067
+ .magicfeedback-back:hover {
2068
+ border-color: var(--mf-primary);
2069
+ background-color: var(--mf-bg-hover);
2070
+ }
2071
+ }
2072
+
2073
+ /* hidden on the first page; keep the submit button on the right */
2074
+ .magicfeedback-back[hidden] {
2075
+ display: none;
2076
+ }
2077
+
2078
+ .magicfeedback-action-container .magicfeedback-submit {
2079
+ margin-left: auto;
1748
2080
  }
1749
2081
 
1750
2082
  /* ========================================
@@ -1764,8 +2096,10 @@ div.magicfeedback-radio {
1764
2096
  transition: var(--mf-transition-fast);
1765
2097
  }
1766
2098
 
1767
- .magicfeedback-skip-container:hover {
1768
- color: var(--mf-primary);
2099
+ @media (hover: hover) {
2100
+ .magicfeedback-skip-container:hover {
2101
+ color: var(--mf-primary);
2102
+ }
1769
2103
  }
1770
2104
 
1771
2105
  .magicfeedback-skip-container input[type="checkbox"] {
@@ -1906,9 +2240,11 @@ div.magicfeedback-radio {
1906
2240
  box-shadow: var(--mf-shadow-sm);
1907
2241
  }
1908
2242
 
1909
- .magicfeedback-button:hover {
1910
- background: var(--mf-bg-hover);
1911
- border-color: var(--mf-primary);
2243
+ @media (hover: hover) {
2244
+ .magicfeedback-button:hover {
2245
+ background: var(--mf-bg-hover);
2246
+ border-color: var(--mf-primary);
2247
+ }
1912
2248
  }
1913
2249
 
1914
2250
  .magicfeedback-button:active {
@@ -1922,8 +2258,10 @@ div.magicfeedback-radio {
1922
2258
  box-shadow: var(--mf-shadow-md);
1923
2259
  }
1924
2260
 
1925
- .magicfeedback-button-primary:hover {
1926
- background: var(--mf-primary-hover);
2261
+ @media (hover: hover) {
2262
+ .magicfeedback-button-primary:hover {
2263
+ background: var(--mf-primary-hover);
2264
+ }
1927
2265
  }
1928
2266
 
1929
2267
  .magicfeedback-button-primary:active {
@@ -1949,8 +2287,10 @@ div.magicfeedback-radio {
1949
2287
  box-shadow: var(--mf-shadow-md);
1950
2288
  }
1951
2289
 
1952
- .magicfeedback-start-message-button:hover {
1953
- background: var(--mf-primary-hover);
2290
+ @media (hover: hover) {
2291
+ .magicfeedback-start-message-button:hover {
2292
+ background: var(--mf-primary-hover);
2293
+ }
1954
2294
  }
1955
2295
 
1956
2296
  /* ========================================
@@ -2086,12 +2426,18 @@ div.magicfeedback-radio {
2086
2426
  color: #666;
2087
2427
  }
2088
2428
 
2089
- .magicfeedback-modal-close:hover,
2090
2429
  .magicfeedback-modal-close:focus {
2091
2430
  color: #000;
2092
2431
  outline: none;
2093
2432
  }
2094
2433
 
2434
+ @media (hover: hover) {
2435
+ .magicfeedback-modal-close:hover {
2436
+ color: #000;
2437
+ outline: none;
2438
+ }
2439
+ }
2440
+
2095
2441
  /* ========================================
2096
2442
  FILE / IMAGE UPLOAD
2097
2443
  ======================================== */
@@ -2135,9 +2481,11 @@ div.magicfeedback-radio {
2135
2481
  transition: var(--mf-transition);
2136
2482
  }
2137
2483
 
2138
- .magicfeedback-upload-dropzone:hover {
2139
- border-color: var(--mf-primary);
2140
- background-color: var(--mf-bg-hover);
2484
+ @media (hover: hover) {
2485
+ .magicfeedback-upload-dropzone:hover {
2486
+ border-color: var(--mf-primary);
2487
+ background-color: var(--mf-bg-hover);
2488
+ }
2141
2489
  }
2142
2490
 
2143
2491
  .magicfeedback-upload-dropzone:focus-within {
@@ -2275,13 +2623,20 @@ div.magicfeedback-radio {
2275
2623
  transition: var(--mf-transition-fast);
2276
2624
  }
2277
2625
 
2278
- .magicfeedback-upload-remove:hover,
2279
2626
  .magicfeedback-upload-remove:focus-visible {
2280
2627
  color: var(--mf-error);
2281
2628
  background-color: var(--mf-primary-light);
2282
2629
  outline: none;
2283
2630
  }
2284
2631
 
2632
+ @media (hover: hover) {
2633
+ .magicfeedback-upload-remove:hover {
2634
+ color: var(--mf-error);
2635
+ background-color: var(--mf-primary-light);
2636
+ outline: none;
2637
+ }
2638
+ }
2639
+
2285
2640
  .magicfeedback-upload-remove svg {
2286
2641
  width: 16px;
2287
2642
  height: 16px;
@@ -3028,10 +3383,14 @@ async function renderPopup(container, surveyId, productId, actions, emit, onClos
3028
3383
  // La pantalla final la pinta el popup: `renderSuccess` de @magicfeedback/native usa
3029
3384
  // textContent, así que el mensaje de la plataforma (HTML con imagen) no se vería, y
3030
3385
  // su fallback es un literal genérico que ignora `style.successMessage`.
3031
- addSuccessScreen: false
3386
+ addSuccessScreen: false,
3387
+ // Surveys multi-idioma: native pide a la API este idioma (el del init o el del
3388
+ // dispositivo) y, si el survey no lo tiene, la API sirve el idioma por defecto.
3389
+ lang: options?.language
3032
3390
  };
3033
- generateOptions.onLoadedEvent = ({ formData }) => {
3034
- const surveyLang = Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0;
3391
+ generateOptions.onLoadedEvent = ({ formData, lang }) => {
3392
+ const shownLang = typeof lang === "string" && lang.trim().length > 0 ? lang : void 0;
3393
+ const surveyLang = shownLang ?? (Array.isArray(formData?.lang) ? formData.lang.find((l) => typeof l === "string" && l.trim().length > 0) : void 0);
3035
3394
  if (surveyLang) applyLabels(surveyLang);
3036
3395
  try {
3037
3396
  } catch {
@@ -3407,7 +3766,7 @@ function trimmedOrNull(value) {
3407
3766
  }
3408
3767
  function buildSurveyHtml(opts) {
3409
3768
  const env = opts.env === "development" ? "dev" : "prod";
3410
- const version = opts.version ?? "2.2.22";
3769
+ const version = opts.version ?? "2.2.31";
3411
3770
  const cdn = `https://cdn.jsdelivr.net/npm/@magicfeedback/native@${version}/dist/magicfeedback-sdk.browser.js`;
3412
3771
  const sid = jsonForScript(opts.surveyId);
3413
3772
  const pid = jsonForScript(opts.productId);
@@ -3787,10 +4146,15 @@ ${REVEAL_JS}
3787
4146
  // textContent, as\xED que el mensaje de la plataforma (HTML con imagen) no se ver\xEDa, y su
3788
4147
  // fallback es un literal gen\xE9rico que ignora style.successMessage.
3789
4148
  addSuccessScreen:false,
4149
+ // Surveys multi-idioma: native pide a la API este idioma (el del init o el del
4150
+ // dispositivo); si el survey no lo tiene, la API sirve el idioma por defecto.
4151
+ lang:${initLangJson}||undefined,
3790
4152
  onLoadedEvent:function(args){
3791
- // Idioma del survey: manda sobre el del init para que el chrome no se quede en
3792
- // ingl\xE9s delante de un survey traducido.
3793
- var langs=args && args.formData ? args.formData.lang : null;
4153
+ // Idioma en el que native muestra el survey: manda sobre el del init para que el
4154
+ // chrome no se quede en ingl\xE9s delante de un survey traducido. Native < 2.3 no lo
4155
+ // env\xEDa: entonces el primero de formData.lang (el idioma por defecto).
4156
+ var shown=args && typeof args.lang==='string' && args.lang.trim() ? args.lang : null;
4157
+ var langs=shown ? [shown] : (args && args.formData ? args.formData.lang : null);
3794
4158
  if(langs && langs.length){
3795
4159
  for(var li=0;li<langs.length;li++){
3796
4160
  if(typeof langs[li]==='string' && langs[li].trim()){ ddApplyLabels(langs[li]); break; }
@@ -4376,8 +4740,10 @@ async function safeBodyText(res) {
4376
4740
  function createFeedbackSink(options) {
4377
4741
  let feedbackSessionId;
4378
4742
  let firstPostInFlight = null;
4743
+ let generation = 0;
4379
4744
  const url = `${options.baseUrl}/sdk/feedback`;
4380
4745
  const post = async (body, final, sessionEnd = false) => {
4746
+ const postGeneration = generation;
4381
4747
  const json = JSON.stringify(body);
4382
4748
  const small = json.length <= KEEPALIVE_MAX_BYTES;
4383
4749
  if (final) options.log?.("[DeepdotsAnalytics] final flush via fetch keepalive");
@@ -4407,9 +4773,25 @@ function createFeedbackSink(options) {
4407
4773
  );
4408
4774
  return;
4409
4775
  }
4410
- if (sessionEnd) return;
4776
+ if (sessionEnd) {
4777
+ let closedId = body.sessionId;
4778
+ try {
4779
+ const data = await res.json();
4780
+ closedId = data?.sessionId ?? closedId;
4781
+ } catch {
4782
+ }
4783
+ try {
4784
+ if (closedId) options.onSessionClosed?.(closedId);
4785
+ } catch {
4786
+ }
4787
+ return;
4788
+ }
4411
4789
  try {
4412
4790
  const data = await res.json();
4791
+ if (postGeneration !== generation) {
4792
+ options.log?.("[DeepdotsAnalytics] sessionId of a closed session ignored:", data?.sessionId);
4793
+ return;
4794
+ }
4413
4795
  if (data?.sessionId && data.sessionId !== feedbackSessionId) {
4414
4796
  feedbackSessionId = data.sessionId;
4415
4797
  options.log?.("[DeepdotsAnalytics] feedbackSessionId cached:", feedbackSessionId);
@@ -4422,6 +4804,7 @@ function createFeedbackSink(options) {
4422
4804
  if (meta?.resetSession) {
4423
4805
  feedbackSessionId = void 0;
4424
4806
  firstPostInFlight = null;
4807
+ generation++;
4425
4808
  options.onSessionReset?.();
4426
4809
  return;
4427
4810
  }
@@ -4436,6 +4819,7 @@ function createFeedbackSink(options) {
4436
4819
  if (sessionEnd) {
4437
4820
  feedbackSessionId = void 0;
4438
4821
  firstPostInFlight = null;
4822
+ generation++;
4439
4823
  options.onSessionReset?.();
4440
4824
  } else if (!feedbackSessionId) {
4441
4825
  firstPostInFlight = sent.then(
@@ -5086,6 +5470,8 @@ var DeepdotsPopups = class {
5086
5470
  this.analytics = null;
5087
5471
  /** feedbackSessionId cacheado del canal de analytics (devuelto por POST /sdk/feedback). */
5088
5472
  this.analyticsFeedbackSessionId = void 0;
5473
+ /** `init({ onFeedbackSession })`: avisa al host al abrir y cerrar cada registro. */
5474
+ this.onFeedbackSession = void 0;
5089
5475
  /** Timer del flush periódico de analytics (cada ANALYTICS_FLUSH_INTERVAL_MS). */
5090
5476
  this.analyticsFlushTimer = void 0;
5091
5477
  /** Crash & error reporting (#14–17). Null hasta init(). */
@@ -5143,6 +5529,7 @@ var DeepdotsPopups = class {
5143
5529
  this.renderChrome = config.renderChrome ?? true;
5144
5530
  this.showProgressBar = config.showProgressBar;
5145
5531
  this.surveyCss = config.surveyCss;
5532
+ this.onFeedbackSession = config.onFeedbackSession;
5146
5533
  this.config = {
5147
5534
  apiKey: config.apiKey || void 0,
5148
5535
  debug: config.debug || false,
@@ -5176,7 +5563,9 @@ var DeepdotsPopups = class {
5176
5563
  log: (...a) => this.log(...a),
5177
5564
  onSessionId: (id) => {
5178
5565
  this.analyticsFeedbackSessionId = id;
5566
+ this.notifyFeedbackSession(id, "open");
5179
5567
  },
5568
+ onSessionClosed: (id) => this.notifyFeedbackSession(id, "closed"),
5180
5569
  onSessionReset: () => {
5181
5570
  this.analyticsFeedbackSessionId = void 0;
5182
5571
  }
@@ -5251,6 +5640,23 @@ var DeepdotsPopups = class {
5251
5640
  getSessionId() {
5252
5641
  return this.tracking?.getSessionId() ?? null;
5253
5642
  }
5643
+ /**
5644
+ * Id del registro de analytics abierto: el `sessionId` que devolvió `POST /sdk/feedback`,
5645
+ * que la API guarda como `sdkSessionId` en el Feedback en que se convierte la sesión. No es
5646
+ * `getSessionId()`. Null hasta que el backend acepta el primer lote y de nuevo al cerrar la
5647
+ * sesión: para saber también qué registro se cerró, usa `init({ onFeedbackSession })`.
5648
+ */
5649
+ getFeedbackSessionId() {
5650
+ return this.analyticsFeedbackSessionId ?? null;
5651
+ }
5652
+ /** Avisa al host del registro abierto o cerrado; un fallo del host no corta el envío. */
5653
+ notifyFeedbackSession(sessionId, status) {
5654
+ try {
5655
+ this.onFeedbackSession?.({ sessionId, status });
5656
+ } catch (error) {
5657
+ this.log("analytics \xB7 onFeedbackSession threw:", error);
5658
+ }
5659
+ }
5254
5660
  /** Activa/desactiva el tracking (identidad + sesión + analytics). Kill-switch del contrato §7bis. */
5255
5661
  setTrackingEnabled(enabled) {
5256
5662
  if (!enabled) this.closeSession("tracking_disabled");