@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.mjs CHANGED
@@ -67,10 +67,49 @@ function applyAttrs(element, attrs) {
67
67
  }
68
68
  }
69
69
  }
70
+ var UNITLESS_PROPS = /* @__PURE__ */ new Set([
71
+ "animationIterationCount",
72
+ "aspectRatio",
73
+ "borderImageOutset",
74
+ "borderImageSlice",
75
+ "borderImageWidth",
76
+ "boxFlex",
77
+ "boxFlexGroup",
78
+ "boxOrdinalGroup",
79
+ "columnCount",
80
+ "columns",
81
+ "flex",
82
+ "flexGrow",
83
+ "flexPositive",
84
+ "flexShrink",
85
+ "flexNegative",
86
+ "flexOrder",
87
+ "fontWeight",
88
+ "gridArea",
89
+ "gridRow",
90
+ "gridRowEnd",
91
+ "gridRowSpan",
92
+ "gridRowStart",
93
+ "gridColumn",
94
+ "gridColumnEnd",
95
+ "gridColumnSpan",
96
+ "gridColumnStart",
97
+ "lineClamp",
98
+ "lineHeight",
99
+ "opacity",
100
+ "order",
101
+ "orphans",
102
+ "scale",
103
+ "tabSize",
104
+ "widows",
105
+ "zIndex",
106
+ "zoom"
107
+ ]);
70
108
  function applyStyles(element, styles2) {
71
109
  for (const [key, value] of Object.entries(styles2)) {
72
110
  if (value == null) continue;
73
- element.style[key] = typeof value === "number" ? `${value}px` : value;
111
+ const stringValue = typeof value === "number" && !UNITLESS_PROPS.has(key) ? `${value}px` : String(value);
112
+ element.style[key] = stringValue;
74
113
  }
75
114
  }
