@c15t/svelte 3.0.0-alpha.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.
Files changed (274) hide show
  1. package/AGENTS.md +88 -0
  2. package/README.md +136 -0
  3. package/SKILL.md +14 -0
  4. package/dist/actions/focus-trap.d.ts +8 -0
  5. package/dist/actions/focus-trap.js +23 -0
  6. package/dist/actions/portal.d.ts +8 -0
  7. package/dist/actions/portal.js +39 -0
  8. package/dist/actions/scroll-lock.d.ts +8 -0
  9. package/dist/actions/scroll-lock.js +23 -0
  10. package/dist/components/action-button.svelte +119 -0
  11. package/dist/components/action-button.svelte.d.ts +18 -0
  12. package/dist/components/branding.svelte +139 -0
  13. package/dist/components/branding.svelte.d.ts +13 -0
  14. package/dist/components/consent-dev-tools.svelte +58 -0
  15. package/dist/components/consent-dev-tools.svelte.d.ts +4 -0
  16. package/dist/components/frame.svelte +95 -0
  17. package/dist/components/frame.svelte.d.ts +12 -0
  18. package/dist/components/iab-panel.svelte +542 -0
  19. package/dist/components/iab-panel.svelte.d.ts +16 -0
  20. package/dist/components/iab-prompt.svelte +304 -0
  21. package/dist/components/iab-prompt.svelte.d.ts +14 -0
  22. package/dist/components/iab-purpose-item.svelte +511 -0
  23. package/dist/components/iab-purpose-item.svelte.d.ts +26 -0
  24. package/dist/components/iab-stack-item.svelte +146 -0
  25. package/dist/components/iab-stack-item.svelte.d.ts +19 -0
  26. package/dist/components/iab-vendor-list.svelte +900 -0
  27. package/dist/components/iab-vendor-list.svelte.d.ts +19 -0
  28. package/dist/components/icons/c15-t-icon-only.svelte +27 -0
  29. package/dist/components/icons/c15-t-icon-only.svelte.d.ts +9 -0
  30. package/dist/components/icons/chevron-down-icon.svelte +30 -0
  31. package/dist/components/icons/chevron-down-icon.svelte.d.ts +10 -0
  32. package/dist/components/icons/chevron-right-icon.svelte +36 -0
  33. package/dist/components/icons/chevron-right-icon.svelte.d.ts +12 -0
  34. package/dist/components/icons/close-icon.svelte +40 -0
  35. package/dist/components/icons/close-icon.svelte.d.ts +10 -0
  36. package/dist/components/icons/consent-icon-only.svelte +36 -0
  37. package/dist/components/icons/consent-icon-only.svelte.d.ts +9 -0
  38. package/dist/components/icons/external-link-icon.svelte +30 -0
  39. package/dist/components/icons/external-link-icon.svelte.d.ts +8 -0
  40. package/dist/components/icons/globe-icon.svelte +39 -0
  41. package/dist/components/icons/globe-icon.svelte.d.ts +9 -0
  42. package/dist/components/icons/info-icon.svelte +42 -0
  43. package/dist/components/icons/info-icon.svelte.d.ts +9 -0
  44. package/dist/components/icons/inth-logo.svelte +38 -0
  45. package/dist/components/icons/inth-logo.svelte.d.ts +9 -0
  46. package/dist/components/icons/layers-icon.svelte +25 -0
  47. package/dist/components/icons/layers-icon.svelte.d.ts +8 -0
  48. package/dist/components/icons/legitimate-interest-icon.svelte +29 -0
  49. package/dist/components/icons/legitimate-interest-icon.svelte.d.ts +10 -0
  50. package/dist/components/icons/lock-icon.svelte +31 -0
  51. package/dist/components/icons/lock-icon.svelte.d.ts +8 -0
  52. package/dist/components/icons/search-icon.svelte +33 -0
  53. package/dist/components/icons/search-icon.svelte.d.ts +8 -0
  54. package/dist/components/inline-legal-links.svelte +95 -0
  55. package/dist/components/inline-legal-links.svelte.d.ts +10 -0
  56. package/dist/components/manager-provider.svelte +459 -0
  57. package/dist/components/manager-provider.svelte.d.ts +27 -0
  58. package/dist/components/overlay.svelte +79 -0
  59. package/dist/components/overlay.svelte.d.ts +7 -0
  60. package/dist/components/panel-link.svelte +33 -0
  61. package/dist/components/panel-link.svelte.d.ts +10 -0
  62. package/dist/components/panel-trigger.svelte +237 -0
  63. package/dist/components/panel-trigger.svelte.d.ts +16 -0
  64. package/dist/components/panel.svelte +258 -0
  65. package/dist/components/panel.svelte.d.ts +22 -0
  66. package/dist/components/policy-actions-renderer.svelte +79 -0
  67. package/dist/components/policy-actions-renderer.svelte.d.ts +19 -0
  68. package/dist/components/preferences.svelte +320 -0
  69. package/dist/components/preferences.svelte.d.ts +8 -0
  70. package/dist/components/prompt.svelte +514 -0
  71. package/dist/components/prompt.svelte.d.ts +43 -0
  72. package/dist/context.svelte.d.ts +125 -0
  73. package/dist/context.svelte.js +354 -0
  74. package/dist/devtools-options.d.ts +7 -0
  75. package/dist/devtools-options.js +1 -0
  76. package/dist/devtools.d.ts +9 -0
  77. package/dist/devtools.js +7 -0
  78. package/dist/headless.d.ts +10 -0
  79. package/dist/headless.js +7 -0
  80. package/dist/iab/styles.css +11 -0
  81. package/dist/iab-loader.d.ts +30 -0
  82. package/dist/iab-loader.js +33 -0
  83. package/dist/iab-translations.d.ts +84 -0
  84. package/dist/iab-translations.js +45 -0
  85. package/dist/iab-types.d.ts +62 -0
  86. package/dist/iab-types.js +43 -0
  87. package/dist/index.d.ts +28 -0
  88. package/dist/index.js +21 -0
  89. package/dist/kit/app.d.ts +35 -0
  90. package/dist/kit/handle.d.ts +34 -0
  91. package/dist/kit/handle.js +87 -0
  92. package/dist/kit/index.d.ts +30 -0
  93. package/dist/kit/index.js +5 -0
  94. package/dist/kit/load-consent.d.ts +57 -0
  95. package/dist/kit/load-consent.js +144 -0
  96. package/dist/kit/routes.d.ts +46 -0
  97. package/dist/kit/routes.js +203 -0
  98. package/dist/kit/types.d.ts +56 -0
  99. package/dist/kit/types.js +1 -0
  100. package/dist/primitives/accordion/accordion-item-content.svelte +35 -0
  101. package/dist/primitives/accordion/accordion-item-content.svelte.d.ts +9 -0
  102. package/dist/primitives/accordion/accordion-item-trigger.svelte +54 -0
  103. package/dist/primitives/accordion/accordion-item-trigger.svelte.d.ts +10 -0
  104. package/dist/primitives/accordion/accordion-item.svelte +61 -0
  105. package/dist/primitives/accordion/accordion-item.svelte.d.ts +11 -0
  106. package/dist/primitives/accordion/accordion-root.svelte +61 -0
  107. package/dist/primitives/accordion/accordion-root.svelte.d.ts +13 -0
  108. package/dist/primitives/accordion/context.d.ts +18 -0
  109. package/dist/primitives/accordion/context.js +23 -0
  110. package/dist/primitives/accordion/index.d.ts +29 -0
  111. package/dist/primitives/accordion/index.js +11 -0
  112. package/dist/primitives/collapsible/collapsible-content.svelte +36 -0
  113. package/dist/primitives/collapsible/collapsible-content.svelte.d.ts +9 -0
  114. package/dist/primitives/collapsible/collapsible-indicator.svelte +29 -0
  115. package/dist/primitives/collapsible/collapsible-indicator.svelte.d.ts +9 -0
  116. package/dist/primitives/collapsible/collapsible-root.svelte +70 -0
  117. package/dist/primitives/collapsible/collapsible-root.svelte.d.ts +11 -0
  118. package/dist/primitives/collapsible/collapsible-trigger.svelte +52 -0
  119. package/dist/primitives/collapsible/collapsible-trigger.svelte.d.ts +10 -0
  120. package/dist/primitives/collapsible/context.d.ts +10 -0
  121. package/dist/primitives/collapsible/context.js +12 -0
  122. package/dist/primitives/collapsible/index.d.ts +26 -0
  123. package/dist/primitives/collapsible/index.js +11 -0
  124. package/dist/primitives/dialog/context.d.ts +14 -0
  125. package/dist/primitives/dialog/context.js +12 -0
  126. package/dist/primitives/dialog/dialog-backdrop.svelte +39 -0
  127. package/dist/primitives/dialog/dialog-backdrop.svelte.d.ts +7 -0
  128. package/dist/primitives/dialog/dialog-close-trigger.svelte +48 -0
  129. package/dist/primitives/dialog/dialog-close-trigger.svelte.d.ts +11 -0
  130. package/dist/primitives/dialog/dialog-content.svelte +81 -0
  131. package/dist/primitives/dialog/dialog-content.svelte.d.ts +9 -0
  132. package/dist/primitives/dialog/dialog-description.svelte +28 -0
  133. package/dist/primitives/dialog/dialog-description.svelte.d.ts +9 -0
  134. package/dist/primitives/dialog/dialog-positioner.svelte +33 -0
  135. package/dist/primitives/dialog/dialog-positioner.svelte.d.ts +9 -0
  136. package/dist/primitives/dialog/dialog-root.svelte +117 -0
  137. package/dist/primitives/dialog/dialog-root.svelte.d.ts +14 -0
  138. package/dist/primitives/dialog/dialog-title.svelte +28 -0
  139. package/dist/primitives/dialog/dialog-title.svelte.d.ts +9 -0
  140. package/dist/primitives/dialog/index.d.ts +50 -0
  141. package/dist/primitives/dialog/index.js +19 -0
  142. package/dist/primitives/index.d.ts +7 -0
  143. package/dist/primitives/index.js +7 -0
  144. package/dist/primitives/portal/index.d.ts +4 -0
  145. package/dist/primitives/portal/index.js +2 -0
  146. package/dist/primitives/portal/portal.svelte +23 -0
  147. package/dist/primitives/portal/portal.svelte.d.ts +9 -0
  148. package/dist/primitives/preference-item/context.d.ts +11 -0
  149. package/dist/primitives/preference-item/context.js +12 -0
  150. package/dist/primitives/preference-item/index.d.ts +54 -0
  151. package/dist/primitives/preference-item/index.js +21 -0
  152. package/dist/primitives/preference-item/preference-item-auxiliary.svelte +33 -0
  153. package/dist/primitives/preference-item/preference-item-auxiliary.svelte.d.ts +9 -0
  154. package/dist/primitives/preference-item/preference-item-content.svelte +68 -0
  155. package/dist/primitives/preference-item/preference-item-content.svelte.d.ts +14 -0
  156. package/dist/primitives/preference-item/preference-item-control.svelte +33 -0
  157. package/dist/primitives/preference-item/preference-item-control.svelte.d.ts +9 -0
  158. package/dist/primitives/preference-item/preference-item-header.svelte +33 -0
  159. package/dist/primitives/preference-item/preference-item-header.svelte.d.ts +9 -0
  160. package/dist/primitives/preference-item/preference-item-leading.svelte +33 -0
  161. package/dist/primitives/preference-item/preference-item-leading.svelte.d.ts +9 -0
  162. package/dist/primitives/preference-item/preference-item-meta.svelte +33 -0
  163. package/dist/primitives/preference-item/preference-item-meta.svelte.d.ts +9 -0
  164. package/dist/primitives/preference-item/preference-item-root.svelte +82 -0
  165. package/dist/primitives/preference-item/preference-item-root.svelte.d.ts +12 -0
  166. package/dist/primitives/preference-item/preference-item-title.svelte +33 -0
  167. package/dist/primitives/preference-item/preference-item-title.svelte.d.ts +9 -0
  168. package/dist/primitives/preference-item/preference-item-trigger.svelte +62 -0
  169. package/dist/primitives/preference-item/preference-item-trigger.svelte.d.ts +10 -0
  170. package/dist/primitives/switch/context.d.ts +7 -0
  171. package/dist/primitives/switch/context.js +12 -0
  172. package/dist/primitives/switch/index.d.ts +25 -0
  173. package/dist/primitives/switch/index.js +11 -0
  174. package/dist/primitives/switch/switch-control.svelte +21 -0
  175. package/dist/primitives/switch/switch-control.svelte.d.ts +9 -0
  176. package/dist/primitives/switch/switch-hidden-input.svelte +31 -0
  177. package/dist/primitives/switch/switch-hidden-input.svelte.d.ts +8 -0
  178. package/dist/primitives/switch/switch-root.svelte +86 -0
  179. package/dist/primitives/switch/switch-root.svelte.d.ts +12 -0
  180. package/dist/primitives/switch/switch-thumb.svelte +14 -0
  181. package/dist/primitives/switch/switch-thumb.svelte.d.ts +7 -0
  182. package/dist/primitives/tabs/context.d.ts +11 -0
  183. package/dist/primitives/tabs/context.js +12 -0
  184. package/dist/primitives/tabs/index.d.ts +32 -0
  185. package/dist/primitives/tabs/index.js +11 -0
  186. package/dist/primitives/tabs/tabs-content.svelte +45 -0
  187. package/dist/primitives/tabs/tabs-content.svelte.d.ts +11 -0
  188. package/dist/primitives/tabs/tabs-list.svelte +38 -0
  189. package/dist/primitives/tabs/tabs-list.svelte.d.ts +10 -0
  190. package/dist/primitives/tabs/tabs-root.svelte +67 -0
  191. package/dist/primitives/tabs/tabs-root.svelte.d.ts +14 -0
  192. package/dist/primitives/tabs/tabs-trigger.svelte +108 -0
  193. package/dist/primitives/tabs/tabs-trigger.svelte.d.ts +11 -0
  194. package/dist/server/headers.d.ts +13 -0
  195. package/dist/server/headers.js +37 -0
  196. package/dist/server/index.d.ts +8 -0
  197. package/dist/server/index.js +116 -0
  198. package/dist/server/normalize-url.d.ts +23 -0
  199. package/dist/server/normalize-url.js +46 -0
  200. package/dist/server/types.d.ts +25 -0
  201. package/dist/server/types.js +1 -0
  202. package/dist/styles.css +10 -0
  203. package/dist/transports/offline.d.ts +14 -0
  204. package/dist/transports/offline.js +22 -0
  205. package/dist/types.d.ts +70 -0
  206. package/dist/types.js +1 -0
  207. package/dist/use-banner-visibility.svelte.d.ts +25 -0
  208. package/dist/use-banner-visibility.svelte.js +108 -0
  209. package/dist/utils.d.ts +25 -0
  210. package/dist/utils.js +32 -0
  211. package/dist/version.d.ts +1 -0
  212. package/dist/version.js +2 -0
  213. package/docs/README.md +88 -0
  214. package/docs/assets/v3/brand-bar.png +0 -0
  215. package/docs/assets/v3/brand-card.png +0 -0
  216. package/docs/assets/v3/choice-wall.png +0 -0
  217. package/docs/assets/v3/mobile-card.png +0 -0
  218. package/docs/assets/v3/preferences.png +0 -0
  219. package/docs/customization/overview.md +45 -0
  220. package/docs/customization/recipes.md +79 -0
  221. package/docs/customization/slots.md +55 -0
  222. package/docs/customization/tokens.md +76 -0
  223. package/docs/customization/translations.md +49 -0
  224. package/docs/frameworks/svelte/quickstart.md +133 -0
  225. package/docs/frameworks/sveltekit/quickstart.md +144 -0
  226. package/docs/guides/consent-state.md +60 -0
  227. package/docs/guides/data-fetching.md +163 -0
  228. package/docs/guides/deployment-modes.md +63 -0
  229. package/docs/guides/troubleshooting.md +68 -0
  230. package/docs/guides/verify-consent.md +62 -0
  231. package/docs/integrations/adobe-analytics.md +294 -0
  232. package/docs/integrations/ahrefs-analytics.md +291 -0
  233. package/docs/integrations/amplitude.md +292 -0
  234. package/docs/integrations/building-integrations.md +47 -0
  235. package/docs/integrations/clearbit.md +291 -0
  236. package/docs/integrations/cloudflare-web-analytics.md +292 -0
  237. package/docs/integrations/crisp.md +294 -0
  238. package/docs/integrations/databuddy.md +311 -0
  239. package/docs/integrations/fathom-analytics.md +293 -0
  240. package/docs/integrations/google-maps.md +384 -0
  241. package/docs/integrations/google-tag-manager.md +306 -0
  242. package/docs/integrations/google-tag.md +305 -0
  243. package/docs/integrations/heap.md +292 -0
  244. package/docs/integrations/hightouch.md +293 -0
  245. package/docs/integrations/hotjar.md +292 -0
  246. package/docs/integrations/intercom.md +285 -0
  247. package/docs/integrations/linkedin-insights.md +291 -0
  248. package/docs/integrations/logrocket.md +293 -0
  249. package/docs/integrations/matomo-analytics.md +316 -0
  250. package/docs/integrations/meta-pixel.md +285 -0
  251. package/docs/integrations/microsoft-clarity.md +292 -0
  252. package/docs/integrations/microsoft-uet.md +291 -0
  253. package/docs/integrations/mixpanel-analytics.md +295 -0
  254. package/docs/integrations/openai-pixel.md +441 -0
  255. package/docs/integrations/overview.md +111 -0
  256. package/docs/integrations/pirsch.md +295 -0
  257. package/docs/integrations/plausible-analytics.md +295 -0
  258. package/docs/integrations/posthog.md +397 -0
  259. package/docs/integrations/promptwatch.md +291 -0
  260. package/docs/integrations/reddit-pixel.md +294 -0
  261. package/docs/integrations/rudderstack.md +328 -0
  262. package/docs/integrations/rybbit-analytics.md +296 -0
  263. package/docs/integrations/segment.md +292 -0
  264. package/docs/integrations/snapchat-pixel.md +293 -0
  265. package/docs/integrations/tiktok-pixel.md +291 -0
  266. package/docs/integrations/umami-analytics.md +295 -0
  267. package/docs/integrations/vercel-analytics.md +293 -0
  268. package/docs/integrations/x-pixel.md +291 -0
  269. package/docs/integrations/youtube.md +405 -0
  270. package/docs/upgrade-v3.md +334 -0
  271. package/iab/styles.css +1 -0
  272. package/package.json +153 -0
  273. package/readme.json +36 -0
  274. package/styles.css +1 -0
