@redacto.io/consent-sdk-js 0.1.0 → 0.1.2-beta.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/redacto.js CHANGED
@@ -97,10 +97,49 @@ function applyAttrs(element, attrs) {
97
97
  }
98
98
  }
99
99
  }
100
+ var UNITLESS_PROPS = /* @__PURE__ */ new Set([
101
+ "animationIterationCount",
102
+ "aspectRatio",
103
+ "borderImageOutset",
104
+ "borderImageSlice",
105
+ "borderImageWidth",
106
+ "boxFlex",
107
+ "boxFlexGroup",
108
+ "boxOrdinalGroup",
109
+ "columnCount",
110
+ "columns",
111
+ "flex",
112
+ "flexGrow",
113
+ "flexPositive",
114
+ "flexShrink",
115
+ "flexNegative",
116
+ "flexOrder",
117
+ "fontWeight",
118
+ "gridArea",
119
+ "gridRow",
120
+ "gridRowEnd",
121
+ "gridRowSpan",
122
+ "gridRowStart",
123
+ "gridColumn",
124
+ "gridColumnEnd",
125
+ "gridColumnSpan",
126
+ "gridColumnStart",
127
+ "lineClamp",
128
+ "lineHeight",
129
+ "opacity",
130
+ "order",
131
+ "orphans",
132
+ "scale",
133
+ "tabSize",
134
+ "widows",
135
+ "zIndex",
136
+ "zoom"
137
+ ]);
100
138
  function applyStyles(element, styles2) {
101
139
  for (const [key, value] of Object.entries(styles2)) {
102
140
  if (value == null) continue;
103
- element.style[key] = typeof value === "number" ? `${value}px` : value;
141
+ const stringValue = typeof value === "number" && !UNITLESS_PROPS.has(key) ? `${value}px` : String(value);
142
+ element.style[key] = stringValue;
104
143
  }
105
144
  }