76
115
  function appendChildren(parent, children) {
@@ -548,19 +587,8 @@ var verifyGuardianStatus = async ({
548
587
  };
549
588
 
550
589
  // src/styles/inject.ts
551
- var stylesInjected = false;
552
- var injectCheckboxStyles = () => {
553
- if (stylesInjected || typeof document === "undefined") {
554
- return;
555
- }
556
- const STYLE_ID = "redacto-consent-checkbox-styles";
557
- if (document.getElementById(STYLE_ID)) {
558
- stylesInjected = true;
559
- return;
560
- }
561
- const style = document.createElement("style");
562
- style.id = STYLE_ID;
563
- style.textContent = `
590
+ var STYLE_ID = "redacto-consent-checkbox-styles";
591
+ var STYLE_CONTENT = `
564
592
  /* Redacto Consent SDK Checkbox Styles */
565
593
  .redacto-checkbox-large {
566
594
  appearance: none !important;
@@ -691,8 +719,18 @@ var injectCheckboxStyles = () => {
691
719
  background-color: #2563eb !important;
692
720
  }
693
721
  `;
694
- document.head.appendChild(style);
695
- stylesInjected = true;
722
+ var injectCheckboxStyles = (root) => {
723
+ if (typeof document === "undefined") return;
724
+ const existing = root instanceof Document ? root.getElementById(STYLE_ID) : root.querySelector(`#${STYLE_ID}`);
725
+ if (existing) return;
726
+ const style = document.createElement("style");
727
+ style.id = STYLE_ID;
728
+ style.textContent = STYLE_CONTENT;
729
+ if (root instanceof Document) {
730
+ root.head.appendChild(style);
731
+ } else {
732
+ root.appendChild(style);
733
+ }
696
734
  };
697
735
 
698
736
  // src/styles/modal.ts
@@ -747,7 +785,8 @@ var styles = {
747
785
  topSection: {
748
786
  display: "flex",
749
787
  justifyContent: "space-between",
750
- alignItems: "center",
788
+ alignItems: "flex-start",
789
+ gap: "10px",
751
790
  paddingBottom: "15px",
752
791
  borderBottom: "none",
753
792
  backgroundColor: "#ffffff",
@@ -759,6 +798,8 @@ var styles = {
759
798
  display: "flex",
760
799
  alignItems: "center",
761
800
  gap: "10px",
801
+ flex: "1 1 0",
802
+ minWidth: 0,
762
803
  margin: 0,
763
804
  padding: 0,
764
805
  boxSizing: "border-box"
@@ -766,7 +807,9 @@ var styles = {
766
807
  logo: {
767
808
  height: "32px",
768
809
  width: "auto",
810
+ maxWidth: "140px",
769
811
  objectFit: "contain",
812
+ flexShrink: 0,
770
813
  margin: 0,
771
814
  padding: 0,
772
815
  display: "block"
@@ -778,6 +821,10 @@ var styles = {
778
821
  letterSpacing: "0.2px",
779
822
  verticalAlign: "middle",
780
823
  color: "#101828",
824
+ flex: "1 1 auto",
825
+ minWidth: 0,
826
+ wordBreak: "normal",
827
+ overflowWrap: "anywhere",
781
828
  margin: 0,
782
829
  padding: 0,
783
830
  textAlign: "left"
@@ -789,6 +836,7 @@ var styles = {
789
836
  display: "flex",
790
837
  alignItems: "center",
791
838
  justifyContent: "center",
839
+ flexShrink: 0,
792
840
  fontWeight: 400,
793
841
  fontSize: "12px",
794
842
  lineHeight: "150%",
@@ -1273,12 +1321,41 @@ function createGreenCheckmark(size, ariaLabel) {
1273
1321
  }
1274
1322
 
1275
1323
  // src/modal/language-selector.ts
1324
+ var LANGUAGE_NATIVE_LABELS = {
1325
+ English: "English",
1326
+ Assamese: "\u0985\u09B8\u09AE\u09C0\u09AF\u09BC\u09BE (Assamese)",
1327
+ Bengali: "\u09AC\u09BE\u0982\u09B2\u09BE (Bengali)",
1328
+ Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
1329
+ Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
1330
+ "Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
1331
+ Gujarati: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)",
1332
+ Hindi: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)",
1333
+ Kannada: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)",
1334
+ Kashmiri: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)",
1335
+ Maithili: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)",
1336
+ Malayalam: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)",
1337
+ Manipuri: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)",
1338
+ Marathi: "\u092E\u0930\u093E\u0920\u0940 (Marathi)",
1339
+ Nepali: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)",
1340
+ Odia: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)",
1341
+ Punjabi: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)",
1342
+ Sanskrit: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)",
1343
+ Santali: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)",
1344
+ Sindhi: "\u0633\u0646\u068C\u064A (Sindhi)",
1345
+ Tamil: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)",
1346
+ Telugu: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)",
1347
+ Urdu: "\u0627\u0631\u062F\u0648 (Urdu)"
1348
+ };
1349
+ var toNativeLabel = (name) => {
1350
+ var _a;
1351
+ return (_a = LANGUAGE_NATIVE_LABELS[name]) != null ? _a : name;
1352
+ };
1276
1353
  function renderLanguageSelector(options) {
1277
1354
  var _a, _b, _c, _d;
1278
- const { languages, selectedLanguage, settings, onSelect } = options;
1355
+ const { languages, selectedLanguage, settings, onSelect, dropdownContainer } = options;
1279
1356
  let isOpen = false;
1280
1357
  const buttonText = el("span");
1281
- buttonText.textContent = selectedLanguage;
1358
+ buttonText.textContent = toNativeLabel(selectedLanguage);
1282
1359
  const chevronSvg = svg(
1283
1360
  "svg",
1284
1361
  {
@@ -1307,7 +1384,7 @@ function renderLanguageSelector(options) {
1307
1384
  tabIndex: "0",
1308
1385
  "aria-haspopup": "listbox",
1309
1386
  "aria-expanded": "false",
1310
- "aria-label": `Language selector, current: ${selectedLanguage}`,
1387
+ "aria-label": `Language selector, current: ${toNativeLabel(selectedLanguage)}`,
1311
1388
  onClick: () => toggleDropdown(),
1312
1389
  onKeydown: (e) => {
1313
1390
  if (e.key === "Enter" || e.key === " ") {
@@ -1364,13 +1441,11 @@ function renderLanguageSelector(options) {
1364
1441
  e.target.style.backgroundColor = isSelected ? "#f3f4f6" : "#ffffff";
1365
1442
  }
1366
1443
  });
1367
- item.textContent = lang;
1444
+ item.textContent = toNativeLabel(lang);
1368
1445
  dropdown.appendChild(item);
1369
1446
  }
1370
1447
  const container = el("div", { style: styles.languageSelectorContainer }, button);
1371
- if (typeof document !== "undefined") {
1372
- document.body.appendChild(dropdown);
1373
- }
1448
+ dropdownContainer.appendChild(dropdown);
1374
1449
  function positionDropdown() {
1375
1450
  const rect = button.getBoundingClientRect();
1376
1451
  dropdown.style.top = `${rect.bottom + 4}px`;
@@ -1378,10 +1453,14 @@ function renderLanguageSelector(options) {
1378
1453
  dropdown.style.left = "auto";
1379
1454
  }
1380
1455
  const clickOutsideHandler = (e) => {
1381
- const target = e.target;
1382
- if (!container.contains(target) && !dropdown.contains(target)) {
1383
- closeDropdown();
1384
- }
1456
+ var _a2, _b2;
1457
+ const path = (_b2 = (_a2 = e.composedPath) == null ? void 0 : _a2.call(e)) != null ? _b2 : [];
1458
+ if (path.includes(container) || path.includes(dropdown)) return;
1459
+ if (path.length === 0) {
1460
+ const target = e.target;
1461
+ if (container.contains(target) || dropdown.contains(target)) return;
1462
+ }
1463
+ closeDropdown();
1385
1464
  };
1386
1465
  const repositionHandler = () => {
1387
1466
  if (isOpen) positionDropdown();
@@ -1414,6 +1493,7 @@ function renderLanguageSelector(options) {
1414
1493
  // src/modal/keyboard.ts
1415
1494
  function setupKeyboardTrap(modalEl, onEscape) {
1416
1495
  const handler = (e) => {
1496
+ var _a;
1417
1497
  if (e.key === "Escape") {
1418
1498
  e.preventDefault();
1419
1499
  onEscape();
@@ -1432,13 +1512,15 @@ function setupKeyboardTrap(modalEl, onEscape) {
1432
1512
  }
1433
1513
  const first = focusable[0];
1434
1514
  const last = focusable[focusable.length - 1];
1515
+ const root = modalEl.getRootNode();
1516
+ const active = (_a = root.activeElement) != null ? _a : document.activeElement;
1435
1517
  if (e.shiftKey) {
1436
- if (document.activeElement === first) {
1518
+ if (active === first) {
1437
1519
  e.preventDefault();
1438
1520
  last.focus();
1439
1521
  }
1440
1522
  } else {
1441
- if (document.activeElement === last) {
1523
+ if (active === last) {
1442
1524
  e.preventDefault();
1443
1525
  first.focus();
1444
1526
  }
@@ -1528,7 +1610,15 @@ var DPO_AUDIO_ELEMENT_MAP = [
1528
1610
 
1529
1611
  // src/modal/tts.ts
1530
1612
  var TTSManager = class {
1531
- constructor(onStateChange) {
1613
+ /**
1614
+ * @param onStateChange Invoked whenever playback state changes so the host
1615
+ * can refresh its UI (e.g. swap the play/pause icon).
1616
+ * @param root The root in which to look up highlightable elements by id.
1617
+ * When the modal is rendered inside a shadow root, pass that shadow root
1618
+ * so queries stay scoped to the SDK's tree (and don't accidentally hit
1619
+ * element ids that the host page also defines).
1620
+ */
1621
+ constructor(onStateChange, root) {
1532
1622
  this.audioUrls = null;
1533
1623
  this.audioSequence = [];
1534
1624
  this.elementIdSequence = [];
@@ -1540,6 +1630,7 @@ var TTSManager = class {
1540
1630
  this.highlightedElementId = null;
1541
1631
  this.abortController = null;
1542
1632
  this.onStateChange = onStateChange;
1633
+ this.root = root;
1543
1634
  }
1544
1635
  get isPlaying() {
1545
1636
  return this._isPlaying;
@@ -1552,9 +1643,11 @@ var TTSManager = class {
1552
1643
  }
1553
1644
  async fetchAudioUrls(params) {
1554
1645
  var _a;
1555
- this.stop();
1646
+ this.stopInternal();
1556
1647
  this._isAvailable = false;
1557
1648
  this.audioUrls = null;
1649
+ this.audioSequence = [];
1650
+ this.elementIdSequence = [];
1558
1651
  this.onStateChange();
1559
1652
  (_a = this.abortController) == null ? void 0 : _a.abort();
1560
1653
  this.abortController = new AbortController();
@@ -1567,13 +1660,15 @@ var TTSManager = class {
1567
1660
  signal: this.abortController.signal
1568
1661
  });
1569
1662
  this.audioUrls = response.detail;
1570
- this._isAvailable = true;
1571
1663
  this.buildSequences(params.content);
1664
+ this._isAvailable = this.audioSequence.length > 0;
1572
1665
  this.onStateChange();
1573
1666
  } catch (error) {
1574
1667
  if (error.name !== "AbortError") {
1575
1668
  this._isAvailable = false;
1576
1669
  this.audioUrls = null;
1670
+ this.audioSequence = [];
1671
+ this.elementIdSequence = [];
1577
1672
  this.onStateChange();
1578
1673
  }
1579
1674
  }
@@ -1647,6 +1742,16 @@ var TTSManager = class {
1647
1742
  }
1648
1743
  }
1649
1744
  stop() {
1745
+ this.stopInternal();
1746
+ this.onStateChange();
1747
+ }
1748
+ /**
1749
+ * Internal stop used by code paths (like fetchAudioUrls) that will fire
1750
+ * their own onStateChange after composing a richer state update — we don't
1751
+ * want the intermediate notification to flicker the UI based on a half-set
1752
+ * state (e.g. _isAvailable still true from the previous language).
1753
+ */
1754
+ stopInternal() {
1650
1755
  if (this.currentAudio) {
1651
1756
  this.currentAudio.pause();
1652
1757
  this.currentAudio.currentTime = 0;
@@ -1657,7 +1762,6 @@ var TTSManager = class {
1657
1762
  this.currentIndex = 0;
1658
1763
  this.removeHighlights();
1659
1764
  this.highlightedElementId = null;
1660
- this.onStateChange();
1661
1765
  }
1662
1766
  destroy() {
1663
1767
  var _a;
@@ -1698,7 +1802,7 @@ var TTSManager = class {
1698
1802
  this.removeHighlights();
1699
1803
  this.highlightedElementId = elementId;
1700
1804
  setTimeout(() => {
1701
- const element = document.getElementById(elementId);
1805
+ const element = "getElementById" in this.root ? this.root.getElementById(elementId) : this.root.querySelector(`#${CSS.escape(elementId)}`);
1702
1806
  if (element) {
1703
1807
  element.classList.add("redacto-tts-highlight");
1704
1808
  const rect = element.getBoundingClientRect();
@@ -1714,7 +1818,7 @@ var TTSManager = class {
1714
1818
  }, 50);
1715
1819
  }
1716
1820
  removeHighlights() {
1717
- document.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1821
+ this.root.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1718
1822
  el2.classList.remove("redacto-tts-highlight");
1719
1823
  });
1720
1824
  }
@@ -2521,6 +2625,10 @@ function resolveColors(settings, activeConfig) {
2521
2625
  // src/modal/index.ts
2522
2626
  var ModalController = class {
2523
2627
  constructor(options) {
2628
+ this.hostEl = null;
2629
+ this.shadowRoot = null;
2630
+ this.previousBodyOverflow = null;
2631
+ this.previousHtmlOverflow = null;
2524
2632
  this.overlayEl = null;
2525
2633
  this.modalEl = null;
2526
2634
  this.purposeRefs = null;
@@ -2529,6 +2637,7 @@ var ModalController = class {
2529
2637
  this.keyboardCleanup = null;
2530
2638
  this.abortController = null;
2531
2639
  this.unsubscribe = null;
2640
+ this.ttsManager = null;
2532
2641
  this.guardianFormRefs = null;
2533
2642
  this.statusPollTimeout = null;
2534
2643
  this.popupCheckInterval = null;
@@ -2580,10 +2689,8 @@ var ModalController = class {
2580
2689
  guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
2581
2690
  });
2582
2691
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
2583
- this.ttsManager = new TTSManager(() => this.updateTTSButton());
2584
2692
  }
2585
2693
  async init() {
2586
- injectCheckboxStyles();
2587
2694
  this.unsubscribe = this.store.subscribe((state, prevState) => {
2588
2695
  this.handleStateChange(state, prevState);
2589
2696
  });
@@ -2597,18 +2704,22 @@ var ModalController = class {
2597
2704
  (_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
2598
2705
  (_c = this.unsubscribe) == null ? void 0 : _c.call(this);
2599
2706
  this.mediaQuery.destroy();
2600
- this.ttsManager.destroy();
2707
+ (_d = this.ttsManager) == null ? void 0 : _d.destroy();
2601
2708
  this.stopPolling();
2602
2709
  this.store.destroy();
2603
- if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
2604
- document.body.removeChild(this.langSelectorRefs.dropdown);
2710
+ if (this.hostEl && this.hostEl.parentNode) {
2711
+ this.hostEl.parentNode.removeChild(this.hostEl);
2605
2712
  }
2606
- if (this.overlayEl && document.body.contains(this.overlayEl)) {
2607
- document.body.removeChild(this.overlayEl);
2713
+ if (this.previousBodyOverflow !== null) {
2714
+ document.body.style.overflow = this.previousBodyOverflow;
2715
+ this.previousBodyOverflow = null;
2608
2716
  }
2609
- if (this.modalEl && document.body.contains(this.modalEl)) {
2610
- document.body.removeChild(this.modalEl);
2717
+ if (this.previousHtmlOverflow !== null) {
2718
+ document.documentElement.style.overflow = this.previousHtmlOverflow;
2719
+ this.previousHtmlOverflow = null;
2611
2720
  }
2721
+ this.hostEl = null;
2722
+ this.shadowRoot = null;
2612
2723
  this.overlayEl = null;
2613
2724
  this.modalEl = null;
2614
2725
  this.purposeRefs = null;
@@ -2777,13 +2888,32 @@ var ModalController = class {
2777
2888
  }
2778
2889
  }
2779
2890
  renderOverlayWithLoading() {
2891
+ this.hostEl = document.createElement("div");
2892
+ this.hostEl.setAttribute("data-redacto-consent", "");
2893
+ this.hostEl.style.position = "fixed";
2894
+ this.hostEl.style.top = "0";
2895
+ this.hostEl.style.left = "0";
2896
+ this.hostEl.style.width = "0";
2897
+ this.hostEl.style.height = "0";
2898
+ this.hostEl.style.zIndex = "2147483646";
2899
+ document.body.appendChild(this.hostEl);
2900
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
2901
+ this.previousBodyOverflow = document.body.style.overflow;
2902
+ this.previousHtmlOverflow = document.documentElement.style.overflow;
2903
+ document.body.style.overflow = "hidden";
2904
+ document.documentElement.style.overflow = "hidden";
2905
+ injectCheckboxStyles(this.shadowRoot);
2906
+ this.ttsManager = new TTSManager(
2907
+ () => this.updateTTSButton(),
2908
+ this.shadowRoot
2909
+ );
2780
2910
  const blockUI = this.options.blockUI !== false;
2781
2911
  if (blockUI) {
2782
2912
  this.overlayEl = el("div", {
2783
2913
  style: styles.overlay,
2784
2914
  role: "presentation"
2785
2915
  });
2786
- document.body.appendChild(this.overlayEl);
2916
+ this.shadowRoot.appendChild(this.overlayEl);
2787
2917
  }
2788
2918
  const settingsObj = this.options.settings;
2789
2919
  const modalStyle = { ...styles.modal };
@@ -2809,10 +2939,10 @@ var ModalController = class {
2809
2939
  );
2810
2940
  const contentEl = el("div", { style: styles.content }, loadingEl);
2811
2941
  this.modalEl.appendChild(contentEl);
2812
- document.body.appendChild(this.modalEl);
2942
+ this.shadowRoot.appendChild(this.modalEl);
2813
2943
  }
2814
2944
  rebuildUI() {
2815
- var _a, _b, _c, _d, _e, _f, _g;
2945
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2816
2946
  const state = this.store.getState();
2817
2947
  if (!state.content || !this.modalEl) return;
2818
2948
  const activeConfig = state.content.detail.active_config;
@@ -2893,15 +3023,22 @@ var ModalController = class {
2893
3023
  const textColor = colors.text;
2894
3024
  const linkColor = colors.link;
2895
3025
  const font = colors.font;
2896
- const logoImg = activeConfig.logo_url ? el("img", { src: activeConfig.logo_url, style: styles.logo, alt: "Logo" }) : null;
3026
+ const logoImg = activeConfig.logo_url ? el("img", {
3027
+ src: activeConfig.logo_url,
3028
+ style: {
3029
+ ...styles.logo,
3030
+ maxWidth: isMobile ? "96px" : "140px",
3031
+ maxHeight: isMobile ? "28px" : "32px"
3032
+ },
3033
+ alt: "Logo"
3034
+ }) : null;
2897
3035
  this.noticeTitleEl = el("span", {
2898
3036
  id: "privacy-notice-title",
2899
3037
  style: { ...styles.title, color: headingColor, fontFamily: font, fontSize: isMobile ? "16px" : "18px" }
2900
3038
  });
2901
3039
  this.noticeTitleEl.textContent = this.getTranslatedText("notice_banner_heading", activeConfig.notice_banner_heading);
2902
- const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
2903
- if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
2904
- document.body.removeChild(this.langSelectorRefs.dropdown);
3040
+ if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && this.shadowRoot && this.langSelectorRefs.dropdown.parentNode === this.shadowRoot) {
3041
+ this.shadowRoot.removeChild(this.langSelectorRefs.dropdown);
2905
3042
  }
2906
3043
  const supportedLangs = Object.keys(activeConfig.supported_languages_and_translations || {});
2907
3044
  const allLanguages = [activeConfig.default_language || "English", ...supportedLangs.filter((l) => l !== activeConfig.default_language)];
@@ -2909,7 +3046,8 @@ var ModalController = class {
2909
3046
  languages: allLanguages,
2910
3047
  selectedLanguage: state.selectedLanguage,
2911
3048
  settings: settingsObj,
2912
- onSelect: (lang) => this.handleLanguageSelect(lang)
3049
+ onSelect: (lang) => this.handleLanguageSelect(lang),
3050
+ dropdownContainer: this.shadowRoot
2913
3051
  });
2914
3052
  const ttsStrokeColor = ((_f = (_e = settingsObj == null ? void 0 : settingsObj.button) == null ? void 0 : _e.language) == null ? void 0 : _f.textColor) || "currentColor";
2915
3053
  const speakerSvg = svg(
@@ -2940,10 +3078,13 @@ var ModalController = class {
2940
3078
  type: "button",
2941
3079
  "aria-label": "Start audio playback",
2942
3080
  title: "Start Talkback",
2943
- onClick: () => this.ttsManager.toggle()
3081
+ onClick: () => {
3082
+ var _a2;
3083
+ return (_a2 = this.ttsManager) == null ? void 0 : _a2.toggle();
3084
+ }
2944
3085
  });
2945
3086
  this.ttsButton.appendChild(speakerSvg);
2946
- this.ttsManager.fetchAudioUrls({
3087
+ (_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
2947
3088
  accessToken: this.options.accessToken,
2948
3089
  baseUrl: this.options.baseUrl,
2949
3090
  noticeUuid: activeConfig.notice_uuid,
@@ -2951,9 +3092,44 @@ var ModalController = class {
2951
3092
  content: activeConfig
2952
3093
  });
2953
3094
  const topRight = el("div", {
2954
- style: { display: "flex", alignItems: "center", gap: "8px" }
3095
+ style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
2955
3096
  }, this.ttsButton, this.langSelectorRefs.container);
2956
- const topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3097
+ let topSection;
3098
+ if (isMobile) {
3099
+ const headerRow = el(
3100
+ "div",
3101
+ {
3102
+ style: {
3103
+ display: "flex",
3104
+ alignItems: "center",
3105
+ gap: "10px",
3106
+ width: "100%"
3107
+ }
3108
+ },
3109
+ ...logoImg ? [logoImg] : [],
3110
+ el(
3111
+ "div",
3112
+ { style: { marginLeft: "auto", display: "flex", alignItems: "center" } },
3113
+ topRight
3114
+ )
3115
+ );
3116
+ topSection = el(
3117
+ "div",
3118
+ {
3119
+ style: {
3120
+ ...styles.topSection,
3121
+ flexDirection: "column",
3122
+ alignItems: "stretch",
3123
+ flexWrap: "nowrap"
3124
+ }
3125
+ },
3126
+ headerRow,
3127
+ this.noticeTitleEl
3128
+ );
3129
+ } else {
3130
+ const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
3131
+ topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3132
+ }
2957
3133
  this.middleSection = el("div", { style: styles.middleSection });
2958
3134
  const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
2959
3135
  const noticeTextSpan = el("span", { id: "privacy-notice-text" });
@@ -3198,7 +3374,7 @@ var ModalController = class {
3198
3374
  );
3199
3375
  this.modalEl.appendChild(contentWrapper);
3200
3376
  this.updateAcceptButtonState(state);
3201
- (_g = this.keyboardCleanup) == null ? void 0 : _g.call(this);
3377
+ (_h = this.keyboardCleanup) == null ? void 0 : _h.call(this);
3202
3378
  this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
3203
3379
  }
3204
3380
  getLanguageCode(displayLanguage) {
@@ -3756,54 +3932,56 @@ var ModalController = class {
3756
3932
  // ─── TTS ───
3757
3933
  updateTTSButton() {
3758
3934
  var _a, _b, _c;
3759
- if (!this.ttsButton) return;
3760
- if (this.ttsManager.isAvailable) {
3761
- this.ttsButton.style.display = "flex";
3762
- clearElement(this.ttsButton);
3763
- const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3764
- if (this.ttsManager.isPlaying) {
3765
- const pauseSvg = svg(
3766
- "svg",
3767
- {
3768
- xmlns: "http://www.w3.org/2000/svg",
3769
- width: "16",
3770
- height: "16",
3771
- viewBox: "0 0 24 24",
3772
- fill: "none",
3773
- stroke: strokeColor,
3774
- "stroke-width": "2",
3775
- "stroke-linecap": "round",
3776
- "stroke-linejoin": "round",
3777
- "aria-hidden": "true"
3778
- },
3779
- svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3780
- svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3781
- );
3782
- this.ttsButton.appendChild(pauseSvg);
3783
- this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3784
- this.ttsButton.setAttribute("title", "Pause Talkback");
3785
- } else {
3786
- const speakerSvg = svg(
3787
- "svg",
3788
- {
3789
- xmlns: "http://www.w3.org/2000/svg",
3790
- width: "16",
3791
- height: "16",
3792
- viewBox: "0 0 24 24",
3793
- fill: "none",
3794
- stroke: strokeColor,
3795
- "stroke-width": "2",
3796
- "stroke-linecap": "round",
3797
- "stroke-linejoin": "round",
3798
- "aria-hidden": "true"
3799
- },
3800
- svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
3801
- svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
3802
- );
3803
- this.ttsButton.appendChild(speakerSvg);
3804
- this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
3805
- this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3806
- }
3935
+ if (!this.ttsButton || !this.ttsManager) return;
3936
+ if (!this.ttsManager.isAvailable) {
3937
+ this.ttsButton.style.display = "none";
3938
+ return;
3939
+ }
3940
+ this.ttsButton.style.display = "flex";
3941
+ clearElement(this.ttsButton);
3942
+ const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3943
+ if (this.ttsManager.isPlaying) {
3944
+ const pauseSvg = svg(
3945
+ "svg",
3946
+ {
3947
+ xmlns: "http://www.w3.org/2000/svg",
3948
+ width: "16",
3949
+ height: "16",
3950
+ viewBox: "0 0 24 24",
3951
+ fill: "none",
3952
+ stroke: strokeColor,
3953
+ "stroke-width": "2",
3954
+ "stroke-linecap": "round",
3955
+ "stroke-linejoin": "round",
3956
+ "aria-hidden": "true"
3957
+ },
3958
+ svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3959
+ svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3960
+ );
3961
+ this.ttsButton.appendChild(pauseSvg);
3962
+ this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3963
+ this.ttsButton.setAttribute("title", "Pause Talkback");
3964
+ } else {
3965
+ const speakerSvg = svg(
3966
+ "svg",
3967
+ {
3968
+ xmlns: "http://www.w3.org/2000/svg",
3969
+ width: "16",
3970
+ height: "16",
3971
+ viewBox: "0 0 24 24",
3972
+ fill: "none",
3973
+ stroke: strokeColor,
3974
+ "stroke-width": "2",
3975
+ "stroke-linecap": "round",
3976
+ "stroke-linejoin": "round",
3977
+ "aria-hidden": "true"
3978
+ },
3979
+ svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
3980
+ svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
3981
+ );
3982
+ this.ttsButton.appendChild(speakerSvg);
3983
+ this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
3984
+ this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3807
3985
  }
3808
3986
  }
3809
3987
  updateButtonStates(state) {
@@ -4123,6 +4301,8 @@ function renderInlinePurposes(purposes, options) {
4123
4301
  var InlineController = class {
4124
4302
  constructor(options) {
4125
4303
  this.targetEl = null;
4304
+ this.hostEl = null;
4305
+ this.shadowRoot = null;
4126
4306
  this.rootEl = null;
4127
4307
  this.purposeRefs = null;
4128
4308
  this.errorEl = null;
@@ -4144,7 +4324,6 @@ var InlineController = class {
4144
4324
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
4145
4325
  }
4146
4326
  async init() {
4147
- injectCheckboxStyles();
4148
4327
  if (typeof this.options.target === "string") {
4149
4328
  this.targetEl = document.querySelector(this.options.target);
4150
4329
  if (!this.targetEl) {
@@ -4153,6 +4332,12 @@ var InlineController = class {
4153
4332
  } else {
4154
4333
  this.targetEl = this.options.target;
4155
4334
  }
4335
+ this.hostEl = document.createElement("div");
4336
+ this.hostEl.setAttribute("data-redacto-consent-inline", "");
4337
+ this.hostEl.style.display = "block";
4338
+ this.targetEl.appendChild(this.hostEl);
4339
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
4340
+ injectCheckboxStyles(this.shadowRoot);
4156
4341
  this.unsubscribe = this.store.subscribe((state, prevState) => {
4157
4342
  this.handleStateChange(state, prevState);
4158
4343
  });
@@ -4165,9 +4350,11 @@ var InlineController = class {
4165
4350
  (_a = this.unsubscribe) == null ? void 0 : _a.call(this);
4166
4351
  this.mediaQuery.destroy();
4167
4352
  this.store.destroy();
4168
- if (this.targetEl && this.rootEl) {
4169
- this.targetEl.removeChild(this.rootEl);
4353
+ if (this.hostEl && this.hostEl.parentNode) {
4354
+ this.hostEl.parentNode.removeChild(this.hostEl);
4170
4355
  }
4356
+ this.hostEl = null;
4357
+ this.shadowRoot = null;
4171
4358
  this.rootEl = null;
4172
4359
  this.purposeRefs = null;
4173
4360
  }
@@ -4223,8 +4410,8 @@ var InlineController = class {
4223
4410
  this.rebuildUI();
4224
4411
  }
4225
4412
  if (state.hasAlreadyConsented || state.fetchError) {
4226
- if (this.rootEl && this.targetEl) {
4227
- this.targetEl.removeChild(this.rootEl);
4413
+ if (this.rootEl && this.shadowRoot && this.rootEl.parentNode === this.shadowRoot) {
4414
+ this.shadowRoot.removeChild(this.rootEl);
4228
4415
  this.rootEl = null;
4229
4416
  }
4230
4417
  return;
@@ -4243,7 +4430,7 @@ var InlineController = class {
4243
4430
  }
4244
4431
  renderLoading() {
4245
4432
  var _a;
4246
- if (!this.targetEl) return;
4433
+ if (!this.shadowRoot) return;
4247
4434
  const textColor = ((_a = this.options.settings) == null ? void 0 : _a.textColor) || "#344054";
4248
4435
  this.rootEl = el(
4249
4436
  "div",
@@ -4251,13 +4438,13 @@ var InlineController = class {
4251
4438
  el("div", { style: inlineStyles.loadingSpinner }),
4252
4439
  el("p", { style: { fontSize: "16px", fontWeight: 400, color: textColor } }, "Loading...")
4253
4440
  );
4254
- this.targetEl.appendChild(this.rootEl);
4441
+ this.shadowRoot.appendChild(this.rootEl);
4255
4442
  }
4256
4443
  rebuildUI() {
4257
4444
  const state = this.store.getState();
4258
- if (!state.content || !this.targetEl) return;
4259
- if (this.rootEl && this.targetEl.contains(this.rootEl)) {
4260
- this.targetEl.removeChild(this.rootEl);
4445
+ if (!state.content || !this.shadowRoot) return;
4446
+ if (this.rootEl && this.rootEl.parentNode === this.shadowRoot) {
4447
+ this.shadowRoot.removeChild(this.rootEl);
4261
4448
  }
4262
4449
  this.errorEl = el("div", {
4263
4450
  role: "alert",
@@ -4291,7 +4478,7 @@ var InlineController = class {
4291
4478
  this.errorEl,
4292
4479
  this.purposeRefs.container
4293
4480
  );
4294
- this.targetEl.appendChild(this.rootEl);
4481
+ this.shadowRoot.appendChild(this.rootEl);
4295
4482
  this.notifyValidation(state);
4296
4483
  }
4297
4484
  getTranslatedText(key, defaultText, itemId) {
@@ -4518,7 +4705,7 @@ function getErrorMessage(errorCode) {
4518
4705
  }
4519
4706
  function handleDigilockerCallback(options) {
4520
4707
  var _a, _b, _c;
4521
- injectCheckboxStyles();
4708
+ injectCheckboxStyles(document);
4522
4709
  const params = new URLSearchParams(window.location.search);
4523
4710
  const sessionToken = params.get("session_token");
4524
4711
  const urlStatus = params.get("status");