@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.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,42 @@ 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
+ Bhojpuri: "\u092D\u094B\u091C\u092A\u0941\u0930\u0940 (Bhojpuri)",
1329
+ Bodo: "\u092C\u0921\u093C\u094B (Bodo)",
1330
+ Dogri: "\u0921\u094B\u0917\u0930\u0940 (Dogri)",
1331
+ "Goan Konkani": "\u0915\u094B\u0902\u0915\u0923\u0940 (Goan Konkani)",
1332
+ Gujarati: "\u0A97\u0AC1\u0A9C\u0AB0\u0ABE\u0AA4\u0AC0 (Gujarati)",
1333
+ Hindi: "\u0939\u093F\u0928\u094D\u0926\u0940 (Hindi)",
1334
+ Kannada: "\u0C95\u0CA8\u0CCD\u0CA8\u0CA1 (Kannada)",
1335
+ Kashmiri: "\u0643\u0672\u0634\u064F\u0631 (Kashmiri)",
1336
+ Maithili: "\u092E\u0948\u0925\u093F\u0932\u0940 (Maithili)",
1337
+ Malayalam: "\u0D2E\u0D32\u0D2F\u0D3E\u0D33\u0D02 (Malayalam)",
1338
+ Manipuri: "\uABC3\uABE9\uABC7\uABE9\uABC2\uABE3\uABDF (Manipuri)",
1339
+ Marathi: "\u092E\u0930\u093E\u0920\u0940 (Marathi)",
1340
+ Nepali: "\u0928\u0947\u092A\u093E\u0932\u0940 (Nepali)",
1341
+ Odia: "\u0B13\u0B21\u0B3C\u0B3F\u0B06 (Odia)",
1342
+ Punjabi: "\u0A2A\u0A70\u0A1C\u0A3E\u0A2C\u0A40 (Punjabi)",
1343
+ Sanskrit: "\u0938\u0902\u0938\u094D\u0915\u0943\u0924\u092E\u094D (Sanskrit)",
1344
+ Santali: "\u1C65\u1C5F\u1C71\u1C5B\u1C5F\u1C72\u1C64 (Santali)",
1345
+ Sindhi: "\u0633\u0646\u068C\u064A (Sindhi)",
1346
+ Tamil: "\u0BA4\u0BAE\u0BBF\u0BB4\u0BCD (Tamil)",
1347
+ Telugu: "\u0C24\u0C46\u0C32\u0C41\u0C17\u0C41 (Telugu)",
1348
+ Urdu: "\u0627\u0631\u062F\u0648 (Urdu)"
1349
+ };
1350
+ var toNativeLabel = (name) => {
1351
+ var _a;
1352
+ return (_a = LANGUAGE_NATIVE_LABELS[name]) != null ? _a : name;
1353
+ };
1276
1354
  function renderLanguageSelector(options) {
1277
1355
  var _a, _b, _c, _d;
1278
- const { languages, selectedLanguage, settings, onSelect } = options;
1356
+ const { languages, selectedLanguage, settings, onSelect, dropdownContainer } = options;
1279
1357
  let isOpen = false;
1280
1358
  const buttonText = el("span");
1281
- buttonText.textContent = selectedLanguage;
1359
+ buttonText.textContent = toNativeLabel(selectedLanguage);
1282
1360
  const chevronSvg = svg(
1283
1361
  "svg",
1284
1362
  {
@@ -1307,7 +1385,7 @@ function renderLanguageSelector(options) {
1307
1385
  tabIndex: "0",
1308
1386
  "aria-haspopup": "listbox",
1309
1387
  "aria-expanded": "false",
1310
- "aria-label": `Language selector, current: ${selectedLanguage}`,
1388
+ "aria-label": `Language selector, current: ${toNativeLabel(selectedLanguage)}`,
1311
1389
  onClick: () => toggleDropdown(),
1312
1390
  onKeydown: (e) => {
1313
1391
  if (e.key === "Enter" || e.key === " ") {
@@ -1364,13 +1442,11 @@ function renderLanguageSelector(options) {
1364
1442
  e.target.style.backgroundColor = isSelected ? "#f3f4f6" : "#ffffff";
1365
1443
  }
1366
1444
  });
1367
- item.textContent = lang;
1445
+ item.textContent = toNativeLabel(lang);
1368
1446
  dropdown.appendChild(item);
1369
1447
  }
1370
1448
  const container = el("div", { style: styles.languageSelectorContainer }, button);
1371
- if (typeof document !== "undefined") {
1372
- document.body.appendChild(dropdown);
1373
- }
1449
+ dropdownContainer.appendChild(dropdown);
1374
1450
  function positionDropdown() {
1375
1451
  const rect = button.getBoundingClientRect();
1376
1452
  dropdown.style.top = `${rect.bottom + 4}px`;
@@ -1378,10 +1454,14 @@ function renderLanguageSelector(options) {
1378
1454
  dropdown.style.left = "auto";
1379
1455
  }
1380
1456
  const clickOutsideHandler = (e) => {
1381
- const target = e.target;
1382
- if (!container.contains(target) && !dropdown.contains(target)) {
1383
- closeDropdown();
1384
- }
1457
+ var _a2, _b2;
1458
+ const path = (_b2 = (_a2 = e.composedPath) == null ? void 0 : _a2.call(e)) != null ? _b2 : [];
1459
+ if (path.includes(container) || path.includes(dropdown)) return;
1460
+ if (path.length === 0) {
1461
+ const target = e.target;
1462
+ if (container.contains(target) || dropdown.contains(target)) return;
1463
+ }
1464
+ closeDropdown();
1385
1465
  };
1386
1466
  const repositionHandler = () => {
1387
1467
  if (isOpen) positionDropdown();
@@ -1414,6 +1494,7 @@ function renderLanguageSelector(options) {
1414
1494
  // src/modal/keyboard.ts
1415
1495
  function setupKeyboardTrap(modalEl, onEscape) {
1416
1496
  const handler = (e) => {
1497
+ var _a;
1417
1498
  if (e.key === "Escape") {
1418
1499
  e.preventDefault();
1419
1500
  onEscape();
@@ -1432,13 +1513,15 @@ function setupKeyboardTrap(modalEl, onEscape) {
1432
1513
  }
1433
1514
  const first = focusable[0];
1434
1515
  const last = focusable[focusable.length - 1];
1516
+ const root = modalEl.getRootNode();
1517
+ const active = (_a = root.activeElement) != null ? _a : document.activeElement;
1435
1518
  if (e.shiftKey) {
1436
- if (document.activeElement === first) {
1519
+ if (active === first) {
1437
1520
  e.preventDefault();
1438
1521
  last.focus();
1439
1522
  }
1440
1523
  } else {
1441
- if (document.activeElement === last) {
1524
+ if (active === last) {
1442
1525
  e.preventDefault();
1443
1526
  first.focus();
1444
1527
  }
@@ -1528,7 +1611,15 @@ var DPO_AUDIO_ELEMENT_MAP = [
1528
1611
 
1529
1612
  // src/modal/tts.ts
1530
1613
  var TTSManager = class {
1531
- constructor(onStateChange) {
1614
+ /**
1615
+ * @param onStateChange Invoked whenever playback state changes so the host
1616
+ * can refresh its UI (e.g. swap the play/pause icon).
1617
+ * @param root The root in which to look up highlightable elements by id.
1618
+ * When the modal is rendered inside a shadow root, pass that shadow root
1619
+ * so queries stay scoped to the SDK's tree (and don't accidentally hit
1620
+ * element ids that the host page also defines).
1621
+ */
1622
+ constructor(onStateChange, root) {
1532
1623
  this.audioUrls = null;
1533
1624
  this.audioSequence = [];
1534
1625
  this.elementIdSequence = [];
@@ -1540,6 +1631,7 @@ var TTSManager = class {
1540
1631
  this.highlightedElementId = null;
1541
1632
  this.abortController = null;
1542
1633
  this.onStateChange = onStateChange;
1634
+ this.root = root;
1543
1635
  }
1544
1636
  get isPlaying() {
1545
1637
  return this._isPlaying;
@@ -1552,9 +1644,11 @@ var TTSManager = class {
1552
1644
  }
1553
1645
  async fetchAudioUrls(params) {
1554
1646
  var _a;
1555
- this.stop();
1647
+ this.stopInternal();
1556
1648
  this._isAvailable = false;
1557
1649
  this.audioUrls = null;
1650
+ this.audioSequence = [];
1651
+ this.elementIdSequence = [];
1558
1652
  this.onStateChange();
1559
1653
  (_a = this.abortController) == null ? void 0 : _a.abort();
1560
1654
  this.abortController = new AbortController();
@@ -1567,13 +1661,15 @@ var TTSManager = class {
1567
1661
  signal: this.abortController.signal
1568
1662
  });
1569
1663
  this.audioUrls = response.detail;
1570
- this._isAvailable = true;
1571
1664
  this.buildSequences(params.content);
1665
+ this._isAvailable = this.audioSequence.length > 0;
1572
1666
  this.onStateChange();
1573
1667
  } catch (error) {
1574
1668
  if (error.name !== "AbortError") {
1575
1669
  this._isAvailable = false;
1576
1670
  this.audioUrls = null;
1671
+ this.audioSequence = [];
1672
+ this.elementIdSequence = [];
1577
1673
  this.onStateChange();
1578
1674
  }
1579
1675
  }
@@ -1647,6 +1743,16 @@ var TTSManager = class {
1647
1743
  }
1648
1744
  }
1649
1745
  stop() {
1746
+ this.stopInternal();
1747
+ this.onStateChange();
1748
+ }
1749
+ /**
1750
+ * Internal stop used by code paths (like fetchAudioUrls) that will fire
1751
+ * their own onStateChange after composing a richer state update — we don't
1752
+ * want the intermediate notification to flicker the UI based on a half-set
1753
+ * state (e.g. _isAvailable still true from the previous language).
1754
+ */
1755
+ stopInternal() {
1650
1756
  if (this.currentAudio) {
1651
1757
  this.currentAudio.pause();
1652
1758
  this.currentAudio.currentTime = 0;
@@ -1657,7 +1763,6 @@ var TTSManager = class {
1657
1763
  this.currentIndex = 0;
1658
1764
  this.removeHighlights();
1659
1765
  this.highlightedElementId = null;
1660
- this.onStateChange();
1661
1766
  }
1662
1767
  destroy() {
1663
1768
  var _a;
@@ -1698,7 +1803,7 @@ var TTSManager = class {
1698
1803
  this.removeHighlights();
1699
1804
  this.highlightedElementId = elementId;
1700
1805
  setTimeout(() => {
1701
- const element = document.getElementById(elementId);
1806
+ const element = "getElementById" in this.root ? this.root.getElementById(elementId) : this.root.querySelector(`#${CSS.escape(elementId)}`);
1702
1807
  if (element) {
1703
1808
  element.classList.add("redacto-tts-highlight");
1704
1809
  const rect = element.getBoundingClientRect();
@@ -1714,7 +1819,7 @@ var TTSManager = class {
1714
1819
  }, 50);
1715
1820
  }
1716
1821
  removeHighlights() {
1717
- document.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1822
+ this.root.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
1718
1823
  el2.classList.remove("redacto-tts-highlight");
1719
1824
  });
1720
1825
  }
@@ -2521,6 +2626,10 @@ function resolveColors(settings, activeConfig) {
2521
2626
  // src/modal/index.ts
2522
2627
  var ModalController = class {
2523
2628
  constructor(options) {
2629
+ this.hostEl = null;
2630
+ this.shadowRoot = null;
2631
+ this.previousBodyOverflow = null;
2632
+ this.previousHtmlOverflow = null;
2524
2633
  this.overlayEl = null;
2525
2634
  this.modalEl = null;
2526
2635
  this.purposeRefs = null;
@@ -2529,6 +2638,7 @@ var ModalController = class {
2529
2638
  this.keyboardCleanup = null;
2530
2639
  this.abortController = null;
2531
2640
  this.unsubscribe = null;
2641
+ this.ttsManager = null;
2532
2642
  this.guardianFormRefs = null;
2533
2643
  this.statusPollTimeout = null;
2534
2644
  this.popupCheckInterval = null;
@@ -2580,10 +2690,8 @@ var ModalController = class {
2580
2690
  guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
2581
2691
  });
2582
2692
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
2583
- this.ttsManager = new TTSManager(() => this.updateTTSButton());
2584
2693
  }
2585
2694
  async init() {
2586
- injectCheckboxStyles();
2587
2695
  this.unsubscribe = this.store.subscribe((state, prevState) => {
2588
2696
  this.handleStateChange(state, prevState);
2589
2697
  });
@@ -2597,18 +2705,22 @@ var ModalController = class {
2597
2705
  (_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
2598
2706
  (_c = this.unsubscribe) == null ? void 0 : _c.call(this);
2599
2707
  this.mediaQuery.destroy();
2600
- this.ttsManager.destroy();
2708
+ (_d = this.ttsManager) == null ? void 0 : _d.destroy();
2601
2709
  this.stopPolling();
2602
2710
  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);
2711
+ if (this.hostEl && this.hostEl.parentNode) {
2712
+ this.hostEl.parentNode.removeChild(this.hostEl);
2605
2713
  }
2606
- if (this.overlayEl && document.body.contains(this.overlayEl)) {
2607
- document.body.removeChild(this.overlayEl);
2714
+ if (this.previousBodyOverflow !== null) {
2715
+ document.body.style.overflow = this.previousBodyOverflow;
2716
+ this.previousBodyOverflow = null;
2608
2717
  }
2609
- if (this.modalEl && document.body.contains(this.modalEl)) {
2610
- document.body.removeChild(this.modalEl);
2718
+ if (this.previousHtmlOverflow !== null) {
2719
+ document.documentElement.style.overflow = this.previousHtmlOverflow;
2720
+ this.previousHtmlOverflow = null;
2611
2721
  }
2722
+ this.hostEl = null;
2723
+ this.shadowRoot = null;
2612
2724
  this.overlayEl = null;
2613
2725
  this.modalEl = null;
2614
2726
  this.purposeRefs = null;
@@ -2777,13 +2889,32 @@ var ModalController = class {
2777
2889
  }
2778
2890
  }
2779
2891
  renderOverlayWithLoading() {
2892
+ this.hostEl = document.createElement("div");
2893
+ this.hostEl.setAttribute("data-redacto-consent", "");
2894
+ this.hostEl.style.position = "fixed";
2895
+ this.hostEl.style.top = "0";
2896
+ this.hostEl.style.left = "0";
2897
+ this.hostEl.style.width = "0";
2898
+ this.hostEl.style.height = "0";
2899
+ this.hostEl.style.zIndex = "2147483646";
2900
+ document.body.appendChild(this.hostEl);
2901
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
2902
+ this.previousBodyOverflow = document.body.style.overflow;
2903
+ this.previousHtmlOverflow = document.documentElement.style.overflow;
2904
+ document.body.style.overflow = "hidden";
2905
+ document.documentElement.style.overflow = "hidden";
2906
+ injectCheckboxStyles(this.shadowRoot);
2907
+ this.ttsManager = new TTSManager(
2908
+ () => this.updateTTSButton(),
2909
+ this.shadowRoot
2910
+ );
2780
2911
  const blockUI = this.options.blockUI !== false;
2781
2912
  if (blockUI) {
2782
2913
  this.overlayEl = el("div", {
2783
2914
  style: styles.overlay,
2784
2915
  role: "presentation"
2785
2916
  });
2786
- document.body.appendChild(this.overlayEl);
2917
+ this.shadowRoot.appendChild(this.overlayEl);
2787
2918
  }
2788
2919
  const settingsObj = this.options.settings;
2789
2920
  const modalStyle = { ...styles.modal };
@@ -2809,10 +2940,10 @@ var ModalController = class {
2809
2940
  );
2810
2941
  const contentEl = el("div", { style: styles.content }, loadingEl);
2811
2942
  this.modalEl.appendChild(contentEl);
2812
- document.body.appendChild(this.modalEl);
2943
+ this.shadowRoot.appendChild(this.modalEl);
2813
2944
  }
2814
2945
  rebuildUI() {
2815
- var _a, _b, _c, _d, _e, _f, _g;
2946
+ var _a, _b, _c, _d, _e, _f, _g, _h;
2816
2947
  const state = this.store.getState();
2817
2948
  if (!state.content || !this.modalEl) return;
2818
2949
  const activeConfig = state.content.detail.active_config;
@@ -2893,15 +3024,22 @@ var ModalController = class {
2893
3024
  const textColor = colors.text;
2894
3025
  const linkColor = colors.link;
2895
3026
  const font = colors.font;
2896
- const logoImg = activeConfig.logo_url ? el("img", { src: activeConfig.logo_url, style: styles.logo, alt: "Logo" }) : null;
3027
+ const logoImg = activeConfig.logo_url ? el("img", {
3028
+ src: activeConfig.logo_url,
3029
+ style: {
3030
+ ...styles.logo,
3031
+ maxWidth: isMobile ? "96px" : "140px",
3032
+ maxHeight: isMobile ? "28px" : "32px"
3033
+ },
3034
+ alt: "Logo"
3035
+ }) : null;
2897
3036
  this.noticeTitleEl = el("span", {
2898
3037
  id: "privacy-notice-title",
2899
3038
  style: { ...styles.title, color: headingColor, fontFamily: font, fontSize: isMobile ? "16px" : "18px" }
2900
3039
  });
2901
3040
  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);
3041
+ if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && this.shadowRoot && this.langSelectorRefs.dropdown.parentNode === this.shadowRoot) {
3042
+ this.shadowRoot.removeChild(this.langSelectorRefs.dropdown);
2905
3043
  }
2906
3044
  const supportedLangs = Object.keys(activeConfig.supported_languages_and_translations || {});
2907
3045
  const allLanguages = [activeConfig.default_language || "English", ...supportedLangs.filter((l) => l !== activeConfig.default_language)];
@@ -2909,7 +3047,8 @@ var ModalController = class {
2909
3047
  languages: allLanguages,
2910
3048
  selectedLanguage: state.selectedLanguage,
2911
3049
  settings: settingsObj,
2912
- onSelect: (lang) => this.handleLanguageSelect(lang)
3050
+ onSelect: (lang) => this.handleLanguageSelect(lang),
3051
+ dropdownContainer: this.shadowRoot
2913
3052
  });
2914
3053
  const ttsStrokeColor = ((_f = (_e = settingsObj == null ? void 0 : settingsObj.button) == null ? void 0 : _e.language) == null ? void 0 : _f.textColor) || "currentColor";
2915
3054
  const speakerSvg = svg(
@@ -2940,10 +3079,13 @@ var ModalController = class {
2940
3079
  type: "button",
2941
3080
  "aria-label": "Start audio playback",
2942
3081
  title: "Start Talkback",
2943
- onClick: () => this.ttsManager.toggle()
3082
+ onClick: () => {
3083
+ var _a2;
3084
+ return (_a2 = this.ttsManager) == null ? void 0 : _a2.toggle();
3085
+ }
2944
3086
  });
2945
3087
  this.ttsButton.appendChild(speakerSvg);
2946
- this.ttsManager.fetchAudioUrls({
3088
+ (_g = this.ttsManager) == null ? void 0 : _g.fetchAudioUrls({
2947
3089
  accessToken: this.options.accessToken,
2948
3090
  baseUrl: this.options.baseUrl,
2949
3091
  noticeUuid: activeConfig.notice_uuid,
@@ -2951,9 +3093,44 @@ var ModalController = class {
2951
3093
  content: activeConfig
2952
3094
  });
2953
3095
  const topRight = el("div", {
2954
- style: { display: "flex", alignItems: "center", gap: "8px" }
3096
+ style: { display: "flex", alignItems: "center", gap: "8px", flexShrink: 0 }
2955
3097
  }, this.ttsButton, this.langSelectorRefs.container);
2956
- const topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3098
+ let topSection;
3099
+ if (isMobile) {
3100
+ const headerRow = el(
3101
+ "div",
3102
+ {
3103
+ style: {
3104
+ display: "flex",
3105
+ alignItems: "center",
3106
+ gap: "10px",
3107
+ width: "100%"
3108
+ }
3109
+ },
3110
+ ...logoImg ? [logoImg] : [],
3111
+ el(
3112
+ "div",
3113
+ { style: { marginLeft: "auto", display: "flex", alignItems: "center" } },
3114
+ topRight
3115
+ )
3116
+ );
3117
+ topSection = el(
3118
+ "div",
3119
+ {
3120
+ style: {
3121
+ ...styles.topSection,
3122
+ flexDirection: "column",
3123
+ alignItems: "stretch",
3124
+ flexWrap: "nowrap"
3125
+ }
3126
+ },
3127
+ headerRow,
3128
+ this.noticeTitleEl
3129
+ );
3130
+ } else {
3131
+ const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
3132
+ topSection = el("div", { style: styles.topSection }, topLeft, topRight);
3133
+ }
2957
3134
  this.middleSection = el("div", { style: styles.middleSection });
2958
3135
  const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
2959
3136
  const noticeTextSpan = el("span", { id: "privacy-notice-text" });
@@ -3198,7 +3375,7 @@ var ModalController = class {
3198
3375
  );
3199
3376
  this.modalEl.appendChild(contentWrapper);
3200
3377
  this.updateAcceptButtonState(state);
3201
- (_g = this.keyboardCleanup) == null ? void 0 : _g.call(this);
3378
+ (_h = this.keyboardCleanup) == null ? void 0 : _h.call(this);
3202
3379
  this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
3203
3380
  }
3204
3381
  getLanguageCode(displayLanguage) {
@@ -3756,54 +3933,56 @@ var ModalController = class {
3756
3933
  // ─── TTS ───
3757
3934
  updateTTSButton() {
3758
3935
  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
- }
3936
+ if (!this.ttsButton || !this.ttsManager) return;
3937
+ if (!this.ttsManager.isAvailable) {
3938
+ this.ttsButton.style.display = "none";
3939
+ return;
3940
+ }
3941
+ this.ttsButton.style.display = "flex";
3942
+ clearElement(this.ttsButton);
3943
+ const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
3944
+ if (this.ttsManager.isPlaying) {
3945
+ const pauseSvg = svg(
3946
+ "svg",
3947
+ {
3948
+ xmlns: "http://www.w3.org/2000/svg",
3949
+ width: "16",
3950
+ height: "16",
3951
+ viewBox: "0 0 24 24",
3952
+ fill: "none",
3953
+ stroke: strokeColor,
3954
+ "stroke-width": "2",
3955
+ "stroke-linecap": "round",
3956
+ "stroke-linejoin": "round",
3957
+ "aria-hidden": "true"
3958
+ },
3959
+ svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
3960
+ svg("rect", { x: "14", y: "4", width: "4", height: "16" })
3961
+ );
3962
+ this.ttsButton.appendChild(pauseSvg);
3963
+ this.ttsButton.setAttribute("aria-label", "Pause audio playback");
3964
+ this.ttsButton.setAttribute("title", "Pause Talkback");
3965
+ } else {
3966
+ const speakerSvg = svg(
3967
+ "svg",
3968
+ {
3969
+ xmlns: "http://www.w3.org/2000/svg",
3970
+ width: "16",
3971
+ height: "16",
3972
+ viewBox: "0 0 24 24",
3973
+ fill: "none",
3974
+ stroke: strokeColor,
3975
+ "stroke-width": "2",
3976
+ "stroke-linecap": "round",
3977
+ "stroke-linejoin": "round",
3978
+ "aria-hidden": "true"
3979
+ },
3980
+ svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
3981
+ svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
3982
+ );
3983
+ this.ttsButton.appendChild(speakerSvg);
3984
+ this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
3985
+ this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
3807
3986
  }
3808
3987
  }
3809
3988
  updateButtonStates(state) {
@@ -4123,6 +4302,8 @@ function renderInlinePurposes(purposes, options) {
4123
4302
  var InlineController = class {
4124
4303
  constructor(options) {
4125
4304
  this.targetEl = null;
4305
+ this.hostEl = null;
4306
+ this.shadowRoot = null;
4126
4307
  this.rootEl = null;
4127
4308
  this.purposeRefs = null;
4128
4309
  this.errorEl = null;
@@ -4144,7 +4325,6 @@ var InlineController = class {
4144
4325
  this.mediaQuery = createMediaQuery("(max-width: 768px)");
4145
4326
  }
4146
4327
  async init() {
4147
- injectCheckboxStyles();
4148
4328
  if (typeof this.options.target === "string") {
4149
4329
  this.targetEl = document.querySelector(this.options.target);
4150
4330
  if (!this.targetEl) {
@@ -4153,6 +4333,12 @@ var InlineController = class {
4153
4333
  } else {
4154
4334
  this.targetEl = this.options.target;
4155
4335
  }
4336
+ this.hostEl = document.createElement("div");
4337
+ this.hostEl.setAttribute("data-redacto-consent-inline", "");
4338
+ this.hostEl.style.display = "block";
4339
+ this.targetEl.appendChild(this.hostEl);
4340
+ this.shadowRoot = this.hostEl.attachShadow({ mode: "open" });
4341
+ injectCheckboxStyles(this.shadowRoot);
4156
4342
  this.unsubscribe = this.store.subscribe((state, prevState) => {
4157
4343
  this.handleStateChange(state, prevState);
4158
4344
  });
@@ -4165,9 +4351,11 @@ var InlineController = class {
4165
4351
  (_a = this.unsubscribe) == null ? void 0 : _a.call(this);
4166
4352
  this.mediaQuery.destroy();
4167
4353
  this.store.destroy();
4168
- if (this.targetEl && this.rootEl) {
4169
- this.targetEl.removeChild(this.rootEl);
4354
+ if (this.hostEl && this.hostEl.parentNode) {
4355
+ this.hostEl.parentNode.removeChild(this.hostEl);
4170
4356
  }
4357
+ this.hostEl = null;
4358
+ this.shadowRoot = null;
4171
4359
  this.rootEl = null;
4172
4360
  this.purposeRefs = null;
4173
4361
  }
@@ -4223,8 +4411,8 @@ var InlineController = class {
4223
4411
  this.rebuildUI();
4224
4412
  }
4225
4413
  if (state.hasAlreadyConsented || state.fetchError) {
4226
- if (this.rootEl && this.targetEl) {
4227
- this.targetEl.removeChild(this.rootEl);
4414
+ if (this.rootEl && this.shadowRoot && this.rootEl.parentNode === this.shadowRoot) {
4415
+ this.shadowRoot.removeChild(this.rootEl);
4228
4416
  this.rootEl = null;
4229
4417
  }
4230
4418
  return;
@@ -4243,7 +4431,7 @@ var InlineController = class {
4243
4431
  }
4244
4432
  renderLoading() {
4245
4433
  var _a;
4246
- if (!this.targetEl) return;
4434
+ if (!this.shadowRoot) return;
4247
4435
  const textColor = ((_a = this.options.settings) == null ? void 0 : _a.textColor) || "#344054";
4248
4436
  this.rootEl = el(
4249
4437
  "div",
@@ -4251,13 +4439,13 @@ var InlineController = class {
4251
4439
  el("div", { style: inlineStyles.loadingSpinner }),
4252
4440
  el("p", { style: { fontSize: "16px", fontWeight: 400, color: textColor } }, "Loading...")
4253
4441
  );
4254
- this.targetEl.appendChild(this.rootEl);
4442
+ this.shadowRoot.appendChild(this.rootEl);
4255
4443
  }
4256
4444
  rebuildUI() {
4257
4445
  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);
4446
+ if (!state.content || !this.shadowRoot) return;
4447
+ if (this.rootEl && this.rootEl.parentNode === this.shadowRoot) {
4448
+ this.shadowRoot.removeChild(this.rootEl);
4261
4449
  }
4262
4450
  this.errorEl = el("div", {
4263
4451
  role: "alert",
@@ -4291,7 +4479,7 @@ var InlineController = class {
4291
4479
  this.errorEl,
4292
4480
  this.purposeRefs.container
4293
4481
  );
4294
- this.targetEl.appendChild(this.rootEl);
4482
+ this.shadowRoot.appendChild(this.rootEl);
4295
4483
  this.notifyValidation(state);
4296
4484
  }
4297
4485
  getTranslatedText(key, defaultText, itemId) {
@@ -4518,7 +4706,7 @@ function getErrorMessage(errorCode) {
4518
4706
  }
4519
4707
  function handleDigilockerCallback(options) {
4520
4708
  var _a, _b, _c;
4521
- injectCheckboxStyles();
4709
+ injectCheckboxStyles(document);
4522
4710
  const params = new URLSearchParams(window.location.search);
4523
4711
  const sessionToken = params.get("session_token");
4524
4712
  const urlStatus = params.get("status");