@redacto.io/consent-sdk-js 0.1.0 → 0.1.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.
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,42 @@ 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
+ Bhojpuri: "\u092D\u094B\u091C\u092A\u0941\u0930\u0940 (Bhojpuri)",
1359
+ Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
1360
+ Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
1361
+ "Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
1362
+ Gujarati: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)",
1363
+ Hindi: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)",
1364
+ Kannada: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)",
1365
+ Kashmiri: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)",
1366
+ Maithili: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)",
1367
+ Malayalam: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)",
1368
+ Manipuri: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)",
1369
+ Marathi: "\u092E\u0930\u093E\u0920\u0940 (Marathi)",
1370
+ Nepali: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)",
1371
+ Odia: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)",
1372
+ Punjabi: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)",
1373
+ Sanskrit: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)",
1374
+ Santali: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)",
1375
+ Sindhi: "\u0633\u0646\u068C\u064A (Sindhi)",
1376
+ Tamil: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)",
1377
+ Telugu: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)",
1378
+ Urdu: "\u0627\u0631\u062F\u0648 (Urdu)"
1379
+ };
1380
+ var toNativeLabel = (name) => {
1381
+ var _a;
1382
+ return (_a = LANGUAGE_NATIVE_LABELS[name]) != null ? _a : name;
1383
+ };
1306
1384
  function renderLanguageSelector(options) {
1307
1385
  var _a, _b, _c, _d;
1308
- const { languages, selectedLanguage, settings, onSelect } = options;
1386
+ const { languages, selectedLanguage, settings, onSelect, dropdownContainer } = options;
1309
1387
  let isOpen = false;
1310
1388
  const buttonText = el("span");
1311
- buttonText.textContent = selectedLanguage;
1389
+ buttonText.textContent = toNativeLabel(selectedLanguage);
1312
1390
  const chevronSvg = svg(
1313
1391
  "svg",
1314
1392
  {
@@ -1337,7 +1415,7 @@ function renderLanguageSelector(options) {
1337
1415
  tabIndex: "0",
1338
1416
  "aria-haspopup": "listbox",
1339
1417
  "aria-expanded": "false",
1340
- "aria-label": `Language selector, current: ${selectedLanguage}`,
1418
+ "aria-label": `Language selector, current: ${toNativeLabel(selectedLanguage)}`,
1341
1419
  onClick: () => toggleDropdown(),
1342
1420
  onKeydown: (e) => {
1343
1421
  if (e.key === "Enter" || e.key === " ") {
@@ -1394,13 +1472,11 @@ function renderLanguageSelector(options) {
1394
1472
  e.target.style.backgroundColor = isSelected ? "#f3f4f6" : "#ffffff";
1395
1473
  }
1396
1474
  });
1397
- item.textContent = lang;
1475
+ item.textContent = toNativeLabel(lang);
1398
1476
  dropdown.appendChild(item);
1399
1477
  }
1400
1478
  const container = el("div", { style: styles.languageSelectorContainer }, button);
1401
- if (typeof document !== "undefined") {
1402
- document.body.appendChild(dropdown);
1403
- }
1479
+ dropdownContainer.appendChild(dropdown);
1404
1480
  function positionDropdown() {
1405
1481
  const rect = button.getBoundingClientRect();
1406
1482
  dropdown.style.top = `${rect.bottom + 4}px`;
@@ -1408,10 +1484,14 @@ function renderLanguageSelector(options) {
1408
1484
  dropdown.style.left = "auto";
1409
1485
  }
1410
1486
  const clickOutsideHandler = (e) => {
1411
- const target = e.target;
1412
- if (!container.contains(target) && !dropdown.contains(target)) {
1413
- closeDropdown();
1414
- }
1487
+ var _a2, _b2;
1488
+ const path = (_b2 = (_a2 = e.composedPath) == null ? void 0 : _a2.call(e)) != null ? _b2 : [];
1489
+ if (path.includes(container) || path.includes(dropdown)) return;
1490
+ if (path.length === 0) {
1491
+ const target = e.target;
1492
+ if (container.contains(target) || dropdown.contains(target)) return;
1493
+ }
1494
+ closeDropdown();
1415
1495
  };
1416
1496
  const repositionHandler = () => {
1417
1497
  if (isOpen) positionDropdown();
@@ -1444,6 +1524,7 @@ function renderLanguageSelector(options) {
1444
1524
  // src/modal/keyboard.ts
1445
1525
  function setupKeyboardTrap(modalEl, onEscape) {
1446
1526
  const handler = (e) => {
1527
+ var _a;
1447
1528
  if (e.key === "Escape") {
1448
1529
  e.preventDefault();
1449
1530
  onEscape();
@@ -1462,13 +1543,15 @@ function setupKeyboardTrap(modalEl, onEscape) {
1462
1543
  }
1463
1544
  const first = focusable[0];
1464
1545
  const last = focusable[focusable.length - 1];
1546
+ const root = modalEl.getRootNode();
1547
+ const active = (_a = root.activeElement) != null ? _a : document.activeElement;
1465
1548
  if (e.shiftKey) {
1466
- if (document.activeElement === first) {
1549
+ if (active === first) {
1467
1550
  e.preventDefault();
1468
1551
  last.focus();
1469
1552
  }
1470
1553
  } else {
1471
- if (document.activeElement === last) {
1554
+ if (active === last) {
1472
1555
  e.preventDefault();
1473
1556
  first.focus();
1474
1557
  }
@@ -1558,7 +1641,15 @@ var DPO_AUDIO_ELEMENT_MAP = [
1558
1641
 
1559
1642
  // src/modal/tts.ts
1560
1643
  var TTSManager = class {
1561
- constructor(onStateChange) {
1644
+ /**
1645
+ * @param onStateChange Invoked whenever playback state changes so the host
1646
+ * can refresh its UI (e.g. swap the play/pause icon).
1647
+ * @param root The root in which to look up highlightable elements by id.
1648
+ * When the modal is rendered inside a shadow root, pass that shadow root
1649
+ * so queries stay scoped to the SDK's tree (and don't accidentally hit
1650
+ * element ids that the host page also defines).
1651
+ */
1652
+ constructor(onStateChange, root) {
1562
1653
  this.audioUrls = null;
1563
1654
  this.audioSequence = [];
1564
1655
  this.elementIdSequence = [];
@@ -1570,6 +1661,7 @@ var TTSManager = class {
1570
1661
  this.highlightedElementId = null;
1571
1662
  this.abortController = null;
1572
1663
  this.onStateChange = onStateChange;
1664
+ this.root = root;
1573
1665
  }
1574
1666
  get isPlaying() {
1575
1667
  return this._isPlaying;
@@ -1582,9 +1674,11 @@ var TTSManager = class {
1582
1674
  }
1583
1675
  async fetchAudioUrls(params) {
1584
1676
  var _a;
1585
- this.stop();
1677
+ this.stopInternal();
1586
1678
  this._isAvailable = false;
1587
1679
  this.audioUrls = null;
1680
+ this.audioSequence = [];
1681
+ this.elementIdSequence = [];
1588
1682
  this.onStateChange();
1589
1683
  (_a = this.abortController) == null ? void 0 : _a.abort();
1590
1684
  this.abortController = new AbortController();
@@ -1597,13 +1691,15 @@ var TTSManager = class {
1597
1691
  signal: this.abortController.signal
1598
1692
  });
1599
1693
  this.audioUrls = response.detail;
1600
- this._isAvailable = true;
1601
1694
  this.buildSequences(params.content);
1695
+ this._isAvailable = this.audioSequence.length > 0;
1602
1696
  this.onStateChange();
1603
1697
  } catch (error) {
1604
1698
  if (error.name !== "AbortError") {
1605
1699
  this._isAvailable = false;
1606
1700
  this.audioUrls = null;
1701
+ this.audioSequence = [];
1702
+ this.elementIdSequence = [];
1607
1703
  this.onStateChange();
1608
1704
  }
1609
1705
  }
@@ -1677,6 +1773,16 @@ var TTSManager = class {
1677
1773
  }
1678
1774
  }
1679
1775
  stop() {
1776
+ this.stopInternal();
1777
+ this.onStateChange();
1778
+ }
1779
+ /**
1780
+ * Internal stop used by code paths (like fetchAudioUrls) that will fire
1781
+ * their own onStateChange after composing a richer state update — we don't
1782
+ * want the intermediate notification to flicker the UI based on a half-set
1783
+ * state (e.g. _isAvailable still true from the previous language).
1784
+ */
1785
+ stopInternal() {
1680
1786
  if (this.currentAudio) {
1681
1787
  this.currentAudio.pause();
1682
1788
  this.currentAudio.currentTime = 0;
@@ -1687,7 +1793,6 @@ var TTSManager = class {
1687
1793
  this.currentIndex = 0;
1688
1794
  this.removeHighlights();
1689
1795
  this.highlightedElementId = null;
1690
- this.onStateChange();
1691
1796
  }
1692
1797
  destroy() {
1693
1798
  var _a;
@@ -1728,7 +1833,7 @@ var TTSManager = class {
1728
1833
  this.removeHighlights();
1729
1834
  this.highlightedElementId = elementId;
1730
1835
  setTimeout(() => {
1731
- const element = document.getElementById(elementId);
1836
+ const element = "getElementById" in this.root ? this.root.getElementById(elementId) : this.root.querySelector(`#${CSS.escape(elementId)}`);
1732
1837
  if (element) {
1733
1838
  element.classList.add("redacto-tts-highlight");
1734
1839
  const rect = element.getBoundingClientRect();
@@ -1744,7 +1849,7 @@ var TTSManager = class {
1744
1849
  }, 50);
1745
1850
  }
1746
1851
  removeHighlights() {
1747
- document.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1852
+ this.root.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1748
1853
  el2.classList.remove("redacto-tts-highlight");
1749
1854
  });
1750
1855
  }
@@ -2551,6 +2656,10 @@ function resolveColors(settings, activeConfig) {
2551
2656
  // src/modal/index.ts
2552
2657
  var ModalController = class {
2553
2658
  constructor(options) {
2659
+ this.hostEl = null;
2660
+ this.shadowRoot = null;
2661
+ this.previousBodyOverflow = null;
2662
+ this.previousHtmlOverflow = null;
2554
2663
  this.overlayEl = null;
2555
2664
  this.modalEl = null;
2556
2665
  this.purposeRefs = null;
@@ -2559,6 +2668,7 @@ var ModalController = class {
2559
2668
  this.keyboardCleanup = null;
2560
2669
  this.abortController = null;
2561
2670
  this.unsubscribe = null;
2671
+ this.ttsManager = null;
2562
2672
  this.guardianFormRefs = null;
2563
2673
  this.statusPollTimeout = null;
2564
2674
  this.popupCheckInterval = null;
@@ -2610,10 +2720,8 @@ var ModalController = class {
2610
2720
  guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
2611
2721
  });
2612
2722
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
2613
- this.ttsManager = new TTSManager(() => this.updateTTSButton());
2614
2723
  }
2615
2724
  async init() {
2616
- injectCheckboxStyles();
2617
2725
  this.unsubscribe = this.store.subscribe((state, prevState) => {
2618
2726
  this.handleStateChange(state, prevState);
2619
2727
  });
@@ -2627,18 +2735,22 @@ var ModalController = class {
2627
2735
  (_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
2628
2736
  (_c = this.unsubscribe) == null ? void 0 : _c.call(this);
2629
2737
  this.mediaQuery.destroy();
2630
- this.ttsManager.destroy();
2738
+ (_d = this.ttsManager) == null ? void 0 : _d.destroy();
2631
2739
  this.stopPolling();
2632
2740
  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);
2741
+ if (this.hostEl && this.hostEl.parentNode) {
2742
+ this.hostEl.parentNode.removeChild(this.hostEl);
2635
2743
  }
2636
- if (this.overlayEl && document.body.contains(this.overlayEl)) {
2637
- document.body.removeChild(this.overlayEl);
2744
+ if (this.previousBodyOverflow !== null) {
2745
+ document.body.style.overflow = this.previousBodyOverflow;
2746
+ this.previousBodyOverflow = null;
2638
2747
  }
2639
- if (this.modalEl && document.body.contains(this.modalEl)) {
2640
- document.body.removeChild(this.modalEl);
2748
+ if (this.previousHtmlOverflow !== null) {
2749
+ document.documentElement.style.overflow = this.previousHtmlOverflow;
2750
+ this.previousHtmlOverflow = null;
2641
2751
  }
2752
+ this.hostEl = null;
2753
+ this.shadowRoot = null;
2642
2754
  this.overlayEl = null;
2643
2755
  this.modalEl = null;
2644
2756
  this.purposeRefs = null;
@@ -2807,13 +2919,32 @@ var ModalController = class {
2807
2919
  }
2808
2920
  }
2809
2921
  renderOverlayWithLoading() {
2922
+ this.hostEl = document.createElement("div");
2923
+ this.hostEl.setAttribute("data-redacto-consent", "");
2924
+ this.hostEl.style.position = "fixed";
2925
+ this.hostEl.style.top = "0";
2926
+ this.hostEl.style.left = "0";
2927
+ this.hostEl.style.width = "0";
2928
+ this.hostEl.style.height = "0";
2929
+ this.hostEl.style.zIndex = "2147483646";
2930
+ document.body.appendChild(this.hostEl);
2931
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
2932
+ this.previousBodyOverflow = document.body.style.overflow;
2933
+ this.previousHtmlOverflow = document.documentElement.style.overflow;
2934
+ document.body.style.overflow = "hidden";
2935
+ document.documentElement.style.overflow = "hidden";
2936
+ injectCheckboxStyles(this.shadowRoot);
2937
+ this.ttsManager = new TTSManager(
2938
+ () => this.updateTTSButton(),
2939
+ this.shadowRoot
2940
+ );
2810
2941
  const blockUI = this.options.blockUI !== false;
2811
2942
  if (blockUI) {
2812
2943
  this.overlayEl = el("div", {
2813
2944
  style: styles.overlay,
2814
2945
  role: "presentation"
2815
2946
  });
2816
- document.body.appendChild(this.overlayEl);
2947
+ this.shadowRoot.appendChild(this.overlayEl);
2817
2948
  }
2818
2949
  const settingsObj = this.options.settings;
2819
2950
  const modalStyle = { ...styles.modal };
@@ -2839,10 +2970,10 @@ var ModalController = class {
2839
2970
  );
2840
2971
  const contentEl = el("div", { style: styles.content }, loadingEl);
2841
2972
  this.modalEl.appendChild(contentEl);
2842
- document.body.appendChild(this.modalEl);
2973
+ this.shadowRoot.appendChild(this.modalEl);
2843
2974
  }
2844
2975
  rebuildUI() {
2845
- var _a, _b, _c, _d, _e, _f, _g;
2976
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2846
2977
  const state = this.store.getState();
2847
2978
  if (!state.content || !this.modalEl) return;
2848
2979
  const activeConfig = state.content.detail.active_config;
@@ -2923,15 +3054,22 @@ var ModalController = class {
2923
3054
  const textColor = colors.text;
2924
3055
  const linkColor = colors.link;
2925
3056
  const font = colors.font;
2926
- const logoImg = activeConfig.logo_url ? el("img", { src: activeConfig.logo_url, style: styles.logo, alt: "Logo" }) : null;
3057
+ const logoImg = activeConfig.logo_url ? el("img", {
3058
+ src: activeConfig.logo_url,
3059
+ style: {
3060
+ ...styles.logo,
3061
+ maxWidth: isMobile ? "96px" : "140px",
3062
+ maxHeight: isMobile ? "28px" : "32px"
3063
+ },
3064
+ alt: "Logo"
3065
+ }) : null;
2927
3066
  this.noticeTitleEl = el("span", {
2928
3067
  id: "privacy-notice-title",
2929
3068
  style: { ...styles.title, color: headingColor, fontFamily: font, fontSize: isMobile ? "16px" : "18px" }
2930
3069
  });
2931
3070
  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);
3071
+ if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && this.shadowRoot && this.langSelectorRefs.dropdown.parentNode === this.shadowRoot) {
3072
+ this.shadowRoot.removeChild(this.langSelectorRefs.dropdown);
2935
3073
  }
2936
3074
  const supportedLangs = Object.keys(activeConfig.supported_languages_and_translations || {});
2937
3075
  const allLanguages = [activeConfig.default_language || "English", ...supportedLangs.filter((l) => l !== activeConfig.default_language)];
@@ -2939,7 +3077,8 @@ var ModalController = class {
2939
3077
  languages: allLanguages,
2940
3078
  selectedLanguage: state.selectedLanguage,
2941
3079
  settings: settingsObj,
2942
- onSelect: (lang) => this.handleLanguageSelect(lang)
3080
+ onSelect: (lang) => this.handleLanguageSelect(lang),
3081
+ dropdownContainer: this.shadowRoot
2943
3082
  });
2944
3083
  const ttsStrokeColor = ((_f = (_e = settingsObj == null ? void 0 : settingsObj.button) == null ? void 0 : _e.language) == null ? void 0 : _f.textColor) || "currentColor";
2945
3084
  const speakerSvg = svg(
@@ -2970,10 +3109,13 @@ var ModalController = class {
2970
3109
  type: "button",
2971
3110
  "aria-label": "Start audio playback",
2972
3111
  title: "Start Talkback",
2973
- onClick: () => this.ttsManager.toggle()
3112
+ onClick: () => {
3113
+ var _a2;
3114
+ return (_a2 = this.ttsManager) == null ? void 0 : _a2.toggle();
3115
+ }
2974
3116
  });
2975
3117
  this.ttsButton.appendChild(speakerSvg);
2976
- this.ttsManager.fetchAudioUrls({
3118
+ (_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
2977
3119
  accessToken: this.options.accessToken,
2978
3120
  baseUrl: this.options.baseUrl,
2979
3121
  noticeUuid: activeConfig.notice_uuid,
@@ -2981,9 +3123,44 @@ var ModalController = class {
2981
3123
  content: activeConfig
2982
3124
  });
2983
3125
  const topRight = el("div", {
2984
- style: { display: "flex", alignItems: "center", gap: "8px" }
3126
+ style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
2985
3127
  }, this.ttsButton, this.langSelectorRefs.container);
2986
- const topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3128
+ let topSection;
3129
+ if (isMobile) {
3130
+ const headerRow = el(
3131
+ "div",
3132
+ {
3133
+ style: {
3134
+ display: "flex",
3135
+ alignItems: "center",
3136
+ gap: "10px",
3137
+ width: "100%"
3138
+ }
3139
+ },
3140
+ ...logoImg ? [logoImg] : [],
3141
+ el(
3142
+ "div",
3143
+ { style: { marginLeft: "auto", display: "flex", alignItems: "center" } },
3144
+ topRight
3145
+ )
3146
+ );
3147
+ topSection = el(
3148
+ "div",
3149
+ {
3150
+ style: {
3151
+ ...styles.topSection,
3152
+ flexDirection: "column",
3153
+ alignItems: "stretch",
3154
+ flexWrap: "nowrap"
3155
+ }
3156
+ },
3157
+ headerRow,
3158
+ this.noticeTitleEl
3159
+ );
3160
+ } else {
3161
+ const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
3162
+ topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3163
+ }
2987
3164
  this.middleSection = el("div", { style: styles.middleSection });
2988
3165
  const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
2989
3166
  const noticeTextSpan = el("span", { id: "privacy-notice-text" });
@@ -3228,7 +3405,7 @@ var ModalController = class {
3228
3405
  );
3229
3406
  this.modalEl.appendChild(contentWrapper);
3230
3407
  this.updateAcceptButtonState(state);
3231
- (_g = this.keyboardCleanup) == null ? void 0 : _g.call(this);
3408
+ (_h = this.keyboardCleanup) == null ? void 0 : _h.call(this);
3232
3409
  this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
3233
3410
  }
3234
3411
  getLanguageCode(displayLanguage) {
@@ -3786,54 +3963,56 @@ var ModalController = class {
3786
3963
  // ─── TTS ───
3787
3964
  updateTTSButton() {
3788
3965
  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
- }
3966
+ if (!this.ttsButton || !this.ttsManager) return;
3967
+ if (!this.ttsManager.isAvailable) {
3968
+ this.ttsButton.style.display = "none";
3969
+ return;
3970
+ }
3971
+ this.ttsButton.style.display = "flex";
3972
+ clearElement(this.ttsButton);
3973
+ const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3974
+ if (this.ttsManager.isPlaying) {
3975
+ const pauseSvg = svg(
3976
+ "svg",
3977
+ {
3978
+ xmlns: "http://www.w3.org/2000/svg",
3979
+ width: "16",
3980
+ height: "16",
3981
+ viewBox: "0 0 24 24",
3982
+ fill: "none",
3983
+ stroke: strokeColor,
3984
+ "stroke-width": "2",
3985
+ "stroke-linecap": "round",
3986
+ "stroke-linejoin": "round",
3987
+ "aria-hidden": "true"
3988
+ },
3989
+ svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3990
+ svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3991
+ );
3992
+ this.ttsButton.appendChild(pauseSvg);
3993
+ this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3994
+ this.ttsButton.setAttribute("title", "Pause Talkback");
3995
+ } else {
3996
+ const speakerSvg = svg(
3997
+ "svg",
3998
+ {
3999
+ xmlns: "http://www.w3.org/2000/svg",
4000
+ width: "16",
4001
+ height: "16",
4002
+ viewBox: "0 0 24 24",
4003
+ fill: "none",
4004
+ stroke: strokeColor,
4005
+ "stroke-width": "2",
4006
+ "stroke-linecap": "round",
4007
+ "stroke-linejoin": "round",
4008
+ "aria-hidden": "true"
4009
+ },
4010
+ svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
4011
+ svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
4012
+ );
4013
+ this.ttsButton.appendChild(speakerSvg);
4014
+ this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
4015
+ this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3837
4016
  }
3838
4017
  }
3839
4018
  updateButtonStates(state) {
@@ -4153,6 +4332,8 @@ function renderInlinePurposes(purposes, options) {
4153
4332
  var InlineController = class {
4154
4333
  constructor(options) {
4155
4334
  this.targetEl = null;
4335
+ this.hostEl = null;
4336
+ this.shadowRoot = null;
4156
4337
  this.rootEl = null;
4157
4338
  this.purposeRefs = null;
4158
4339
  this.errorEl = null;
@@ -4174,7 +4355,6 @@ var InlineController = class {
4174
4355
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
4175
4356
  }
4176
4357
  async init() {
4177
- injectCheckboxStyles();
4178
4358
  if (typeof this.options.target === "string") {
4179
4359
  this.targetEl = document.querySelector(this.options.target);
4180
4360
  if (!this.targetEl) {
@@ -4183,6 +4363,12 @@ var InlineController = class {
4183
4363
  } else {
4184
4364
  this.targetEl = this.options.target;
4185
4365
  }
4366
+ this.hostEl = document.createElement("div");
4367
+ this.hostEl.setAttribute("data-redacto-consent-inline", "");
4368
+ this.hostEl.style.display = "block";
4369
+ this.targetEl.appendChild(this.hostEl);
4370
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
4371
+ injectCheckboxStyles(this.shadowRoot);
4186
4372
  this.unsubscribe = this.store.subscribe((state, prevState) => {
4187
4373
  this.handleStateChange(state, prevState);
4188
4374
  });
@@ -4195,9 +4381,11 @@ var InlineController = class {
4195
4381
  (_a = this.unsubscribe) == null ? void 0 : _a.call(this);
4196
4382
  this.mediaQuery.destroy();
4197
4383
  this.store.destroy();
4198
- if (this.targetEl && this.rootEl) {
4199
- this.targetEl.removeChild(this.rootEl);
4384
+ if (this.hostEl && this.hostEl.parentNode) {
4385
+ this.hostEl.parentNode.removeChild(this.hostEl);
4200
4386
  }
4387
+ this.hostEl = null;
4388
+ this.shadowRoot = null;
4201
4389
  this.rootEl = null;
4202
4390
  this.purposeRefs = null;
4203
4391
  }
@@ -4253,8 +4441,8 @@ var InlineController = class {
4253
4441
  this.rebuildUI();
4254
4442
  }
4255
4443
  if (state.hasAlreadyConsented || state.fetchError) {
4256
- if (this.rootEl && this.targetEl) {
4257
- this.targetEl.removeChild(this.rootEl);
4444
+ if (this.rootEl && this.shadowRoot && this.rootEl.parentNode === this.shadowRoot) {
4445
+ this.shadowRoot.removeChild(this.rootEl);
4258
4446
  this.rootEl = null;
4259
4447
  }
4260
4448
  return;
@@ -4273,7 +4461,7 @@ var InlineController = class {
4273
4461
  }
4274
4462
  renderLoading() {
4275
4463
  var _a;
4276
- if (!this.targetEl) return;
4464
+ if (!this.shadowRoot) return;
4277
4465
  const textColor = ((_a = this.options.settings) == null ? void 0 : _a.textColor) || "#344054";
4278
4466
  this.rootEl = el(
4279
4467
  "div",
@@ -4281,13 +4469,13 @@ var InlineController = class {
4281
4469
  el("div", { style: inlineStyles.loadingSpinner }),
4282
4470
  el("p", { style: { fontSize: "16px", fontWeight: 400, color: textColor } }, "Loading...")
4283
4471
  );
4284
- this.targetEl.appendChild(this.rootEl);
4472
+ this.shadowRoot.appendChild(this.rootEl);
4285
4473
  }
4286
4474
  rebuildUI() {
4287
4475
  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);
4476
+ if (!state.content || !this.shadowRoot) return;
4477
+ if (this.rootEl && this.rootEl.parentNode === this.shadowRoot) {
4478
+ this.shadowRoot.removeChild(this.rootEl);
4291
4479
  }
4292
4480
  this.errorEl = el("div", {
4293
4481
  role: "alert",
@@ -4321,7 +4509,7 @@ var InlineController = class {
4321
4509
  this.errorEl,
4322
4510
  this.purposeRefs.container
4323
4511
  );
4324
- this.targetEl.appendChild(this.rootEl);
4512
+ this.shadowRoot.appendChild(this.rootEl);
4325
4513
  this.notifyValidation(state);
4326
4514
  }
4327
4515
  getTranslatedText(key, defaultText, itemId) {
@@ -4548,7 +4736,7 @@ function getErrorMessage(errorCode) {
4548
4736
  }
4549
4737
  function handleDigilockerCallback(options) {
4550
4738
  var _a, _b, _c;
4551
- injectCheckboxStyles();
4739
+ injectCheckboxStyles(document);
4552
4740
  const params = new URLSearchParams(window.location.search);
4553
4741
  const sessionToken = params.get("session_token");
4554
4742
  const urlStatus = params.get("status");