@@ -0,0 +1,542 @@
1
+ <script lang="ts">
2
+ import {
3
+ defaultTranslationConfig,
4
+ resolveConsentPresentation,
5
+ resolveIABBannerSummary,
6
+ } from '@c15t/core';
7
+ import type { Model } from '@c15t/core';
8
+ import { isDialogDismissKey } from '@c15t/ui/primitives/dialog';
9
+ import actionStyles from '@c15t/ui/styles/components/consent-actions';
10
+ import styles from '@c15t/ui/styles/components/iab-consent-dialog';
11
+ import { buttonVariants } from '@c15t/ui/styles/primitives';
12
+ import { getTextDirection, resolveTranslations } from '@c15t/ui/utils';
13
+
14
+ import { focusTrap } from '../actions/focus-trap';
15
+ import { portal } from '../actions/portal';
16
+ import { scrollLock } from '../actions/scroll-lock';
17
+ import { getConsentContext, getThemeContext } from '../context.svelte';
18
+ import { getIABTranslations } from '../iab-translations';
19
+ import { resolveIABDialogDisplayModel } from '../iab-types';
20
+ import type { VendorId } from '../iab-types';
21
+ import { Tabs } from '../primitives';
22
+ import Branding from './branding.svelte';
23
+ import IABPurposeItem from './iab-purpose-item.svelte';
24
+ import IABStackItem from './iab-stack-item.svelte';
25
+ import IABVendorList from './iab-vendor-list.svelte';
26
+ import ChevronRightIcon from './icons/chevron-right-icon.svelte';
27
+ import CloseIcon from './icons/close-icon.svelte';
28
+ import InfoIcon from './icons/info-icon.svelte';
29
+ import LockIcon from './icons/lock-icon.svelte';
30
+ import Overlay from './overlay.svelte';
31
+
32
+ let {
33
+ open: openProp,
34
+ noStyle: localNoStyle,
35
+ hideBranding,
36
+ initialTab,
37
+ models = ['iab'] as Model[],
38
+ class: className,
39
+ }: {
40
+ open?: boolean;
41
+ noStyle?: boolean;
42
+ hideBranding?: boolean;
43
+ /**
44
+ * Which tab the preference centre opens on. Lets a "N partners"
45
+ * link land on the vendor list instead of purposes.
46
+ */
47
+ initialTab?: 'purposes' | 'vendors';
48
+ models?: Model[];
49
+ class?: string;
50
+ } = $props();
51
+
52
+ const consent = getConsentContext();
53
+ const theme = getThemeContext();
54
+
55
+ const noStyle = $derived(localNoStyle ?? theme.noStyle ?? false);
56
+
57
+ const preferences = $derived(
58
+ resolveConsentPresentation({
59
+ policy: consent.snapshot.policyRule,
60
+ presentation: {
61
+ preferences: {
62
+ scrollLock: theme.scrollLock,
63
+ trapFocus: theme.trapFocus,
64
+ ...consent.state.presentation?.preferences,
65
+ },
66
+ },
67
+ surface: 'preferences',
68
+ })
69
+ );
70
+
71
+ // IAB state
72
+ const iabState = $derived(consent.state.iab);
73
+
74
+ // Translations
75
+ const iabT = $derived(getIABTranslations(consent.state.translationConfig));
76
+ const coreTranslations = $derived(
77
+ resolveTranslations(
78
+ consent.state.translationConfig,
79
+ defaultTranslationConfig
80
+ )
81
+ );
82
+ const textDirection = $derived(
83
+ getTextDirection(consent.state.translationConfig?.defaultLanguage)
84
+ );
85
+
86
+ // Open state
87
+ const isOpen = $derived(
88
+ consent.state.hasConsentUi &&
89
+ models.includes(consent.state.model) &&
90
+ (openProp ?? consent.state.activeUI === 'dialog') &&
91
+ iabState?.config.enabled === true
92
+ );
93
+
94
+ // Tab state
95
+ let activeTab = $state<string | null>('purposes');
96
+ let selectedVendorId = $state<VendorId | null>(null);
97
+ let specialPurposesExpanded = $state(false);
98
+
99
+ // One writer for the active tab. A caller-supplied `initialTab` outranks
100
+ // the provider's remembered one — that is what makes a "N partners"
101
+ // deep link land on the vendor list — and the effect below mirrors
102
+ // whatever wins back into the provider, so a reopened dialog remembers.
103
+ $effect(() => {
104
+ if (initialTab) {
105
+ activeTab = initialTab;
106
+ return;
107
+ }
108
+ if (isOpen && iabState?.preferenceCenterTab) {
109
+ activeTab = iabState.preferenceCenterTab;
110
+ }
111
+ });
112
+
113
+ const handleClose = function handleClose() {
114
+ consent.state.setActiveUI('none');
115
+ };
116
+
117
+ const handleDialogKeydown = function handleDialogKeydown(
118
+ event: KeyboardEvent
119
+ ) {
120
+ if (isDialogDismissKey(event.key)) {
121
+ event.preventDefault();
122
+ handleClose();
123
+ }
124
+ };
125
+
126
+ $effect(() => {
127
+ if (activeTab === 'purposes' || activeTab === 'vendors') {
128
+ iabState?.setPreferenceCenterTab(activeTab);
129
+ }
130
+ });
131
+
132
+ // The rows this surface renders, from the shared display model.
133
+ const display = $derived(
134
+ resolveIABDialogDisplayModel(
135
+ iabState
136
+ ? {
137
+ customVendors: iabState.nonIABVendors ?? [],
138
+ gvl: iabState.gvl,
139
+ isLoadingGVL: iabState.isLoadingGVL,
140
+ }
141
+ : null
142
+ )
143
+ );
144
+
145
+ const summary = $derived(resolveIABBannerSummary(iabState));
146
+ const isLoading = $derived(iabState?.isLoadingGVL || !iabState?.gvl);
147
+
148
+ const handlePurposeToggle = function handlePurposeToggle(
149
+ purposeId: number,
150
+ value: boolean
151
+ ) {
152
+ iabState?.setPurposeConsent(purposeId, value);
153
+ };
154
+
155
+ const handleSpecialFeatureToggle = function handleSpecialFeatureToggle(
156
+ featureId: number,
157
+ value: boolean
158
+ ) {
159
+ iabState?.setSpecialFeatureOptIn(featureId, value);
160
+ };
161
+
162
+ const handleVendorToggle = function handleVendorToggle(
163
+ vendorId: VendorId,
164
+ value: boolean
165
+ ) {
166
+ iabState?.setVendorConsent(vendorId, value);
167
+ };
168
+
169
+ const handleVendorLegitimateInterestToggle =
170
+ function handleVendorLegitimateInterestToggle(
171
+ vendorId: VendorId,
172
+ value: boolean
173
+ ) {
174
+ iabState?.setVendorLegitimateInterest(vendorId, value);
175
+ };
176
+
177
+ const handlePurposeLegitimateInterestToggle =
178
+ function handlePurposeLegitimateInterestToggle(
179
+ purposeId: number,
180
+ value: boolean
181
+ ) {
182
+ iabState?.setPurposeLegitimateInterest(purposeId, value);
183
+ };
184
+
185
+ const handleSave = async function handleSave() {
186
+ if (!iabState) {
187
+ return;
188
+ }
189
+ try {
190
+ await iabState.save();
191
+ } catch {
192
+ // Keep the prompt available so a later action can retry the failed load/save.
193
+ }
194
+ };
195
+
196
+ const handleAcceptAll = function handleAcceptAll() {
197
+ iabState?.acceptAll();
198
+ return handleSave();
199
+ };
200
+
201
+ const handleRejectAll = function handleRejectAll() {
202
+ iabState?.rejectAll();
203
+ return handleSave();
204
+ };
205
+
206
+ const handleVendorClick = function handleVendorClick(vendorId: VendorId) {
207
+ selectedVendorId = vendorId;
208
+ activeTab = 'vendors';
209
+ iabState?.setPreferenceCenterTab('vendors');
210
+ };
211
+
212
+ const secondaryButtonClass = buttonVariants({
213
+ mode: 'stroke',
214
+ size: 'small',
215
+ variant: 'neutral',
216
+ }).root();
217
+ const primaryButtonClass = buttonVariants({
218
+ mode: 'filled',
219
+ size: 'small',
220
+ variant: 'primary',
221
+ }).root();
222
+ </script>
223
+
224
+ <!--
225
+ Plain elements, not the Ark dialog: React's IAB preference centre is
226
+ hand-rolled, and a primitive library's `data-slot`/`data-state`
227
+ bookkeeping is exactly the kind of difference the cross-framework gate
228
+ is there to catch. The behaviour the primitive provided — portal, focus
229
+ trap, scroll lock, Escape to close — comes from the same actions the
230
+ IAB banner uses.
231
+ -->
232
+ {#if isOpen}
233
+ <div use:portal>
234
+ {#if preferences.blocking}
235
+ <Overlay
236
+ variant="iab-dialog"
237
+ visible={isOpen}
238
+ />
239
+ {/if}
240
+ <div
241
+ class={noStyle
242
+ ? ''
243
+ : `${styles.root || ''} ${styles.dialogVisible || ''}`}
244
+ data-testid="iab-consent-dialog-root"
245
+ dir={textDirection}
246
+ >
247
+ <!-- A `div`, not a `dialog`: the user agent's dialog padding is
248
+ 1em, which the card sets for itself. -->
249
+ <div
250
+ class={noStyle
251
+ ? className || ''
252
+ : `${styles.card || ''} ${className || ''} ${styles.contentVisible || ''}`}
253
+ data-testid="iab-consent-dialog-card"
254
+ role="dialog"
255
+ aria-modal={preferences.blocking ? 'true' : undefined}
256
+ aria-label={iabT.preferenceCenter.title}
257
+ tabindex="-1"
258
+ use:focusTrap={preferences.blocking}
259
+ use:scrollLock={preferences.blocking}
260
+ onkeydown={handleDialogKeydown}
261
+ >
262
+ <!-- Header -->
263
+ <div class={noStyle ? '' : styles.header || ''}>
264
+ <div class={noStyle ? '' : styles.headerContent || ''}>
265
+ <h2 class={noStyle ? '' : styles.title || ''}>
266
+ {iabT.preferenceCenter.title}
267
+ </h2>
268
+ <p class={noStyle ? '' : styles.description || ''}>
269
+ {iabT.preferenceCenter.description}
270
+ </p>
271
+ </div>
272
+ <button
273
+ type="button"
274
+ class={noStyle ? '' : styles.closeButton || ''}
275
+ aria-label={coreTranslations.common.close}
276
+ data-testid="iab-consent-dialog-close"
277
+ onclick={handleClose}
278
+ >
279
+ <CloseIcon
280
+ style="height:1rem;width:1rem"
281
+ aria-hidden={true}
282
+ />
283
+ </button>
284
+ </div>
285
+
286
+ <Tabs.Root
287
+ bind:value={activeTab}
288
+ class={noStyle ? '' : styles.body || ''}
289
+ >
290
+ <div class={noStyle ? '' : styles.tabsContainer || ''}>
291
+ <Tabs.List class={noStyle ? '' : styles.tabsList || ''}>
292
+ <Tabs.Trigger
293
+ value="purposes"
294
+ class={noStyle ? '' : styles.tabButton || ''}
295
+ >
296
+ {iabT.preferenceCenter.tabs.purposes}
297
+ {#if !isLoading}
298
+ ({display.purposeTabCount})
299
+ {/if}
300
+ </Tabs.Trigger>
301
+ <Tabs.Trigger
302
+ value="vendors"
303
+ class={noStyle ? '' : styles.tabButton || ''}
304
+ >
305
+ {iabT.preferenceCenter.tabs.vendors}
306
+ {#if !isLoading}
307
+ ({display.vendorTabCount})
308
+ {/if}
309
+ </Tabs.Trigger>
310
+ <div
311
+ aria-hidden="true"
312
+ class={noStyle ? '' : styles.tabIndicator || ''}
313
+ data-active-tab={activeTab}
314
+ ></div>
315
+ </Tabs.List>
316
+ </div>
317
+
318
+ <div class={noStyle ? '' : styles.content || ''}>
319
+ <Tabs.Content
320
+ value="purposes"
321
+ forceMount
322
+ class={noStyle ? '' : styles.tabPanel || ''}
323
+ >
324
+ {#if isLoading}
325
+ <div class={noStyle ? '' : styles.loadingContainer || ''}>
326
+ <div class={noStyle ? '' : styles.loadingSpinner || ''}></div>
327
+ <p class={noStyle ? '' : styles.loadingText || ''}>
328
+ {iabT.common.loading}
329
+ </p>
330
+ </div>
331
+ {:else if display.isReady && iabState}
332
+ <!-- Purposes, stacks and special features, from the
333
+ shared display model so every adapter lists the
334
+ same rows in the same order. -->
335
+ {#each display.consentRows as row (row.testId)}
336
+ {#if row.kind === 'stack'}
337
+ <IABStackItem
338
+ stack={row}
339
+ consents={iabState.purposeConsents}
340
+ onToggle={handlePurposeToggle}
341
+ vendorConsents={iabState.vendorConsents}
342
+ onVendorToggle={handleVendorToggle}
343
+ onVendorClick={handleVendorClick}
344
+ vendorLegitimateInterests={iabState.vendorLegitimateInterests}
345
+ onVendorLegitimateInterestToggle={handleVendorLegitimateInterestToggle}
346
+ purposeLegitimateInterests={iabState.purposeLegitimateInterests}
347
+ onPurposeLegitimateInterestToggle={handlePurposeLegitimateInterestToggle}
348
+ {noStyle}
349
+ {iabT}
350
+ />
351
+ {:else if row.toggle === 'special-feature'}
352
+ <IABPurposeItem
353
+ purpose={row}
354
+ testId={row.testId}
355
+ isEnabled={iabState.specialFeatureOptIns[row.id] ?? false}
356
+ onToggle={(value) =>
357
+ handleSpecialFeatureToggle(row.id, value)}
358
+ vendorConsents={iabState.vendorConsents}
359
+ onVendorToggle={handleVendorToggle}
360
+ onVendorClick={handleVendorClick}
361
+ vendorLegitimateInterests={iabState.vendorLegitimateInterests}
362
+ onVendorLegitimateInterestToggle={handleVendorLegitimateInterestToggle}
363
+ {noStyle}
364
+ {iabT}
365
+ />
366
+ {:else}
367
+ <IABPurposeItem
368
+ purpose={row}
369
+ testId={row.testId}
370
+ isEnabled={iabState.purposeConsents[row.id] ?? false}
371
+ onToggle={(value) => handlePurposeToggle(row.id, value)}
372
+ vendorConsents={iabState.vendorConsents}
373
+ onVendorToggle={handleVendorToggle}
374
+ onVendorClick={handleVendorClick}
375
+ vendorLegitimateInterests={iabState.vendorLegitimateInterests}
376
+ onVendorLegitimateInterestToggle={handleVendorLegitimateInterestToggle}
377
+ purposeLegitimateInterests={iabState.purposeLegitimateInterests}
378
+ onPurposeLegitimateInterestToggle={handlePurposeLegitimateInterestToggle}
379
+ {noStyle}
380
+ {iabT}
381
+ />
382
+ {/if}
383
+ {/each}
384
+
385
+ <!-- Essential Functions: Special Purposes + Features (locked) -->
386
+ {#if display.essentialRows.length > 0}
387
+ <div
388
+ class={noStyle ? '' : styles.specialPurposesSection || ''}
389
+ >
390
+ <div
391
+ class={noStyle ? '' : styles.specialPurposesHeader || ''}
392
+ >
393
+ <button
394
+ type="button"
395
+ aria-expanded={specialPurposesExpanded}
396
+ class={noStyle ? '' : styles.purposeTrigger || ''}
397
+ onclick={() =>
398
+ (specialPurposesExpanded = !specialPurposesExpanded)}
399
+ >
400
+ <ChevronRightIcon
401
+ class={noStyle ? '' : styles.purposeArrow || ''}
402
+ aria-hidden={true}
403
+ expanded={specialPurposesExpanded}
404
+ />
405
+ <div class={noStyle ? '' : styles.purposeInfo || ''}>
406
+ <h3
407
+ class={noStyle
408
+ ? ''
409
+ : styles.specialPurposesTitle || ''}
410
+ >
411
+ {iabT.preferenceCenter.specialPurposes.title}
412
+ <LockIcon
413
+ class={noStyle ? '' : styles.lockIcon || ''}
414
+ aria-hidden={true}
415
+ />
416
+ </h3>
417
+ <p class={noStyle ? '' : styles.purposeMeta || ''}>
418
+ {display.essentialPartnerCount}
419
+ {display.essentialPartnerCount === 1
420
+ ? iabT.preferenceCenter.vendorList.partnerSingular
421
+ : iabT.preferenceCenter.vendorList.partnerPlural}
422
+ </p>
423
+ </div>
424
+ </button>
425
+ <div style="position:relative">
426
+ <InfoIcon
427
+ class={noStyle ? '' : styles.infoIcon || ''}
428
+ aria-label={iabT.preferenceCenter.specialPurposes
429
+ .tooltip}
430
+ />
431
+ </div>
432
+ </div>
433
+
434
+ {#if specialPurposesExpanded}
435
+ <div style="padding:0.75rem">
436
+ {#each display.essentialRows as row (row.testId)}
437
+ <IABPurposeItem
438
+ purpose={row}
439
+ testId={row.testId}
440
+ isEnabled={true}
441
+ onToggle={() => {}}
442
+ vendorConsents={iabState.vendorConsents}
443
+ onVendorToggle={handleVendorToggle}
444
+ onVendorClick={handleVendorClick}
445
+ isLocked={true}
446
+ {noStyle}
447
+ {iabT}
448
+ />
449
+ {/each}
450
+ </div>
451
+ {/if}
452
+ </div>
453
+ {/if}
454
+
455
+ <!-- Consent storage notice -->
456
+ <div class={noStyle ? '' : styles.consentNotice || ''}>
457
+ <p class={noStyle ? '' : styles.consentNoticeText || ''}>
458
+ {iabT.preferenceCenter.footer.consentStorage}
459
+ </p>
460
+ </div>
461
+ {/if}
462
+ </Tabs.Content>
463
+
464
+ <Tabs.Content
465
+ value="vendors"
466
+ forceMount
467
+ class={noStyle ? '' : styles.tabPanel || ''}
468
+ >
469
+ {#if iabState}
470
+ <IABVendorList
471
+ vendorData={iabState.gvl}
472
+ purposes={display.data.purposes}
473
+ vendorConsents={iabState.vendorConsents}
474
+ onVendorToggle={handleVendorToggle}
475
+ {selectedVendorId}
476
+ onClearSelection={() => (selectedVendorId = null)}
477
+ customVendors={iabState.nonIABVendors}
478
+ vendorLegitimateInterests={iabState.vendorLegitimateInterests}
479
+ onVendorLegitimateInterestToggle={handleVendorLegitimateInterestToggle}
480
+ {noStyle}
481
+ {iabT}
482
+ />
483
+ {/if}
484
+ </Tabs.Content>
485
+ </div>
486
+ </Tabs.Root>
487
+
488
+ <!-- Footer -->
489
+ <div
490
+ class={noStyle ? '' : `${styles.footer} ${actionStyles.actionRoot}`}
491
+ data-direction="row"
492
+ data-split
493
+ >
494
+ <div
495
+ class={noStyle ? '' : actionStyles.actionGroup}
496
+ data-direction="row"
497
+ >
498
+ <button
499
+ type="button"
500
+ class={noStyle ? '' : secondaryButtonClass}
501
+ onclick={handleRejectAll}
502
+ disabled={!summary.isReady}
503
+ data-action="reject"
504
+ >
505
+ {iabT.common.rejectAll}
506
+ </button>
507
+ <button
508
+ type="button"
509
+ class={noStyle ? '' : secondaryButtonClass}
510
+ onclick={handleAcceptAll}
511
+ disabled={!summary.isReady}
512
+ data-action="accept"
513
+ >
514
+ {iabT.common.acceptAll}
515
+ </button>
516
+ </div>
517
+ <div
518
+ class={noStyle ? '' : actionStyles.actionGroup}
519
+ data-direction="row"
520
+ >
521
+ <button
522
+ type="button"
523
+ class={noStyle ? '' : primaryButtonClass}
524
+ onclick={handleSave}
525
+ disabled={isLoading}
526
+ data-action="customize"
527
+ >
528
+ {iabT.common.saveSettings}
529
+ </button>
530
+ </div>
531
+ </div>
532
+ <Branding
533
+ {hideBranding}
534
+ {noStyle}
535
+ variant="dialog-tag"
536
+ themeKey="iabConsentDialogTag"
537
+ data-testid="iab-consent-dialog-branding"
538
+ />
539
+ </div>
540
+ </div>
541
+ </div>
542
+ {/if}
@@ -0,0 +1,16 @@
1
+ import type { Model } from '@c15t/core';
2
+ type $$ComponentProps = {
3
+ open?: boolean;
4
+ noStyle?: boolean;
5
+ hideBranding?: boolean;
6
+ /**
7
+ * Which tab the preference centre opens on. Lets a "N partners"
8
+ * link land on the vendor list instead of purposes.
9
+ */
10
+ initialTab?: 'purposes' | 'vendors';
11
+ models?: Model[];
12
+ class?: string;
13
+ };
14
+ declare const IabPanel: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type IabPanel = ReturnType<typeof IabPanel>;
16
+ export default IabPanel;