@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,10 @@
1
+ import type { Snippet } from 'svelte';
2
+ type $$ComponentProps = {
3
+ children: Snippet;
4
+ noStyle?: boolean;
5
+ 'data-testid'?: string;
6
+ [key: string]: unknown;
7
+ };
8
+ declare const PanelLink: import("svelte").Component<$$ComponentProps, {}, "">;
9
+ type PanelLink = ReturnType<typeof PanelLink>;
10
+ export default PanelLink;
@@ -0,0 +1,237 @@
1
+ <script lang="ts">
2
+ import styles from '@c15t/ui/styles/components/consent-dialog-trigger';
3
+ import {
4
+ calculateCornerFromDrag,
5
+ createInitialDragState,
6
+ getPersistedPosition,
7
+ persistPosition as persistToStorage,
8
+ } from '@c15t/ui/utils';
9
+ import type { CornerPosition, DragState } from '@c15t/ui/utils';
10
+ import { onMount, untrack } from 'svelte';
11
+
12
+ import { portal } from '../actions/portal';
13
+ import { getConsentContext } from '../context.svelte';
14
+ import C15TIconOnly from './icons/c15-t-icon-only.svelte';
15
+ import ConsentIconOnly from './icons/consent-icon-only.svelte';
16
+
17
+ type TriggerVisibility = 'always' | 'never';
18
+
19
+ let {
20
+ defaultPosition = 'bottom-right' as CornerPosition,
21
+ persistPosition = true,
22
+ showWhen = 'always' as TriggerVisibility,
23
+ size = 'md' as 'sm' | 'md' | 'lg',
24
+ ariaLabel = 'Open privacy settings',
25
+ noStyle = false,
26
+ class: className,
27
+ onclick,
28
+ onPositionChange,
29
+ }: {
30
+ defaultPosition?: CornerPosition;
31
+ persistPosition?: boolean;
32
+ showWhen?: TriggerVisibility;
33
+ size?: 'sm' | 'md' | 'lg';
34
+ ariaLabel?: string;
35
+ noStyle?: boolean;
36
+ class?: string;
37
+ onclick?: (e: MouseEvent) => void;
38
+ onPositionChange?: (position: CornerPosition) => void;
39
+ } = $props();
40
+
41
+ const consent = getConsentContext();
42
+
43
+ let corner: CornerPosition = $state(untrack(() => defaultPosition));
44
+
45
+ // Drag state
46
+ let dragState: DragState = $state(createInitialDragState());
47
+ let isSnapping = $state(false);
48
+ let hasDragged = $state(false);
49
+ let dragStartTime = $state(0);
50
+ let capturedElement: HTMLElement | null = null;
51
+
52
+ onMount(() => {
53
+ if (persistPosition) {
54
+ const persisted = getPersistedPosition();
55
+ if (persisted) {
56
+ corner = persisted;
57
+ }
58
+ }
59
+ });
60
+
61
+ const branding = $derived(consent.state.branding);
62
+ // Nothing to manage without a resolved policy.
63
+ const visible = $derived(
64
+ consent.state.hasConsentUi &&
65
+ showWhen !== 'never' &&
66
+ consent.snapshot.activeUI !== 'dialog'
67
+ );
68
+
69
+ // Position class mapping
70
+ const cornerClassMap: Record<CornerPosition, string> = {
71
+ 'bottom-left': styles.bottomLeft || '',
72
+ 'bottom-right': styles.bottomRight || '',
73
+ 'top-left': styles.topLeft || '',
74
+ 'top-right': styles.topRight || '',
75
+ };
76
+
77
+ const sizeClassMap: Record<string, string> = {
78
+ lg: styles.lg || '',
79
+ md: styles.md || '',
80
+ sm: styles.sm || '',
81
+ };
82
+
83
+ const positionClass = $derived(cornerClassMap[corner] || '');
84
+
85
+ // Drag style
86
+ const dragStyle = $derived(
87
+ dragState.isDragging
88
+ ? `transform: translate(${dragState.currentX - dragState.startX}px, ${dragState.currentY - dragState.startY}px); transition: none;`
89
+ : 'transform: none;'
90
+ );
91
+
92
+ const updateCorner = function updateCorner(newCorner: CornerPosition) {
93
+ corner = newCorner;
94
+ if (persistPosition) {
95
+ persistToStorage(newCorner);
96
+ }
97
+ onPositionChange?.(newCorner);
98
+ };
99
+
100
+ const handlePointerDown = function handlePointerDown(e: PointerEvent) {
101
+ if (e.button !== 0) {
102
+ return;
103
+ }
104
+
105
+ (e.target as HTMLElement).setPointerCapture(e.pointerId);
106
+ capturedElement = e.target as HTMLElement;
107
+ hasDragged = false;
108
+ dragStartTime = Date.now();
109
+
110
+ dragState = {
111
+ currentX: e.clientX,
112
+ currentY: e.clientY,
113
+ isDragging: true,
114
+ startX: e.clientX,
115
+ startY: e.clientY,
116
+ };
117
+
118
+ isSnapping = false;
119
+ };
120
+
121
+ const handlePointerMove = function handlePointerMove(e: PointerEvent) {
122
+ if (!dragState.isDragging) {
123
+ return;
124
+ }
125
+
126
+ const dx = Math.abs(e.clientX - dragState.startX);
127
+ const dy = Math.abs(e.clientY - dragState.startY);
128
+ if (dx > 5 || dy > 5) {
129
+ hasDragged = true;
130
+ }
131
+
132
+ dragState = {
133
+ ...dragState,
134
+ currentX: e.clientX,
135
+ currentY: e.clientY,
136
+ };
137
+ };
138
+
139
+ const handlePointerUp = function handlePointerUp(e: PointerEvent) {
140
+ if (capturedElement) {
141
+ capturedElement.releasePointerCapture(e.pointerId);
142
+ capturedElement = null;
143
+ }
144
+
145
+ if (!dragState.isDragging) {
146
+ return;
147
+ }
148
+
149
+ if (hasDragged) {
150
+ const dragX = e.clientX - dragState.startX;
151
+ const dragY = e.clientY - dragState.startY;
152
+ const dragDuration = Date.now() - dragStartTime;
153
+
154
+ const velocityX = dragDuration > 0 ? dragX / dragDuration : 0;
155
+ const velocityY = dragDuration > 0 ? dragY / dragDuration : 0;
156
+
157
+ const newCorner = calculateCornerFromDrag(corner, dragX, dragY, {
158
+ velocityX,
159
+ velocityY,
160
+ });
161
+
162
+ if (newCorner !== corner) {
163
+ isSnapping = true;
164
+ setTimeout(() => {
165
+ isSnapping = false;
166
+ }, 300);
167
+ updateCorner(newCorner);
168
+ }
169
+ }
170
+
171
+ dragState = createInitialDragState();
172
+ };
173
+
174
+ const handlePointerCancel = function handlePointerCancel(e: PointerEvent) {
175
+ if (capturedElement) {
176
+ capturedElement.releasePointerCapture(e.pointerId);
177
+ capturedElement = null;
178
+ }
179
+ dragState = createInitialDragState();
180
+ };
181
+
182
+ const handleClick = function handleClick(e: MouseEvent) {
183
+ // Don't open dialog if this was a drag interaction
184
+ if (hasDragged) {
185
+ return;
186
+ }
187
+ onclick?.(e);
188
+ if (!e.defaultPrevented) {
189
+ consent.state.setActiveUI('dialog');
190
+ }
191
+ };
192
+
193
+ const buttonClasses = $derived(
194
+ noStyle
195
+ ? className
196
+ : [
197
+ styles.trigger,
198
+ positionClass,
199
+ sizeClassMap[size],
200
+ dragState.isDragging && styles.dragging,
201
+ isSnapping && styles.snapping,
202
+ className,
203
+ ]
204
+ .filter(Boolean)
205
+ .join(' ')
206
+ );
207
+ </script>
208
+
209
+ {#if visible}
210
+ <div use:portal>
211
+ <button
212
+ type="button"
213
+ class={buttonClasses}
214
+ style={dragStyle}
215
+ data-c15t-trigger="true"
216
+ data-c15t-rights={consent.snapshot.policyRule.rights.join(' ')}
217
+ aria-label={ariaLabel}
218
+ onclick={handleClick}
219
+ onpointerdown={handlePointerDown}
220
+ onpointermove={handlePointerMove}
221
+ onpointerup={handlePointerUp}
222
+ onpointercancel={handlePointerCancel}
223
+ data-testid="consent-dialog-trigger"
224
+ >
225
+ <span
226
+ class={noStyle ? '' : styles.icon || ''}
227
+ aria-hidden="true"
228
+ >
229
+ {#if branding === 'consent'}
230
+ <ConsentIconOnly />
231
+ {:else}
232
+ <C15TIconOnly />
233
+ {/if}
234
+ </span>
235
+ </button>
236
+ </div>
237
+ {/if}
@@ -0,0 +1,16 @@
1
+ import type { CornerPosition } from '@c15t/ui/utils';
2
+ type TriggerVisibility = 'always' | 'never';
3
+ type $$ComponentProps = {
4
+ defaultPosition?: CornerPosition;
5
+ persistPosition?: boolean;
6
+ showWhen?: TriggerVisibility;
7
+ size?: 'sm' | 'md' | 'lg';
8
+ ariaLabel?: string;
9
+ noStyle?: boolean;
10
+ class?: string;
11
+ onclick?: (e: MouseEvent) => void;
12
+ onPositionChange?: (position: CornerPosition) => void;
13
+ };
14
+ declare const PanelTrigger: import("svelte").Component<$$ComponentProps, {}, "">;
15
+ type PanelTrigger = ReturnType<typeof PanelTrigger>;
16
+ export default PanelTrigger;
@@ -0,0 +1,258 @@
1
+ <script lang="ts">
2
+ import type { LegalLinks as LegalLinksType, Model } from '@c15t/core';
3
+ import {
4
+ defaultTranslationConfig,
5
+ resolveConsentPresentation,
6
+ } from '@c15t/core';
7
+ import styles from '@c15t/ui/styles/components/consent-dialog';
8
+ import { getTextDirection, resolveTranslations } from '@c15t/ui/utils';
9
+
10
+ import { getConsentContext, getThemeContext } from '../context.svelte';
11
+ import { Dialog, Portal } from '../primitives';
12
+ import { resolveComponentStyles } from '../utils';
13
+ import Branding from './branding.svelte';
14
+ import InlineLegalLinks from './inline-legal-links.svelte';
15
+ import ConsentDialogTrigger from './panel-trigger.svelte';
16
+ import ConsentWidget from './preferences.svelte';
17
+
18
+ interface ConsentDialogTriggerProps {
19
+ defaultPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
20
+ persistPosition?: boolean;
21
+ showWhen?: 'always' | 'never';
22
+ size?: 'sm' | 'md' | 'lg';
23
+ ariaLabel?: string;
24
+ noStyle?: boolean;
25
+ class?: string;
26
+ }
27
+
28
+ const {
29
+ open: openProp,
30
+ noStyle: localNoStyle,
31
+ hideBranding,
32
+ legalLinks,
33
+ showTrigger = false,
34
+ // A `none` rule that grants the preferences right still opens here.
35
+ models = ['opt-in', 'opt-out', 'iab', 'none'] as Model[],
36
+ class: className,
37
+ }: {
38
+ open?: boolean;
39
+ noStyle?: boolean;
40
+ hideBranding?: boolean;
41
+ legalLinks?: (keyof LegalLinksType)[] | null;
42
+ showTrigger?: boolean | ConsentDialogTriggerProps;
43
+ models?: Model[];
44
+ class?: string;
45
+ } = $props();
46
+
47
+ const consent = getConsentContext();
48
+ const theme = getThemeContext();
49
+ const preferences = $derived(
50
+ resolveConsentPresentation({
51
+ policy: consent.snapshot.policyRule,
52
+ presentation: {
53
+ ...consent.state.presentation,
54
+ preferences: {
55
+ scrollLock: theme.scrollLock,
56
+ trapFocus: theme.trapFocus,
57
+ ...consent.state.presentation?.preferences,
58
+ },
59
+ },
60
+ surface: 'preferences',
61
+ })
62
+ );
63
+
64
+ const noStyle = $derived(localNoStyle ?? theme.noStyle ?? false);
65
+ const disableAnimation = $derived(theme.disableAnimation ?? false);
66
+
67
+ // Translations
68
+ const translations = $derived(
69
+ resolveTranslations(
70
+ consent.state.translationConfig,
71
+ defaultTranslationConfig
72
+ )
73
+ );
74
+ const textDirection = $derived(
75
+ getTextDirection(consent.state.translationConfig?.defaultLanguage)
76
+ );
77
+
78
+ // Open state
79
+ const isOpen = $derived(
80
+ consent.state.hasConsentUi &&
81
+ models.includes(consent.state.model) &&
82
+ (openProp ?? consent.state.activeUI === 'dialog')
83
+ );
84
+ let dialogOpen = $state(false);
85
+ let lastResolvedOpen = $state(false);
86
+
87
+ // Per-element theme key styling
88
+ const rootStyle = $derived(
89
+ resolveComponentStyles(
90
+ 'consentDialog',
91
+ theme.theme,
92
+ { className, noStyle },
93
+ noStyle
94
+ )
95
+ );
96
+
97
+ const cardStyle = $derived(
98
+ resolveComponentStyles(
99
+ 'consentDialogCard',
100
+ theme.theme,
101
+ { baseClassName: styles.card, noStyle },
102
+ noStyle
103
+ )
104
+ );
105
+
106
+ const headerStyle = $derived(
107
+ resolveComponentStyles(
108
+ 'consentDialogHeader',
109
+ theme.theme,
110
+ { baseClassName: styles.header, noStyle },
111
+ noStyle
112
+ )
113
+ );
114
+
115
+ const titleStyle = $derived(
116
+ resolveComponentStyles(
117
+ 'consentDialogTitle',
118
+ theme.theme,
119
+ { baseClassName: styles.title, noStyle },
120
+ noStyle
121
+ )
122
+ );
123
+
124
+ const descriptionStyle = $derived(
125
+ resolveComponentStyles(
126
+ 'consentDialogDescription',
127
+ theme.theme,
128
+ { baseClassName: styles.description, noStyle },
129
+ noStyle
130
+ )
131
+ );
132
+
133
+ const contentStyle = $derived(
134
+ resolveComponentStyles(
135
+ 'consentDialogContent',
136
+ theme.theme,
137
+ { baseClassName: styles.content, noStyle },
138
+ noStyle
139
+ )
140
+ );
141
+
142
+ // Trigger props
143
+ const triggerProps = $derived.by(() => {
144
+ if (showTrigger === true) {
145
+ return {};
146
+ }
147
+ if (showTrigger === false) {
148
+ return null;
149
+ }
150
+ return showTrigger;
151
+ });
152
+
153
+ $effect(() => {
154
+ if (isOpen !== lastResolvedOpen) {
155
+ dialogOpen = isOpen;
156
+ lastResolvedOpen = isOpen;
157
+ }
158
+ });
159
+
160
+ $effect(() => {
161
+ if (lastResolvedOpen && !dialogOpen) {
162
+ consent.state.setActiveUI('none');
163
+ lastResolvedOpen = false;
164
+ }
165
+ });
166
+ </script>
167
+
168
+ {#if triggerProps}
169
+ <ConsentDialogTrigger {...triggerProps} />
170
+ {/if}
171
+
172
+ <Dialog.Root
173
+ bind:open={dialogOpen}
174
+ closeOnInteractOutside={false}
175
+ closeOnEscape={true}
176
+ trapFocus={preferences.blocking}
177
+ preventScroll={preferences.scrollLock}
178
+ lazyMount
179
+ unmountOnExit
180
+ >
181
+ <Portal>
182
+ {#if preferences.blocking}
183
+ <Dialog.Backdrop
184
+ class={noStyle ? '' : styles.overlay || ''}
185
+ data-testid="consent-dialog-overlay"
186
+ />
187
+ {/if}
188
+ <Dialog.Positioner
189
+ class={noStyle
190
+ ? ''
191
+ : `${styles.root || ''} ${!disableAnimation ? (isOpen ? styles.dialogVisible || '' : styles.dialogHidden || '') : ''}`}
192
+ >
193
+ <Dialog.Content
194
+ class={noStyle
195
+ ? rootStyle.className || ''
196
+ : `${styles.container || ''} ${rootStyle.className || ''} ${!disableAnimation ? (isOpen ? styles.contentVisible || '' : styles.contentHidden || '') : ''}`}
197
+ dir={textDirection}
198
+ aria-labelledby="consent-dialog-title"
199
+ aria-describedby="consent-dialog-description"
200
+ data-blocking={preferences.blocking ? 'true' : undefined}
201
+ data-testid="consent-dialog-root"
202
+ >
203
+ <!-- Card -->
204
+ <!-- svelte-ignore a11y_no_noninteractive_tabindex -->
205
+ <div
206
+ class={noStyle ? '' : cardStyle.className || ''}
207
+ data-testid="consent-dialog-card"
208
+ tabindex={-1}
209
+ >
210
+ <!-- Header -->
211
+ <div
212
+ class={noStyle ? '' : headerStyle.className || ''}
213
+ data-testid="consent-dialog-header"
214
+ >
215
+ <h2
216
+ class={noStyle ? '' : titleStyle.className || ''}
217
+ data-testid="consent-dialog-title"
218
+ id="consent-dialog-title"
219
+ >
220
+ {translations.consentManagerDialog.title}
221
+ </h2>
222
+ <div
223
+ class={noStyle ? '' : descriptionStyle.className || ''}
224
+ data-context="dialog"
225
+ data-testid="consent-dialog-description"
226
+ id="consent-dialog-description"
227
+ >
228
+ {translations.consentManagerDialog.description}
229
+ <InlineLegalLinks
230
+ links={legalLinks}
231
+ themeKey="consentDialogContent"
232
+ testIdPrefix="consent-dialog-legal-link"
233
+ />
234
+ </div>
235
+ </div>
236
+
237
+ <!-- Content: ConsentWidget -->
238
+ <div
239
+ class={noStyle ? '' : contentStyle.className || ''}
240
+ data-testid="consent-dialog-content"
241
+ >
242
+ <ConsentWidget
243
+ hideBranding
244
+ {noStyle}
245
+ />
246
+ </div>
247
+ <Branding
248
+ {hideBranding}
249
+ {noStyle}
250
+ variant="dialog-tag"
251
+ themeKey="consentDialogTag"
252
+ data-testid="consent-dialog-branding"
253
+ />
254
+ </div>
255
+ </Dialog.Content>
256
+ </Dialog.Positioner>
257
+ </Portal>
258
+ </Dialog.Root>
@@ -0,0 +1,22 @@
1
+ import type { LegalLinks as LegalLinksType, Model } from '@c15t/core';
2
+ interface ConsentDialogTriggerProps {
3
+ defaultPosition?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
4
+ persistPosition?: boolean;
5
+ showWhen?: 'always' | 'never';
6
+ size?: 'sm' | 'md' | 'lg';
7
+ ariaLabel?: string;
8
+ noStyle?: boolean;
9
+ class?: string;
10
+ }
11
+ type $$ComponentProps = {
12
+ open?: boolean;
13
+ noStyle?: boolean;
14
+ hideBranding?: boolean;
15
+ legalLinks?: (keyof LegalLinksType)[] | null;
16
+ showTrigger?: boolean | ConsentDialogTriggerProps;
17
+ models?: Model[];
18
+ class?: string;
19
+ };
20
+ declare const Panel: import("svelte").Component<$$ComponentProps, {}, "">;
21
+ type Panel = ReturnType<typeof Panel>;
22
+ export default Panel;
@@ -0,0 +1,79 @@
1
+ <script lang="ts">
2
+ import type { SurfacePresentation } from '@c15t/core';
3
+ import actionStyles from '@c15t/ui/styles/components/consent-actions';
4
+ import type { Snippet } from 'svelte';
5
+
6
+ /**
7
+ * Renders policy-driven action groups using the shared `consent-actions`
8
+ * contract: layout (fill, column, split) is expressed through data
9
+ * attributes that the component CSS reads, so every framework adapter
10
+ * produces the same DOM.
11
+ */
12
+ let {
13
+ actionGroups = [],
14
+ primaryActions = [],
15
+ shouldFillActions = false,
16
+ direction = 'row',
17
+ noStyle = false,
18
+ footerClassName,
19
+ footerSubGroupClassName,
20
+ footerTestId,
21
+ footerSubGroupTestId,
22
+ leading,
23
+ renderAction,
24
+ }: {
25
+ actionGroups?: string[][];
26
+ primaryActions?: string[];
27
+ shouldFillActions?: boolean;
28
+ direction?: SurfacePresentation['direction'];
29
+ noStyle?: boolean;
30
+ footerClassName?: string;
31
+ footerSubGroupClassName?: string;
32
+ footerTestId?: string;
33
+ footerSubGroupTestId?: string;
34
+ /** Rendered inside the footer before the action groups, e.g. rights links. */
35
+ leading?: Snippet;
36
+ renderAction?: Snippet<[string, boolean]>;
37
+ } = $props();
38
+
39
+ const isSplit = $derived(actionGroups.length > 1);
40
+ const resolvedFooterClassName = $derived(
41
+ [noStyle ? '' : actionStyles.actionRoot, footerClassName]
42
+ .filter(Boolean)
43
+ .join(' ')
44
+ );
45
+ const resolvedFooterSubGroupClassName = $derived(
46
+ [noStyle ? '' : actionStyles.actionGroup, footerSubGroupClassName]
47
+ .filter(Boolean)
48
+ .join(' ')
49
+ );
50
+ const keyedActionGroups = $derived(
51
+ actionGroups.map((group, groupIndex) => ({
52
+ group,
53
+ groupIndex,
54
+ key: `${group.join('-')}-${groupIndex}`,
55
+ }))
56
+ );
57
+ </script>
58
+
59
+ <div
60
+ class={resolvedFooterClassName}
61
+ data-testid={footerTestId}
62
+ data-direction={direction}
63
+ data-fill={shouldFillActions ? true : undefined}
64
+ data-split={isSplit && !shouldFillActions ? true : undefined}
65
+ >
66
+ {@render leading?.()}
67
+ {#each keyedActionGroups as actionGroup (actionGroup.key)}
68
+ <div
69
+ class={resolvedFooterSubGroupClassName}
70
+ data-testid={footerSubGroupTestId}
71
+ data-direction={direction}
72
+ data-fill={shouldFillActions ? true : undefined}
73
+ >
74
+ {#each actionGroup.group as action (actionGroup.groupIndex + '-' + action)}
75
+ {@render renderAction?.(action, primaryActions.includes(action))}
76
+ {/each}
77
+ </div>
78
+ {/each}
79
+ </div>
@@ -0,0 +1,19 @@
1
+ import type { SurfacePresentation } from '@c15t/core';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ actionGroups?: string[][];
5
+ primaryActions?: string[];
6
+ shouldFillActions?: boolean;
7
+ direction?: SurfacePresentation['direction'];
8
+ noStyle?: boolean;
9
+ footerClassName?: string;
10
+ footerSubGroupClassName?: string;
11
+ footerTestId?: string;
12
+ footerSubGroupTestId?: string;
13
+ /** Rendered inside the footer before the action groups, e.g. rights links. */
14
+ leading?: Snippet;
15
+ renderAction?: Snippet<[string, boolean]>;
16
+ };
17
+ declare const PolicyActionsRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
18
+ type PolicyActionsRenderer = ReturnType<typeof PolicyActionsRenderer>;
19
+ export default PolicyActionsRenderer;