106
145
  function appendChildren(parent, children) {
@@ -578,19 +617,8 @@ var verifyGuardianStatus = async ({
578
617
  };
579
618
 
580
619
  // src/styles/inject.ts
581
- var stylesInjected = false;
582
- var injectCheckboxStyles = () => {
583
- if (stylesInjected || typeof document === "undefined") {
584
- return;
585
- }
586
- const STYLE_ID = "redacto-consent-checkbox-styles";
587
- if (document.getElementById(STYLE_ID)) {
588
- stylesInjected = true;
589
- return;
590
- }
591
- const style = document.createElement("style");
592
- style.id = STYLE_ID;
593
- style.textContent = `
620
+ var STYLE_ID = "redacto-consent-checkbox-styles";
621
+ var STYLE_CONTENT = `
594
622
  /* Redacto Consent SDK Checkbox Styles */
595
623
  .redacto-checkbox-large {
596
624
  appearance: none !important;
@@ -721,8 +749,18 @@ var injectCheckboxStyles = () => {
721
749
  background-color: #2563eb !important;
722
750
  }
723
751
  `;
724
- document.head.appendChild(style);
725
- stylesInjected = true;
752
+ var injectCheckboxStyles = (root) => {
753
+ if (typeof document === "undefined") return;
754
+ const existing = root instanceof Document ? root.getElementById(STYLE_ID) : root.querySelector(`#${STYLE_ID}`);
755
+ if (existing) return;
756
+ const style = document.createElement("style");
757
+ style.id = STYLE_ID;
758
+ style.textContent = STYLE_CONTENT;
759
+ if (root instanceof Document) {
760
+ root.head.appendChild(style);
761
+ } else {
762
+ root.appendChild(style);
763
+ }
726
764
  };
727
765
 
728
766
  // src/styles/modal.ts
@@ -777,7 +815,8 @@ var styles = {
777
815
  topSection: {
778
816
  display: "flex",
779
817
  justifyContent: "space-between",
780
- alignItems: "center",
818
+ alignItems: "flex-start",
819
+ gap: "10px",
781
820
  paddingBottom: "15px",
782
821
  borderBottom: "none",
783
822
  backgroundColor: "#ffffff",
@@ -789,6 +828,8 @@ var styles = {
789
828
  display: "flex",
790
829
  alignItems: "center",
791
830
  gap: "10px",
831
+ flex: "1 1 0",
832
+ minWidth: 0,
792
833
  margin: 0,
793
834
  padding: 0,
794
835
  boxSizing: "border-box"
@@ -796,7 +837,9 @@ var styles = {
796
837
  logo: {
797
838
  height: "32px",
798
839
  width: "auto",
840
+ maxWidth: "140px",
799
841
  objectFit: "contain",
842
+ flexShrink: 0,
800
843
  margin: 0,
801
844
  padding: 0,
802
845
  display: "block"
@@ -808,6 +851,10 @@ var styles = {
808
851
  letterSpacing: "0.2px",
809
852
  verticalAlign: "middle",
810
853
  color: "#101828",
854
+ flex: "1 1 auto",
855
+ minWidth: 0,
856
+ wordBreak: "normal",
857
+ overflowWrap: "anywhere",
811
858
  margin: 0,
812
859
  padding: 0,
813
860
  textAlign: "left"
@@ -819,6 +866,7 @@ var styles = {
819
866
  display: "flex",
820
867
  alignItems: "center",
821
868
  justifyContent: "center",
869
+ flexShrink: 0,
822
870
  fontWeight: 400,
823
871
  fontSize: "12px",
824
872
  lineHeight: "150%",
@@ -1303,12 +1351,41 @@ function createGreenCheckmark(size, ariaLabel) {
1303
1351
  }
1304
1352
 
1305
1353
  // src/modal/language-selector.ts
1354
+ var LANGUAGE_NATIVE_LABELS = {
1355
+ English: "English",
1356
+ Assamese: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)",
1357
+ Bengali: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)",
1358
+ Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
1359
+ Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
1360
+ "Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
1361
+ Gujarati: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)",
1362
+ Hindi: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)",
1363
+ Kannada: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)",
1364
+ Kashmiri: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)",
1365
+ Maithili: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)",
1366
+ Malayalam: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)",
1367
+ Manipuri: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)",
1368
+ Marathi: "\u092E\u0930\u093E\u0920\u0940 (Marathi)",
1369
+ Nepali: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)",
1370
+ Odia: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)",
1371
+ Punjabi: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)",
1372
+ Sanskrit: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)",
1373
+ Santali: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)",
1374
+ Sindhi: "\u0633\u0646\u068C\u064A (Sindhi)",
1375
+ Tamil: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)",
1376
+ Telugu: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)",
1377
+ Urdu: "\u0627\u0631\u062F\u0648 (Urdu)"
1378
+ };
1379
+ var toNativeLabel = (name) => {
1380
+ var _a;
1381
+ return (_a = LANGUAGE_NATIVE_LABELS[name]) != null ? _a : name;
1382
+ };
1306
1383
  function renderLanguageSelector(options) {
1307
1384
  var _a, _b, _c, _d;
1308
- const { languages, selectedLanguage, settings, onSelect } = options;
1385
+ const { languages, selectedLanguage, settings, onSelect, dropdownContainer } = options;
1309
1386
  let isOpen = false;
1310
1387
  const buttonText = el("span");
1311
- buttonText.textContent = selectedLanguage;
1388
+ buttonText.textContent = toNativeLabel(selectedLanguage);
1312
1389
  const chevronSvg = svg(
1313
1390
  "svg",
1314
1391
  {
@@ -1337,7 +1414,7 @@ function renderLanguageSelector(options) {
1337
1414
  tabIndex: "0",
1338
1415
  "aria-haspopup": "listbox",
1339
1416
  "aria-expanded": "false",
1340
- "aria-label": `Language selector, current: ${selectedLanguage}`,
1417
+ "aria-label": `Language selector, current: ${toNativeLabel(selectedLanguage)}`,
1341
1418
  onClick: () => toggleDropdown(),
1342
1419
  onKeydown: (e) => {
1343
1420
  if (e.key === "Enter" || e.key === " ") {
@@ -1394,13 +1471,11 @@ function renderLanguageSelector(options) {
1394
1471
  e.target.style.backgroundColor = isSelected ? "#f3f4f6" : "#ffffff";
1395
1472
  }
1396
1473
  });
1397
- item.textContent = lang;
1474
+ item.textContent = toNativeLabel(lang);
1398
1475
  dropdown.appendChild(item);
1399
1476
  }
1400
1477
  const container = el("div", { style: styles.languageSelectorContainer }, button);
1401
- if (typeof document !== "undefined") {
1402
- document.body.appendChild(dropdown);
1403
- }
1478
+ dropdownContainer.appendChild(dropdown);
1404
1479
  function positionDropdown() {
1405
1480
  const rect = button.getBoundingClientRect();
1406
1481
  dropdown.style.top = `${rect.bottom + 4}px`;
@@ -1408,10 +1483,14 @@ function renderLanguageSelector(options) {
1408
1483
  dropdown.style.left = "auto";
1409
1484
  }
1410
1485
  const clickOutsideHandler = (e) => {
1411
- const target = e.target;
1412
- if (!container.contains(target) && !dropdown.contains(target)) {
1413
- closeDropdown();
1414
- }
1486
+ var _a2, _b2;
1487
+ const path = (_b2 = (_a2 = e.composedPath) == null ? void 0 : _a2.call(e)) != null ? _b2 : [];
1488
+ if (path.includes(container) || path.includes(dropdown)) return;
1489
+ if (path.length === 0) {
1490
+ const target = e.target;
1491
+ if (container.contains(target) || dropdown.contains(target)) return;
1492
+ }
1493
+ closeDropdown();
1415
1494
  };
1416
1495
  const repositionHandler = () => {
1417
1496
  if (isOpen) positionDropdown();
@@ -1444,6 +1523,7 @@ function renderLanguageSelector(options) {
1444
1523
  // src/modal/keyboard.ts
1445
1524
  function setupKeyboardTrap(modalEl, onEscape) {
1446
1525
  const handler = (e) => {
1526
+ var _a;
1447
1527
  if (e.key === "Escape") {
1448
1528
  e.preventDefault();
1449
1529
  onEscape();
@@ -1462,13 +1542,15 @@ function setupKeyboardTrap(modalEl, onEscape) {
1462
1542
  }
1463
1543
  const first = focusable[0];
1464
1544
  const last = focusable[focusable.length - 1];
1545
+ const root = modalEl.getRootNode();
1546
+ const active = (_a = root.activeElement) != null ? _a : document.activeElement;
1465
1547
  if (e.shiftKey) {
1466
- if (document.activeElement === first) {
1548
+ if (active === first) {
1467
1549
  e.preventDefault();
1468
1550
  last.focus();
1469
1551
  }
1470
1552
  } else {
1471
- if (document.activeElement === last) {
1553
+ if (active === last) {
1472
1554
  e.preventDefault();
1473
1555
  first.focus();
1474
1556
  }
@@ -1558,7 +1640,15 @@ var DPO_AUDIO_ELEMENT_MAP = [
1558
1640
 
1559
1641
  // src/modal/tts.ts
1560
1642
  var TTSManager = class {
1561
- constructor(onStateChange) {
1643
+ /**
1644
+ * @param onStateChange Invoked whenever playback state changes so the host
1645
+ * can refresh its UI (e.g. swap the play/pause icon).
1646
+ * @param root The root in which to look up highlightable elements by id.
1647
+ * When the modal is rendered inside a shadow root, pass that shadow root
1648
+ * so queries stay scoped to the SDK's tree (and don't accidentally hit
1649
+ * element ids that the host page also defines).
1650
+ */
1651
+ constructor(onStateChange, root) {
1562
1652
  this.audioUrls = null;
1563
1653
  this.audioSequence = [];
1564
1654
  this.elementIdSequence = [];
@@ -1570,6 +1660,7 @@ var TTSManager = class {
1570
1660
  this.highlightedElementId = null;
1571
1661
  this.abortController = null;
1572
1662
  this.onStateChange = onStateChange;
1663
+ this.root = root;
1573
1664
  }
1574
1665
  get isPlaying() {
1575
1666
  return this._isPlaying;
@@ -1582,9 +1673,11 @@ var TTSManager = class {
1582
1673
  }
1583
1674
  async fetchAudioUrls(params) {
1584
1675
  var _a;
1585
- this.stop();
1676
+ this.stopInternal();
1586
1677
  this._isAvailable = false;
1587
1678
  this.audioUrls = null;
1679
+ this.audioSequence = [];
1680
+ this.elementIdSequence = [];
1588
1681
  this.onStateChange();
1589
1682
  (_a = this.abortController) == null ? void 0 : _a.abort();
1590
1683
  this.abortController = new AbortController();
@@ -1597,13 +1690,15 @@ var TTSManager = class {
1597
1690
  signal: this.abortController.signal
1598
1691
  });
1599
1692
  this.audioUrls = response.detail;
1600
- this._isAvailable = true;
1601
1693
  this.buildSequences(params.content);
1694
+ this._isAvailable = this.audioSequence.length > 0;
1602
1695
  this.onStateChange();
1603
1696
  } catch (error) {
1604
1697
  if (error.name !== "AbortError") {
1605
1698
  this._isAvailable = false;
1606
1699
  this.audioUrls = null;
1700
+ this.audioSequence = [];
1701
+ this.elementIdSequence = [];
1607
1702
  this.onStateChange();
1608
1703
  }
1609
1704
  }
@@ -1677,6 +1772,16 @@ var TTSManager = class {
1677
1772
  }
1678
1773
  }
1679
1774
  stop() {
1775
+ this.stopInternal();
1776
+ this.onStateChange();
1777
+ }
1778
+ /**
1779
+ * Internal stop used by code paths (like fetchAudioUrls) that will fire
1780
+ * their own onStateChange after composing a richer state update — we don't
1781
+ * want the intermediate notification to flicker the UI based on a half-set
1782
+ * state (e.g. _isAvailable still true from the previous language).
1783
+ */
1784
+ stopInternal() {
1680
1785
  if (this.currentAudio) {
1681
1786
  this.currentAudio.pause();
1682
1787
  this.currentAudio.currentTime = 0;
@@ -1687,7 +1792,6 @@ var TTSManager = class {
1687
1792
  this.currentIndex = 0;
1688
1793
  this.removeHighlights();
1689
1794
  this.highlightedElementId = null;
1690
- this.onStateChange();
1691
1795
  }
1692
1796
  destroy() {
1693
1797
  var _a;
@@ -1728,7 +1832,7 @@ var TTSManager = class {
1728
1832
  this.removeHighlights();
1729
1833
  this.highlightedElementId = elementId;
1730
1834
  setTimeout(() => {
1731
- const element = document.getElementById(elementId);
1835
+ const element = "getElementById" in this.root ? this.root.getElementById(elementId) : this.root.querySelector(`#${CSS.escape(elementId)}`);
1732
1836
  if (element) {
1733
1837
  element.classList.add("redacto-tts-highlight");
1734
1838
  const rect = element.getBoundingClientRect();
@@ -1744,7 +1848,7 @@ var TTSManager = class {
1744
1848
  }, 50);
1745
1849
  }
1746
1850
  removeHighlights() {
1747
- document.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1851
+ this.root.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1748
1852
  el2.classList.remove("redacto-tts-highlight");
1749
1853
  });
1750
1854
  }
@@ -2551,6 +2655,10 @@ function resolveColors(settings, activeConfig) {
2551
2655
  // src/modal/index.ts
2552
2656
  var ModalController = class {
2553
2657
  constructor(options) {
2658
+ this.hostEl = null;
2659
+ this.shadowRoot = null;
2660
+ this.previousBodyOverflow = null;
2661
+ this.previousHtmlOverflow = null;
2554
2662
  this.overlayEl = null;
2555
2663
  this.modalEl = null;
2556
2664
  this.purposeRefs = null;
@@ -2559,6 +2667,7 @@ var ModalController = class {
2559
2667
  this.keyboardCleanup = null;
2560
2668
  this.abortController = null;
2561
2669
  this.unsubscribe = null;
2670
+ this.ttsManager = null;
2562
2671
  this.guardianFormRefs = null;
2563
2672
  this.statusPollTimeout = null;
2564
2673
  this.popupCheckInterval = null;
@@ -2610,10 +2719,8 @@ var ModalController = class {
2610
2719
  guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
2611
2720
  });
2612
2721
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
2613
- this.ttsManager = new TTSManager(() => this.updateTTSButton());
2614
2722
  }
2615
2723
  async init() {
2616
- injectCheckboxStyles();
2617
2724
  this.unsubscribe = this.store.subscribe((state, prevState) => {
2618
2725
  this.handleStateChange(state, prevState);
2619
2726
  });
@@ -2627,18 +2734,22 @@ var ModalController = class {
2627
2734
  (_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
2628
2735
  (_c = this.unsubscribe) == null ? void 0 : _c.call(this);
2629
2736
  this.mediaQuery.destroy();
2630
- this.ttsManager.destroy();
2737
+ (_d = this.ttsManager) == null ? void 0 : _d.destroy();
2631
2738
  this.stopPolling();
2632
2739
  this.store.destroy();
2633
- if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
2634
- document.body.removeChild(this.langSelectorRefs.dropdown);
2740
+ if (this.hostEl && this.hostEl.parentNode) {
2741
+ this.hostEl.parentNode.removeChild(this.hostEl);
2635
2742
  }
2636
- if (this.overlayEl && document.body.contains(this.overlayEl)) {
2637
- document.body.removeChild(this.overlayEl);
2743
+ if (this.previousBodyOverflow !== null) {
2744
+ document.body.style.overflow = this.previousBodyOverflow;
2745
+ this.previousBodyOverflow = null;
2638
2746
  }
2639
- if (this.modalEl && document.body.contains(this.modalEl)) {
2640
- document.body.removeChild(this.modalEl);
2747
+ if (this.previousHtmlOverflow !== null) {
2748
+ document.documentElement.style.overflow = this.previousHtmlOverflow;
2749
+ this.previousHtmlOverflow = null;
2641
2750
  }
2751
+ this.hostEl = null;
2752
+ this.shadowRoot = null;
2642
2753
  this.overlayEl = null;
2643
2754
  this.modalEl = null;
2644
2755
  this.purposeRefs = null;
@@ -2807,13 +2918,32 @@ var ModalController = class {
2807
2918
  }
2808
2919
  }
2809
2920
  renderOverlayWithLoading() {
2921
+ this.hostEl = document.createElement("div");
2922
+ this.hostEl.setAttribute("data-redacto-consent", "");
2923
+ this.hostEl.style.position = "fixed";
2924
+ this.hostEl.style.top = "0";
2925
+ this.hostEl.style.left = "0";
2926
+ this.hostEl.style.width = "0";
2927
+ this.hostEl.style.height = "0";
2928
+ this.hostEl.style.zIndex = "2147483646";
2929
+ document.body.appendChild(this.hostEl);
2930
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
2931
+ this.previousBodyOverflow = document.body.style.overflow;
2932
+ this.previousHtmlOverflow = document.documentElement.style.overflow;
2933
+ document.body.style.overflow = "hidden";
2934
+ document.documentElement.style.overflow = "hidden";
2935
+ injectCheckboxStyles(this.shadowRoot);
2936
+ this.ttsManager = new TTSManager(
2937
+ () => this.updateTTSButton(),
2938
+ this.shadowRoot
2939
+ );
2810
2940
  const blockUI = this.options.blockUI !== false;
2811
2941
  if (blockUI) {
2812
2942
  this.overlayEl = el("div", {
2813
2943
  style: styles.overlay,
2814
2944
  role: "presentation"
2815
2945
  });
2816
- document.body.appendChild(this.overlayEl);
2946
+ this.shadowRoot.appendChild(this.overlayEl);
2817
2947
  }
2818
2948
  const settingsObj = this.options.settings;
2819
2949
  const modalStyle = { ...styles.modal };
@@ -2839,10 +2969,10 @@ var ModalController = class {
2839
2969
  );
2840
2970
  const contentEl = el("div", { style: styles.content }, loadingEl);
2841
2971
  this.modalEl.appendChild(contentEl);
2842
- document.body.appendChild(this.modalEl);
2972
+ this.shadowRoot.appendChild(this.modalEl);
2843
2973
  }
2844
2974
  rebuildUI() {
2845
- var _a, _b, _c, _d, _e, _f, _g;
2975
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2846
2976
  const state = this.store.getState();
2847
2977
  if (!state.content || !this.modalEl) return;
2848
2978
  const activeConfig = state.content.detail.active_config;
@@ -2923,15 +3053,22 @@ var ModalController = class {
2923
3053
  const textColor = colors.text;
2924
3054
  const linkColor = colors.link;
2925
3055
  const font = colors.font;
2926
- const logoImg = activeConfig.logo_url ? el("img", { src: activeConfig.logo_url, style: styles.logo, alt: "Logo" }) : null;
3056
+ const logoImg = activeConfig.logo_url ? el("img", {
3057
+ src: activeConfig.logo_url,
3058
+ style: {
3059
+ ...styles.logo,
3060
+ maxWidth: isMobile ? "96px" : "140px",
3061
+ maxHeight: isMobile ? "28px" : "32px"
3062
+ },
3063
+ alt: "Logo"
3064
+ }) : null;
2927
3065
  this.noticeTitleEl = el("span", {
2928
3066
  id: "privacy-notice-title",
2929
3067
  style: { ...styles.title, color: headingColor, fontFamily: font, fontSize: isMobile ? "16px" : "18px" }
2930
3068
  });
2931
3069
  this.noticeTitleEl.textContent = this.getTranslatedText("notice_banner_heading", activeConfig.notice_banner_heading);
2932
- const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
2933
- if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
2934
- document.body.removeChild(this.langSelectorRefs.dropdown);
3070
+ if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && this.shadowRoot && this.langSelectorRefs.dropdown.parentNode === this.shadowRoot) {
3071
+ this.shadowRoot.removeChild(this.langSelectorRefs.dropdown);
2935
3072
  }
2936
3073
  const supportedLangs = Object.keys(activeConfig.supported_languages_and_translations || {});
2937
3074
  const allLanguages = [activeConfig.default_language || "English", ...supportedLangs.filter((l) => l !== activeConfig.default_language)];
@@ -2939,7 +3076,8 @@ var ModalController = class {
2939
3076
  languages: allLanguages,
2940
3077
  selectedLanguage: state.selectedLanguage,
2941
3078
  settings: settingsObj,
2942
- onSelect: (lang) => this.handleLanguageSelect(lang)
3079
+ onSelect: (lang) => this.handleLanguageSelect(lang),
3080
+ dropdownContainer: this.shadowRoot
2943
3081
  });
2944
3082
  const ttsStrokeColor = ((_f = (_e = settingsObj == null ? void 0 : settingsObj.button) == null ? void 0 : _e.language) == null ? void 0 : _f.textColor) || "currentColor";
2945
3083
  const speakerSvg = svg(
@@ -2970,10 +3108,13 @@ var ModalController = class {
2970
3108
  type: "button",
2971
3109
  "aria-label": "Start audio playback",
2972
3110
  title: "Start Talkback",
2973
- onClick: () => this.ttsManager.toggle()
3111
+ onClick: () => {
3112
+ var _a2;
3113
+ return (_a2 = this.ttsManager) == null ? void 0 : _a2.toggle();
3114
+ }
2974
3115
  });
2975
3116
  this.ttsButton.appendChild(speakerSvg);
2976
- this.ttsManager.fetchAudioUrls({
3117
+ (_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
2977
3118
  accessToken: this.options.accessToken,
2978
3119
  baseUrl: this.options.baseUrl,
2979
3120
  noticeUuid: activeConfig.notice_uuid,
@@ -2981,9 +3122,44 @@ var ModalController = class {
2981
3122
  content: activeConfig
2982
3123
  });
2983
3124
  const topRight = el("div", {
2984
- style: { display: "flex", alignItems: "center", gap: "8px" }
3125
+ style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
2985
3126
  }, this.ttsButton, this.langSelectorRefs.container);
2986
- const topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3127
+ let topSection;
3128
+ if (isMobile) {
3129
+ const headerRow = el(
3130
+ "div",
3131
+ {
3132
+ style: {
3133
+ display: "flex",
3134
+ alignItems: "center",
3135
+ gap: "10px",
3136
+ width: "100%"
3137
+ }
3138
+ },
3139
+ ...logoImg ? [logoImg] : [],
3140
+ el(
3141
+ "div",
3142
+ { style: { marginLeft: "auto", display: "flex", alignItems: "center" } },
3143
+ topRight
3144
+ )
3145
+ );
3146
+ topSection = el(
3147
+ "div",
3148
+ {
3149
+ style: {
3150
+ ...styles.topSection,
3151
+ flexDirection: "column",
3152
+ alignItems: "stretch",
3153
+ flexWrap: "nowrap"
3154
+ }
3155
+ },
3156
+ headerRow,
3157
+ this.noticeTitleEl
3158
+ );
3159
+ } else {
3160
+ const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
3161
+ topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3162
+ }
2987
3163
  this.middleSection = el("div", { style: styles.middleSection });
2988
3164
  const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
2989
3165
  const noticeTextSpan = el("span", { id: "privacy-notice-text" });
@@ -3228,7 +3404,7 @@ var ModalController = class {
3228
3404
  );
3229
3405
  this.modalEl.appendChild(contentWrapper);
3230
3406
  this.updateAcceptButtonState(state);
3231
- (_g = this.keyboardCleanup) == null ? void 0 : _g.call(this);
3407
+ (_h = this.keyboardCleanup) == null ? void 0 : _h.call(this);
3232
3408
  this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
3233
3409
  }
3234
3410
  getLanguageCode(displayLanguage) {
@@ -3786,54 +3962,56 @@ var ModalController = class {
3786
3962
  // ─── TTS ───
3787
3963
  updateTTSButton() {
3788
3964
  var _a, _b, _c;
3789
- if (!this.ttsButton) return;
3790
- if (this.ttsManager.isAvailable) {
3791
- this.ttsButton.style.display = "flex";
3792
- clearElement(this.ttsButton);
3793
- const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3794
- if (this.ttsManager.isPlaying) {
3795
- const pauseSvg = svg(
3796
- "svg",
3797
- {
3798
- xmlns: "http://www.w3.org/2000/svg",
3799
- width: "16",
3800
- height: "16",
3801
- viewBox: "0 0 24 24",
3802
- fill: "none",
3803
- stroke: strokeColor,
3804
- "stroke-width": "2",
3805
- "stroke-linecap": "round",
3806
- "stroke-linejoin": "round",
3807
- "aria-hidden": "true"
3808
- },
3809
- svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3810
- svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3811
- );
3812
- this.ttsButton.appendChild(pauseSvg);
3813
- this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3814
- this.ttsButton.setAttribute("title", "Pause Talkback");
3815
- } else {
3816
- const speakerSvg = svg(
3817
- "svg",
3818
- {
3819
- xmlns: "http://www.w3.org/2000/svg",
3820
- width: "16",
3821
- height: "16",
3822
- viewBox: "0 0 24 24",
3823
- fill: "none",
3824
- stroke: strokeColor,
3825
- "stroke-width": "2",
3826
- "stroke-linecap": "round",
3827
- "stroke-linejoin": "round",
3828
- "aria-hidden": "true"
3829
- },
3830
- svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
3831
- svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
3832
- );
3833
- this.ttsButton.appendChild(speakerSvg);
3834
- this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
3835
- this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3836
- }
3965
+ if (!this.ttsButton || !this.ttsManager) return;
3966
+ if (!this.ttsManager.isAvailable) {
3967
+ this.ttsButton.style.display = "none";
3968
+ return;
3969
+ }
3970
+ this.ttsButton.style.display = "flex";
3971
+ clearElement(this.ttsButton);
3972
+ const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3973
+ if (this.ttsManager.isPlaying) {
3974
+ const pauseSvg = svg(
3975
+ "svg",
3976
+ {
3977
+ xmlns: "http://www.w3.org/2000/svg",
3978
+ width: "16",
3979
+ height: "16",
3980
+ viewBox: "0 0 24 24",
3981
+ fill: "none",
3982
+ stroke: strokeColor,
3983
+ "stroke-width": "2",
3984
+ "stroke-linecap": "round",
3985
+ "stroke-linejoin": "round",
3986
+ "aria-hidden": "true"
3987
+ },
3988
+ svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3989
+ svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3990
+ );
3991
+ this.ttsButton.appendChild(pauseSvg);
3992
+ this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3993
+ this.ttsButton.setAttribute("title", "Pause Talkback");
3994
+ } else {
3995
+ const speakerSvg = svg(
3996
+ "svg",
3997
+ {
3998
+ xmlns: "http://www.w3.org/2000/svg",
3999
+ width: "16",
4000
+ height: "16",
4001
+ viewBox: "0 0 24 24",
4002
+ fill: "none",
4003
+ stroke: strokeColor,
4004
+ "stroke-width": "2",
4005
+ "stroke-linecap": "round",
4006
+ "stroke-linejoin": "round",
4007
+ "aria-hidden": "true"
4008
+ },
4009
+ svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
4010
+ svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
4011
+ );
4012
+ this.ttsButton.appendChild(speakerSvg);
4013
+ this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
4014
+ this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3837
4015
  }
3838
4016
  }
3839
4017
  updateButtonStates(state) {
@@ -4153,6 +4331,8 @@ function renderInlinePurposes(purposes, options) {
4153
4331
  var InlineController = class {
4154
4332
  constructor(options) {
4155
4333
  this.targetEl = null;
4334
+ this.hostEl = null;
4335
+ this.shadowRoot = null;
4156
4336
  this.rootEl = null;
4157
4337
  this.purposeRefs = null;
4158
4338
  this.errorEl = null;
@@ -4174,7 +4354,6 @@ var InlineController = class {
4174
4354
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
4175
4355
  }
4176
4356
  async init() {
4177
- injectCheckboxStyles();
4178
4357
  if (typeof this.options.target === "string") {
4179
4358
  this.targetEl = document.querySelector(this.options.target);
4180
4359
  if (!this.targetEl) {
@@ -4183,6 +4362,12 @@ var InlineController = class {
4183
4362
  } else {
4184
4363
  this.targetEl = this.options.target;
4185
4364
  }
4365
+ this.hostEl = document.createElement("div");
4366
+ this.hostEl.setAttribute("data-redacto-consent-inline", "");
4367
+ this.hostEl.style.display = "block";
4368
+ this.targetEl.appendChild(this.hostEl);
4369
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
4370
+ injectCheckboxStyles(this.shadowRoot);
4186
4371
  this.unsubscribe = this.store.subscribe((state, prevState) => {
4187
4372
  this.handleStateChange(state, prevState);
4188
4373
  });
@@ -4195,9 +4380,11 @@ var InlineController = class {
4195
4380
  (_a = this.unsubscribe) == null ? void 0 : _a.call(this);
4196
4381
  this.mediaQuery.destroy();
4197
4382
  this.store.destroy();
4198
- if (this.targetEl && this.rootEl) {
4199
- this.targetEl.removeChild(this.rootEl);
4383
+ if (this.hostEl && this.hostEl.parentNode) {
4384
+ this.hostEl.parentNode.removeChild(this.hostEl);
4200
4385
  }
4386
+ this.hostEl = null;
4387
+ this.shadowRoot = null;
4201
4388
  this.rootEl = null;
4202
4389
  this.purposeRefs = null;
4203
4390
  }
@@ -4253,8 +4440,8 @@ var InlineController = class {
4253
4440
  this.rebuildUI();
4254
4441
  }
4255
4442
  if (state.hasAlreadyConsented || state.fetchError) {
4256
- if (this.rootEl && this.targetEl) {
4257
- this.targetEl.removeChild(this.rootEl);
4443
+ if (this.rootEl && this.shadowRoot && this.rootEl.parentNode === this.shadowRoot) {
4444
+ this.shadowRoot.removeChild(this.rootEl);
4258
4445
  this.rootEl = null;
4259
4446
  }
4260
4447
  return;
@@ -4273,7 +4460,7 @@ var InlineController = class {
4273
4460
  }
4274
4461
  renderLoading() {
4275
4462
  var _a;
4276
- if (!this.targetEl) return;
4463
+ if (!this.shadowRoot) return;
4277
4464
  const textColor = ((_a = this.options.settings) == null ? void 0 : _a.textColor) || "#344054";
4278
4465
  this.rootEl = el(
4279
4466
  "div",
@@ -4281,13 +4468,13 @@ var InlineController = class {
4281
4468
  el("div", { style: inlineStyles.loadingSpinner }),
4282
4469
  el("p", { style: { fontSize: "16px", fontWeight: 400, color: textColor } }, "Loading...")
4283
4470
  );
4284
- this.targetEl.appendChild(this.rootEl);
4471
+ this.shadowRoot.appendChild(this.rootEl);
4285
4472
  }
4286
4473
  rebuildUI() {
4287
4474
  const state = this.store.getState();
4288
- if (!state.content || !this.targetEl) return;
4289
- if (this.rootEl && this.targetEl.contains(this.rootEl)) {
4290
- this.targetEl.removeChild(this.rootEl);
4475
+ if (!state.content || !this.shadowRoot) return;
4476
+ if (this.rootEl && this.rootEl.parentNode === this.shadowRoot) {
4477
+ this.shadowRoot.removeChild(this.rootEl);
4291
4478
  }
4292
4479
  this.errorEl = el("div", {
4293
4480
  role: "alert",
@@ -4321,7 +4508,7 @@ var InlineController = class {
4321
4508
  this.errorEl,
4322
4509
  this.purposeRefs.container
4323
4510
  );
4324
- this.targetEl.appendChild(this.rootEl);
4511
+ this.shadowRoot.appendChild(this.rootEl);
4325
4512
  this.notifyValidation(state);
4326
4513
  }
4327
4514
  getTranslatedText(key, defaultText, itemId) {
@@ -4548,7 +4735,7 @@ function getErrorMessage(errorCode) {
4548
4735
  }
4549
4736
  function handleDigilockerCallback(options) {
4550
4737
  var _a, _b, _c;
4551
- injectCheckboxStyles();
4738
+ injectCheckboxStyles(document);
4552
4739
  const params = new URLSearchParams(window.location.search);
4553
4740
  const sessionToken = params.get("session_token");
4554
4741
  const urlStatus = params.get("status");