@c15t/react 3.1.0 → 3.2.0-canary.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 (120) hide show
  1. package/AGENTS.md +11 -4
  2. package/README.md +2 -2
  3. package/dist/component-hooks/use-consent-gate.js +2 -0
  4. package/dist/component-hooks/use-consent-labels.js +2 -0
  5. package/dist/component-hooks/use-consent-text-direction.js +2 -0
  6. package/dist/component-hooks/use-vendors-listed-under.js +2 -0
  7. package/dist/components/consent-gate/atoms.js +1 -1
  8. package/dist/components/consent-gate/consent-gate.js +1 -1
  9. package/dist/components/iab-prompt/atoms/root.js +1 -1
  10. package/dist/components/panel-trigger/atoms/toolbar.js +1 -1
  11. package/dist/components/preferences/atoms/button.js +1 -1
  12. package/dist/components/preferences/atoms/vendor-list.js +1 -1
  13. package/dist/components/prompt/atoms/root.js +1 -1
  14. package/dist/components/prompt/components.js +1 -1
  15. package/dist/components/prompt/policy-actions.js +1 -1
  16. package/dist/components/prompt/prompt.js +1 -1
  17. package/dist/components/prompt/surface-context.js +1 -1
  18. package/dist/components/prompt/use-banner-copy.js +1 -1
  19. package/dist/components/shared/streamed-surface.js +1 -1
  20. package/dist/headless.js +1 -1
  21. package/dist/index-parts/offline.js +1 -1
  22. package/dist/modes.js +1 -1
  23. package/dist/transports/offline.js +1 -1
  24. package/dist/version.js +1 -1
  25. package/dist-types/component-hooks/use-consent-gate.d.ts +102 -0
  26. package/dist-types/component-hooks/use-consent-labels.d.ts +32 -0
  27. package/dist-types/component-hooks/use-consent-text-direction.d.ts +22 -0
  28. package/dist-types/component-hooks/use-vendors-listed-under.d.ts +30 -0
  29. package/dist-types/components/prompt/atoms/root.d.ts +3 -3
  30. package/dist-types/components/prompt/prompt.d.ts +4 -3
  31. package/dist-types/components/prompt/surface-context.d.ts +1 -14
  32. package/dist-types/components/prompt/use-banner-copy.d.ts +8 -3
  33. package/dist-types/components/shared/streamed-surface.d.ts +47 -10
  34. package/dist-types/headless.d.ts +10 -1
  35. package/dist-types/hooks/use-iab-policy-guard.d.ts +17 -1
  36. package/dist-types/hooks/use-scroll-lock.d.ts +16 -2
  37. package/dist-types/index-parts/offline.d.ts +1 -1
  38. package/dist-types/modes.d.ts +1 -1
  39. package/dist-types/module-hooks/script-loader.d.ts +9 -0
  40. package/dist-types/provider.d.ts +3 -3
  41. package/dist-types/transports/offline.d.ts +2 -2
  42. package/dist-types/version.d.ts +1 -1
  43. package/docs/README.md +11 -4
  44. package/docs/concepts/choose-your-setup.md +9 -9
  45. package/docs/concepts/consent-state.md +81 -9
  46. package/docs/concepts/data-fetching.md +30 -23
  47. package/docs/concepts/modes.md +85 -35
  48. package/docs/customization/recipes.md +4 -4
  49. package/docs/customization/stylesheets.md +3 -3
  50. package/docs/customization/tailwind.md +17 -34
  51. package/docs/customization/translations.md +18 -12
  52. package/docs/frameworks/react/clear-on-revocation.md +3 -2
  53. package/docs/frameworks/react/components/consent-banner.md +3 -3
  54. package/docs/frameworks/react/components/consent-provider.md +29 -29
  55. package/docs/frameworks/react/gpp.md +1 -1
  56. package/docs/frameworks/react/headless.md +146 -19
  57. package/docs/frameworks/react/iab.md +8 -1
  58. package/docs/frameworks/react/quickstart.md +1 -1
  59. package/docs/frameworks/react/rendering.md +1 -1
  60. package/docs/frameworks/react/translations.md +9 -9
  61. package/docs/frameworks/react/troubleshooting.md +1 -1
  62. package/docs/frameworks/react/upgrade-v3.md +39 -32
  63. package/docs/frameworks/react/vendor-consent.md +5 -0
  64. package/docs/guides/banner-experiments.md +3 -3
  65. package/docs/guides/troubleshooting.md +3 -2
  66. package/docs/integrations/adobe-analytics.md +2 -2
  67. package/docs/integrations/ahrefs-analytics.md +2 -2
  68. package/docs/integrations/amplitude.md +2 -2
  69. package/docs/integrations/clay.md +305 -0
  70. package/docs/integrations/clearbit.md +2 -2
  71. package/docs/integrations/cloudflare-web-analytics.md +2 -2
  72. package/docs/integrations/cloudflare-zaraz.md +2 -2
  73. package/docs/integrations/crisp.md +2 -2
  74. package/docs/integrations/databuddy.md +2 -2
  75. package/docs/integrations/fathom-analytics.md +2 -2
  76. package/docs/integrations/front-chat.md +2 -2
  77. package/docs/integrations/google-maps.md +2 -2
  78. package/docs/integrations/google-tag-manager.md +59 -2
  79. package/docs/integrations/google-tag.md +2 -2
  80. package/docs/integrations/heap.md +2 -2
  81. package/docs/integrations/hightouch.md +2 -2
  82. package/docs/integrations/hotjar.md +40 -26
  83. package/docs/integrations/intercom.md +2 -2
  84. package/docs/integrations/klaviyo.md +2 -2
  85. package/docs/integrations/linkedin-insights.md +2 -2
  86. package/docs/integrations/logrocket.md +2 -2
  87. package/docs/integrations/matomo-analytics.md +2 -2
  88. package/docs/integrations/meta-pixel.md +2 -2
  89. package/docs/integrations/microsoft-clarity.md +2 -2
  90. package/docs/integrations/microsoft-uet.md +2 -2
  91. package/docs/integrations/mixpanel-analytics.md +2 -2
  92. package/docs/integrations/one-dollar-stats.md +2 -2
  93. package/docs/integrations/openai-pixel.md +2 -2
  94. package/docs/integrations/other-ads-and-pixels.md +94 -0
  95. package/docs/integrations/other-analytics.md +108 -0
  96. package/docs/integrations/other-chat-and-support.md +112 -0
  97. package/docs/integrations/other-email-and-sms.md +156 -0
  98. package/docs/integrations/other-experience.md +222 -0
  99. package/docs/integrations/overview.md +55 -21
  100. package/docs/integrations/pinterest-tag.md +2 -2
  101. package/docs/integrations/pirsch.md +2 -2
  102. package/docs/integrations/plausible-analytics.md +2 -2
  103. package/docs/integrations/posthog.md +2 -2
  104. package/docs/integrations/promptwatch.md +2 -2
  105. package/docs/integrations/reddit-pixel.md +2 -2
  106. package/docs/integrations/rudderstack.md +2 -2
  107. package/docs/integrations/rybbit-analytics.md +2 -2
  108. package/docs/integrations/segment.md +2 -2
  109. package/docs/integrations/sentry.md +2 -2
  110. package/docs/integrations/shopify.md +323 -0
  111. package/docs/integrations/snapchat-pixel.md +2 -2
  112. package/docs/integrations/tiktok-pixel.md +2 -2
  113. package/docs/integrations/umami-analytics.md +2 -2
  114. package/docs/integrations/vercel-analytics.md +2 -2
  115. package/docs/integrations/vercel-speed-insights.md +7 -2
  116. package/docs/integrations/x-pixel.md +2 -2
  117. package/docs/integrations/youtube.md +2 -2
  118. package/package.json +10 -32
  119. package/dist/components/prompt/resolve-banner-primary-actions.js +0 -1
  120. package/dist-types/components/prompt/resolve-banner-primary-actions.d.ts +0 -14
package/AGENTS.md CHANGED
@@ -39,7 +39,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
39
39
  - [Customize](./docs/frameworks/react/customize.md): Change the colors, fonts, layout, button styles and copy of the c15t banner and dialog in a React app with theme tokens, provider options and slots, and load the stylesheet yourself when you need to.
40
40
  - [Embeds](./docs/frameworks/react/embeds.md): Keep YouTube videos, maps and other iframes out of a React page until their consent category is allowed, with ConsentGate or the iframe blocker in ConsentProvider.
41
41
  - [IAB GPP](./docs/frameworks/react/gpp.md): Add the IAB Global Privacy Platform API (__gpp) to a React app with ConsentGPP from @c15t/react/gpp, so ad tech can read US state opt-outs and the TCF EU consent string.
42
- - [Headless](./docs/frameworks/react/headless.md): Build a custom consent banner in React with the c15t/react/headless hooks inside your ConsentProvider.
42
+ - [Headless](./docs/frameworks/react/headless.md): Build a custom consent banner or embed gate in React with the c15t/react/headless hooks inside your ConsentProvider.
43
43
  - [Hooks](./docs/frameworks/react/hooks.md): Gate features and save consent choices in React components with the focused hooks exported from c15t/react.
44
44
  - [IAB TCF](./docs/frameworks/react/iab.md): Mount the IAB TCF banner and dialog inside a React ConsentProvider and configure the CMP ID, policy and vendor data.
45
45
  - [Network blocker](./docs/frameworks/react/network-blocker.md): Hold fetch and XMLHttpRequest calls in a React app until their consent category is allowed, with networkBlocker rules in the ConsentProvider options.
@@ -56,9 +56,9 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
56
56
  - [Choose your setup](./docs/concepts/choose-your-setup.md): Pick the c15t setup for your framework, rendering mode and hosting, and decide who runs the consent backend.
57
57
  - [Consent categories](./docs/concepts/consent-categories.md): Assign scripts, embeds and features to c15t consent categories, and understand which categories the preference dialog shows.
58
58
  - [Consent state reference](./docs/concepts/consent-state.md): How c15t saves choices, gates IAB vendors, hydrates server records and keeps browser tabs and storage in step.
59
- - [Data fetching](./docs/concepts/data-fetching.md): Choose the recommended build-time policy snapshot or runtime manifest fetching, backend /init, browser resolution or offline rules, and understand where consent choices are saved.
59
+ - [Data fetching](./docs/concepts/data-fetching.md): Choose the recommended build-time policy snapshot or runtime manifest fetching, backend /init, browser resolution or local rules, and understand where consent choices are saved.
60
60
  - [How consent works](./docs/concepts/how-consent-works.md): What c15t decides on each page load, the difference between a permission and a recorded choice, and what happens when a visitor saves.
61
- - [Consent modes](./docs/concepts/modes.md): Reference for manifest(), hosted() and offline() in every c15t framework package, where each one resolves the visitor's policy, what it adds to first-load JavaScript, the build-time manifest fetch and its failure policy, and the backend URL variable each framework reads.
61
+ - [Consent modes](./docs/concepts/modes.md): Reference for manifest(), hosted() and local() in every c15t framework package, where each one resolves the visitor's policy, what it adds to first-load JavaScript, the build-time manifest fetch and its failure policy, and the backend URL variable each framework reads.
62
62
  - [Policies](./docs/concepts/policies.md): How policy models, prompts and scope decide what c15t asks visitors, where to change the rules, and why a banner may not appear.
63
63
 
64
64
  ## Guides
@@ -86,6 +86,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
86
86
  - [Ahrefs Analytics](./docs/integrations/ahrefs-analytics.md): Load Ahrefs Web Analytics only after measurement consent with the c15t ahrefsAnalytics helper, and check it in DevTools.
87
87
  - [Amplitude](./docs/integrations/amplitude.md): Load the Amplitude Browser SDK 2 only after measurement consent with the c15t amplitude helper, which opts the SDK out on revocation and back in on a new grant.
88
88
  - [Custom integrations](./docs/integrations/building-integrations.md): Gate a vendor that has no @c15t/integrations helper, or sync consent with an SDK your app already loads, using a c15t script configuration.
89
+ - [Clay](./docs/integrations/clay.md): Load Clay web intent only after marketing consent and understand its storage and revocation limits.
89
90
  - [Clearbit](./docs/integrations/clearbit.md): Load the Clearbit enrichment tag only after marketing consent with the c15t clearbit helper, and check it in DevTools.
90
91
  - [Cloudflare Web Analytics](./docs/integrations/cloudflare-web-analytics.md): Load the Cloudflare Web Analytics beacon only after measurement consent with the c15t cloudflareWebAnalytics helper, and check it in DevTools.
91
92
  - [Cloudflare Zaraz](./docs/integrations/cloudflare-zaraz.md): Sync c15t permissions to Cloudflare Zaraz purposes with the c15t cloudflareZaraz bridge, which runs on every page without loading a script, and check the tools it controls.
@@ -110,7 +111,12 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
110
111
  - [Mixpanel](./docs/integrations/mixpanel-analytics.md): Load the Mixpanel SDK on every page with the c15t mixpanelAnalytics helper, which switches Mixpanel tracking on and off with measurement consent, and check it in DevTools.
111
112
  - [OneDollarStats](./docs/integrations/one-dollar-stats.md): Load the OneDollarStats tracker only after measurement consent with the c15t oneDollarStats helper, forward its data-attribute settings, and check it in DevTools.
112
113
  - [OpenAI Pixel](./docs/integrations/openai-pixel.md): Load the ChatGPT Ads Measurement Pixel only after marketing consent with the c15t openaiPixel helper, guard oaiq conversion calls, and check it in DevTools.
113
- - [Integrations](./docs/integrations/overview.md): Find all c15t integrations for analytics, tag managers, advertising, email and SMS, chat and embedded content.
114
+ - [Other ads and pixels](./docs/integrations/other-ads-and-pixels.md): Consent-gated helpers for ad platforms and conversion pixels that don't need a full guide.
115
+ - [Other analytics tools](./docs/integrations/other-analytics.md): Consent-gated helpers for analytics tools that don't need a full guide.
116
+ - [Other chat and support tools](./docs/integrations/other-chat-and-support.md): Consent-gated helpers for chat and support tools that don't need a full guide.
117
+ - [Other email, SMS and marketing automation tools](./docs/integrations/other-email-and-sms.md): Consent-gated helpers for email, SMS and marketing automation tools that don't need a full guide.
118
+ - [Experimentation and personalization tools](./docs/integrations/other-experience.md): Consent-gated helpers for experimentation and personalization tools that don't need a full guide.
119
+ - [Integrations](./docs/integrations/overview.md): Find all c15t integrations for analytics, tag managers, advertising, experimentation and personalization, email and SMS, chat and embedded content.
114
120
  - [Pinterest Tag](./docs/integrations/pinterest-tag.md): Load the Pinterest Tag only after marketing consent with the c15t pinterestTag helper, guard pintrk event calls, and check it in DevTools.
115
121
  - [Pirsch](./docs/integrations/pirsch.md): Load Pirsch Analytics only after measurement consent with the c15t pirsch helper, keep custom event bindings working, and check it in DevTools.
116
122
  - [Plausible Analytics](./docs/integrations/plausible-analytics.md): Load the Plausible Analytics tracker only after measurement consent with the c15t plausibleAnalytics helper, and check it in DevTools.
@@ -121,6 +127,7 @@ These docs describe c15t v3. Find your app's row in Choose your setup, then foll
121
127
  - [Rybbit Analytics](./docs/integrations/rybbit-analytics.md): Load Rybbit Analytics only after measurement consent with the c15t rybbitAnalytics helper, map its tracking options to data attributes, and check it in DevTools.
122
128
  - [Segment](./docs/integrations/segment.md): Load Segment Analytics.js only after measurement consent with the c15t segment helper, guard your own track and identify calls, and check it in DevTools.
123
129
  - [Sentry](./docs/integrations/sentry.md): Configure Sentry's CDN or your own SDK with consent for error monitoring, Session Replay and user data.
130
+ - [Shopify](./docs/integrations/shopify.md): Send c15t consent to Shopify's Customer Privacy API from a Hydrogen storefront, so Shopify analytics wait for the visitor's choice and follow each later change.
124
131
  - [Snapchat Pixel](./docs/integrations/snapchat-pixel.md): Load the Snapchat Pixel only after marketing consent with the c15t snapchatPixel helper, guard snaptr event calls, and check it in DevTools.
125
132
  - [TikTok Pixel](./docs/integrations/tiktok-pixel.md): Load the TikTok Pixel only after marketing consent with the c15t tiktokPixel helper, guard ttq event calls, and check it in DevTools.
126
133
  - [Umami Analytics](./docs/integrations/umami-analytics.md): Load the Umami Analytics tracker only after measurement consent with the c15t umamiAnalytics helper, point it at a self-hosted instance, and check it in DevTools.
package/README.md CHANGED
@@ -29,13 +29,13 @@ c15t is used by [2,100+ sites](https://www.wappalyzer.com/technologies/cookie-co
29
29
  - Headless hooks and compound components for a fully custom design
30
30
  - Analytics, ads, and pixels follow each visitor's choice through @c15t/integrations: Google Tag Manager, Google Consent Mode v2, GA4, Meta Pixel, PostHog, and more
31
31
  - IAB TCF 2.4 banner, dialog, and hooks through the @c15t/react/iab subpath
32
- - Translations for 35 languages through @c15t/translations
32
+ - Translations for 53 languages through @c15t/translations
33
33
 
34
34
  ## Prerequisites
35
35
 
36
36
  - React 18 or 19
37
37
  - Node.js 20.19 or later
38
- - An [Inth project](https://inth.com) (free sign-up), a [self-hosted backend](https://c15t.com/docs/self-host/quickstart), or offline mode for local development
38
+ - An [Inth project](https://inth.com) (free sign-up), a [self-hosted backend](https://c15t.com/docs/self-host/quickstart), or local mode for development and tests
39
39
 
40
40
  ## Installation
41
41
 
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{useCallback as e,useEffect as o,useMemo as t}from"react";import{useRegisterConsentCategories as s,useSetActiveUI as n}from"../hooks.js";import{useIsHydrated as l}from"../hooks/use-is-hydrated.js";import{useCategoryAllowed as i,useKernelSelector as r}from"../kernel-selector.js";import{useTranslations as c}from"./use-translations.js";let a="This content is unavailable under your region's consent policy.",p=function(e,o){let{consentGate:t,consentTypes:s}=e,n=s?.[o]?.title??o;return{actionLabel:t?.actionButton?.replace("{category}",n),categoryTitle:n,policyBlockedMessage:t?.policyBlocked??a,title:t?.title?t.title.replace("{category}",n):void 0}},u=function(a){let u,d=i(a),y=l(),m=r(e=>"strict"===e.policyRule.scopeMode&&"necessary"!==a&&!e.policyRule.scope.includes("*")&&!e.policyRule.scope.includes(a)),g=s(),_=n(),C=c();o(()=>{g([a])},[a,g]);let f=t(()=>p(C,a),[C,a]),k=e(()=>{_("dialog")},[_]);return t(()=>({...f,requestConsent:k,state:u}),[f,k,u=d?y?"granted":"pending":m?"policy-blocked":"denied"])};export{a as DEFAULT_POLICY_BLOCKED_MESSAGE,p as resolveConsentGateCopy,u as useConsentGate};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{resolveActionLabels as e,resolvePreferenceControlLabels as t}from"@c15t/core";import{useMemo as o}from"react";import{useTranslations as s}from"./use-translations.js";let r=function(){let r=s();return o(()=>({actions:e(r),preferenceControls:t(r)}),[r])};export{r as useConsentLabels};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{getTextDirection as e}from"@c15t/ui/utils";import{useTranslationLanguage as t}from"../kernel-selector.js";let n=function(){return e(t())};export{n as useConsentTextDirection};
@@ -0,0 +1,2 @@
1
+ "use client";
2
+ import{deniedVendorIds as e,vendorsListedUnder as r}from"@c15t/core";import{useMemo as o}from"react";import{useDeclaredVendors as s,useVendorChoice as t}from"../hooks.js";let n=function(n){let d=s(),i=t();return o(()=>r(d,n,e({vendorChoice:i,vendors:{declared:d,listVersion:null}})),[d,n,i])};export{n as useVendorsListedUnder};
@@ -1 +1 @@
1
- import{Fragment as t,jsx as e,jsxs as o}from"react/jsx-runtime";import s from"@c15t/ui/styles/components/consent-gate";import{forwardRef as n}from"react";import{useTranslations as r}from"../../component-hooks/use-translations.js";import{useUIConfig as a}from"../../ui-config-context.js";import{getSlotProps as i,mergeSlotProps as l}from"../../utils/merge-slot-props.js";import{FIRST_PAINT_SHEETS as m}from"../shared/first-paint-sheets.js";import{Box as c}from"../shared/primitives/box.js";import{ConsentButton as p}from"../shared/primitives/button.js";import{SurfaceStyles as d,useSurfaceSheetsReady as f}from"../shared/surface-styles.js";let u=n(({children:n,...r},a)=>f(m,r.noStyle)?o(t,{children:[e(d,{sheets:m,noStyle:r.noStyle}),e(c,{ref:a,baseClassName:s.placeholder,"data-testid":"consent-gate-placeholder",slotKey:"consent-gate.root",...r,children:n})]}):null),y=n(({children:t,category:o,...n},a)=>{let{consentGate:i,consentTypes:l}=r(),m=o&&i?.title?i.title.replace("{category}",l?.[o]?.title??o):void 0;return e(c,{ref:a,baseClassName:s.title,"data-testid":"consent-gate-title",slotKey:"consent-gate.title",...n,children:t??m})}),g=n(({children:t,category:o,className:s,style:n,...m},c)=>{let{consentGate:d,consentTypes:f}=r(),{components:u}=a(),y=l(i(u,"consent-gate.button"),{className:s,style:n}),g=f?.[o]?.title??o,h=d?.actionButton?.replace("{category}",g);return e(p,{mode:"stroke",size:"small",variant:"primary",...y,...m,ref:c,action:"open-consent-dialog",category:o,"data-testid":"consent-gate-button",children:t??h})});u.displayName="ConsentGateRoot",y.displayName="ConsentGateTitle",g.displayName="ConsentGateButton";export{g as ConsentGateButton,u as ConsentGateRoot,y as ConsentGateTitle};
1
+ import{Fragment as t,jsx as e,jsxs as o}from"react/jsx-runtime";import s from"@c15t/ui/styles/components/consent-gate";import{forwardRef as n}from"react";import{resolveConsentGateCopy as r}from"../../component-hooks/use-consent-gate.js";import{useTranslations as a}from"../../component-hooks/use-translations.js";import{useUIConfig as i}from"../../ui-config-context.js";import{getSlotProps as l,mergeSlotProps as m}from"../../utils/merge-slot-props.js";import{FIRST_PAINT_SHEETS as c}from"../shared/first-paint-sheets.js";import{Box as p}from"../shared/primitives/box.js";import{ConsentButton as d}from"../shared/primitives/button.js";import{SurfaceStyles as f,useSurfaceSheetsReady as u}from"../shared/surface-styles.js";let h=n(({children:n,...r},a)=>u(c,r.noStyle)?o(t,{children:[e(f,{sheets:c,noStyle:r.noStyle}),e(p,{ref:a,baseClassName:s.placeholder,"data-testid":"consent-gate-placeholder",slotKey:"consent-gate.root",...r,children:n})]}):null),g=n(({children:t,category:o,...n},i)=>{let l=a(),m=o?r(l,o).title:void 0;return e(p,{ref:i,baseClassName:s.title,"data-testid":"consent-gate-title",slotKey:"consent-gate.title",...n,children:t??m})}),y=n(({children:t,category:o,className:s,style:n,...c},p)=>{let f=a(),{components:u}=i(),h=m(l(u,"consent-gate.button"),{className:s,style:n}),g=r(f,o).actionLabel;return e(d,{mode:"stroke",size:"small",variant:"primary",...h,...c,ref:p,action:"open-consent-dialog",category:o,"data-testid":"consent-gate-button",children:t??g})});h.displayName="ConsentGateRoot",g.displayName="ConsentGateTitle",y.displayName="ConsentGateButton";export{y as ConsentGateButton,h as ConsentGateRoot,g as ConsentGateTitle};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{jsx as e,jsxs as o}from"react/jsx-runtime";import{forwardRef as r,useEffect as t}from"react";import{useTranslations as s}from"../../component-hooks/use-translations.js";import{usePolicyCategories as i,usePolicyScopeMode as l,useRegisterConsentCategories as n}from"../../hooks.js";import{useIsHydrated as c}from"../../hooks/use-is-hydrated.js";import{useCategoryAllowed as a}from"../../kernel-selector.js";import{ConsentGateButton as m,ConsentGateRoot as d,ConsentGateTitle as p}from"./atoms.js";let y=({category:r,policyBlocked:t,policyBlockedMessage:s})=>o(d,{children:[e(p,{category:r,children:t?s??"This content is unavailable under your region's consent policy.":void 0}),t?null:e(m,{category:r})]}),u=r(({children:o,category:r,placeholder:m,noStyle:d,className:p,theme:u,...h},f)=>{let k=a(r),j=c(),g=i(),v=l(),B=n(),{consentGate:x}=s(),C="necessary"!==r&&!g.includes("*")&&!g.includes(r);return t(()=>{B([r])},[r,B]),e("div",{ref:f,className:p,...h,children:k?j?o:null:m||e(y,{category:r,policyBlocked:"strict"===v&&C,policyBlockedMessage:x?.policyBlocked})})});u.displayName="ConsentGate";let h=u;export{h as ConsentGate};
2
+ import{jsx as e,jsxs as o}from"react/jsx-runtime";import{forwardRef as t}from"react";import{useConsentGate as r}from"../../component-hooks/use-consent-gate.js";import{ConsentGateButton as l,ConsentGateRoot as n,ConsentGateTitle as c}from"./atoms.js";let a=({category:t,policyBlocked:r,policyBlockedMessage:a})=>o(n,{children:[e(c,{category:t,children:r?a:void 0}),r?null:e(l,{category:t})]}),i=t(({children:o,category:t,placeholder:l,noStyle:n,className:c,theme:i,...m},s)=>{let{state:d,policyBlockedMessage:p}=r(t);return e("div",{ref:s,className:c,...m,children:"granted"===d?o:"pending"===d?null:l||e(a,{category:t,policyBlocked:"policy-blocked"===d,policyBlockedMessage:p})})});i.displayName="ConsentGate";let m=i;export{m as ConsentGate};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{Fragment as e,jsx as t,jsxs as o}from"react/jsx-runtime";import r from"@c15t/ui/styles/components/iab-consent-banner";import{forwardRef as n,useMemo as s}from"react";import{ConsentTrackingContext as a}from"../../../context/consent-tracking-context.js";import{LocalThemeContext as i}from"../../../context/theme-context.js";import{useActiveUI as m,useModel as l,useTranslations as c}from"../../../hooks.js";import{useIABConsentManager as d}from"../../../hooks/use-iab-manager.js";import{useLateEntry as p}from"../../../hooks/use-late-entry.js";import{useTextDirection as b}from"../../../hooks/use-text-direction.js";import{useUIConfig as f}from"../../../ui-config-context.js";import{mergeSlotProps as u}from"../../../utils/merge-slot-props.js";import{IAB_FIRST_PAINT_SHEETS as h}from"../../shared/iab-first-paint-sheets.js";import{SurfaceStyles as j,useSurfaceSheetsReady as g}from"../../shared/surface-styles.js";import{IABConsentBannerOverlay as x}from"./overlay.js";let y=["iab"],N=n(({children:n,className:s,style:a,className:i,disableAnimation:d,noStyle:N,models:k=y,...v},B)=>{let C=m(),{components:$}=f(),A=l(),I=c(),S=b(I?.language??"en"),P=g(h,N)&&null!==A&&"banner"===C&&k.includes(A),R=p(P)&&!d,E=u($?.["iab-banner"]?.root,{baseClassName:[r.root],className:s||i,noStyle:N,style:a,...v}),F=N?E.className||"":`${E.className||""} ${r.bannerVisible}${d?"":` ${r.bannerEntering}`}`;return P?o(e,{children:[t(j,{sheets:h,noStyle:N}),t(x,{}),o("div",{ref:B,...E,className:F,"data-position":"ltr"===S?"bottom-left":"bottom-right","data-entry":R?"late":void 0,"data-testid":"iab-consent-banner-root","data-c15t-streamed":"",dir:S,tabIndex:-1,children:[n,t("span",{"data-c15t-stream-end":"",hidden:!0})]})]}):null}),k=({children:e,className:o,noStyle:r,disableAnimation:n,scrollLock:m,trapFocus:l,models:c,uiSource:p,...b})=>{let{policyBanner:f}=d({prompt:{scrollLock:m,trapFocus:l}}),u=f.blocking,h=s(()=>({disableAnimation:n,noStyle:r,scrollLock:u,trapFocus:u}),[n,r,u]),j=s(()=>({uiSource:p??"iab_banner"}),[p]);return t(a.Provider,{value:j,children:t(i.Provider,{value:h,children:t(N,{disableAnimation:n,className:o,noStyle:r,models:c,...b,children:e})})})};N.displayName="IABConsentBannerRootChildren";export{k as IABConsentBannerRoot};
2
+ import{Fragment as e,jsx as t,jsxs as o}from"react/jsx-runtime";import r from"@c15t/ui/styles/components/iab-consent-banner";import{forwardRef as n,useMemo as s}from"react";import{ConsentTrackingContext as a}from"../../../context/consent-tracking-context.js";import{LocalThemeContext as i}from"../../../context/theme-context.js";import{useActiveUI as m,useModel as l,useTranslations as c}from"../../../hooks.js";import{useIABConsentManager as d}from"../../../hooks/use-iab-manager.js";import{useLateEntry as p}from"../../../hooks/use-late-entry.js";import{useTextDirection as b}from"../../../hooks/use-text-direction.js";import{useUIConfig as f}from"../../../ui-config-context.js";import{mergeSlotProps as u}from"../../../utils/merge-slot-props.js";import{IAB_FIRST_PAINT_SHEETS as h}from"../../shared/iab-first-paint-sheets.js";import{SurfaceStyles as j,useSurfaceSheetsReady as x}from"../../shared/surface-styles.js";import{IABConsentBannerOverlay as y}from"./overlay.js";let g=["iab"],N=n(({children:n,className:s,style:a,className:i,disableAnimation:d,noStyle:N,models:k=g,...v},B)=>{let C=m(),{components:$}=f(),A=l(),I=c(),S=b(I?.language??"en"),P=x(h,N)&&null!==A&&"banner"===C&&k.includes(A),R=p(P)&&!d,E=u($?.["iab-banner"]?.root,{baseClassName:[r.root],className:s||i,noStyle:N,style:a,...v}),F=N?E.className||"":`${E.className||""} ${r.bannerVisible}${d?"":` ${r.bannerEntering}`}`;return P?o(e,{children:[t(j,{sheets:h,noStyle:N}),t(y,{}),o("div",{ref:B,...E,className:F,"data-position":"bottom-left","data-entry":R?"late":void 0,"data-testid":"iab-consent-banner-root","data-c15t-streamed":"",dir:S,tabIndex:-1,children:[n,t("span",{"data-c15t-stream-end":"",hidden:!0})]})]}):null}),k=({children:e,className:o,noStyle:r,disableAnimation:n,scrollLock:m,trapFocus:l,models:c,uiSource:p,...b})=>{let{policyBanner:f}=d({prompt:{scrollLock:m,trapFocus:l}}),u=f.blocking,h=s(()=>({disableAnimation:n,noStyle:r,scrollLock:u,trapFocus:u}),[n,r,u]),j=s(()=>({uiSource:p??"iab_banner"}),[p]);return t(a.Provider,{value:j,children:t(i.Provider,{value:h,children:t(N,{disableAnimation:n,className:o,noStyle:r,models:c,...b,children:e})})})};N.displayName="IABConsentBannerRootChildren";export{k as IABConsentBannerRoot};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{jsx as e}from"react/jsx-runtime";import{createElement as t,useLayoutEffect as o,useMemo as r,useRef as i,useState as n}from"react";import s from"@c15t/ui/styles/components/consent-dialog-trigger";import{followDevToolsDock as l}from"@c15t/ui/utils/devtools-launcher";import{useTranslations as a}from"../../../component-hooks/use-translations.js";import{useDevToolsLauncher as d}from"../../../devtools-launcher.js";import{usePolicyRule as c}from"../../../hooks.js";import{useTheme as f}from"../../../hooks/use-theme.js";import{useUIConfig as m}from"../../../ui-config-context.js";import{mergeSlotProps as g}from"../../../utils/merge-slot-props.js";import{TriggerIcon as u}from"./icon.js";import{useTriggerContext as p}from"./root.js";import{DevToolsIcon as b}from"./trigger-icons.js";let h={"bottom-left":s.bottomLeft,"bottom-right":s.bottomRight,"top-left":s.topLeft,"top-right":s.topRight},k={lg:s.lg,md:s.md,sm:s.sm},y={focusId:"devtools",icon:e(b,{}),id:"devtools",kind:"devtools",label:"c15t DevTools"},v=function(){let e=c(),{rights:t}=a();return r(()=>{let o=e.rights.includes("opt-out")?"opt-out":"preferences";return{label:"opt-out"===o&&e.i18n?.messageProfile!=="preferences"?t?.optOut??"Do not sell or share my data":t?.preferences??"Manage preferences",right:o,rights:e.rights.join(" ")}},[e.rights,e.i18n?.messageProfile,t])},I=function(e,t,o,r,i,n){let s=e.map(e=>({...e,focusId:`custom:${e.id}`,kind:"custom"}));return(n&&s.push(y),i)?[...s,{className:t.className,focusId:"preferences",icon:t.icon??"branding",id:"preferences",kind:"preferences",label:t.label??r,onSelect:t.onSelect,right:o,style:t.style}]:s},j=function(e,t,o){let r=e.find(e=>"preferences"===e.kind),i=e.find(e=>"devtools"===e.kind);if(!r&&!i)return e;let n=e.filter(e=>"custom"===e.kind),s="horizontal"===t?o.endsWith("left"):o.startsWith("top"),l=r?[r]:[],a=i?[i]:[];return s?[...l,...n,...a]:[...a,...n,...l]},N=({actions:a,preferences:c,size:b="md",orientation:y="horizontal",ariaLabel:N="Privacy controls",showPreferences:S=!0,className:w,style:x,noStyle:D=!1})=>{let{components:C}=m(),{disableAnimation:T,noStyle:A}=f(),{label:L,right:P,rights:R}=v(),{corner:z,isDragging:E,isSnapping:H,wasDragged:M,handlers:O,dragStyle:W,openDialog:B}=p(),F=d(),K=null!==F,U=F?.dock,V=i(null),$=r(()=>j(I(a,c,P,L,S,K),y,z),[a,z,L,y,c,P,K,S]);o(()=>{let e=V.current;if(U&&e&&!E)return l(e,z,U)},[z,U,E]);let q=$.find(e=>!e.disabled)?.focusId,[G,J]=n(q),Q=i(new Map),X=D||A,Y=g(C?.trigger?.toolbar,{baseClassName:[s.toolbar,"vertical"===y&&s.toolbarVertical,h[z],E&&s.dragging,H&&s.snapping],className:w,noStyle:X,style:x}),Z=g(C?.trigger?.toolbarItem,{baseClassName:[s.toolbarItem,k[b]],noStyle:X}),_=g(C?.trigger?.toolbarIcon,{baseClassName:s.toolbarIcon,noStyle:X}),ee=$.some(e=>e.focusId===G&&!e.disabled)?G:q;return e("div",{...Y,"aria-label":N,"aria-orientation":y,"data-corner":z,"data-c15t-trigger-toolbar":"true","data-c15t-trigger":"true","data-disable-animation":T?"":void 0,"data-dragging":E||void 0,"data-snapping":H||void 0,dir:"ltr",onKeyDown:e=>{let t,o="vertical"===y?"ArrowDown":"ArrowRight",r="vertical"===y?"ArrowUp":"ArrowLeft";if(e.key!==o&&e.key!==r&&"Home"!==e.key&&"End"!==e.key)return;let i=$.filter(e=>!e.disabled);if(0===i.length)return;e.preventDefault();let n=i.findIndex(e=>e.focusId===ee);t="Home"===e.key?0:"End"===e.key?i.length-1:e.key===o?(n+1)%i.length:(n-1+i.length)%i.length;let s=i[t];s&&(J(s.focusId),Q.current.get(s.focusId)?.focus())},ref:V,role:"toolbar",style:{...Y.style,...W},tabIndex:-1,...O,children:$.map(o=>t("button",{...Z,key:o.focusId,ref:e=>{e?Q.current.set(o.focusId,e):Q.current.delete(o.focusId)},"aria-label":o.label,"aria-expanded":"devtools"===o.kind?F?.isOpen:void 0,"aria-pressed":"custom"===o.kind?o.pressed:void 0,className:[Z.className,o.className].filter(Boolean).join(" "),"data-c15t-rights":"preferences"===o.kind?R:void 0,"data-c15t-trigger-action":o.kind,"data-c15t-trigger-item":o.id,"data-right":"preferences"===o.kind?o.right:void 0,disabled:o.disabled,onClick:e=>((e,t)=>{if(!(0!==e.detail&&M())){if("devtools"===t.kind)return void F?.toggle();t.onSelect?.(),"preferences"===t.kind&&B()}})(e,o),onFocus:()=>J(o.focusId),style:{...Z.style,...o.style},tabIndex:o.focusId===ee?0:-1,type:"button"},e("span",{..._,"aria-hidden":"true",children:e(u,{icon:o.icon,noStyle:X})})))})};N.displayName="ConsentDialogTriggerToolbar";export{N as TriggerToolbar};
2
+ import{jsx as e}from"react/jsx-runtime";import{createElement as t,useLayoutEffect as o,useMemo as r,useRef as i,useState as n}from"react";import l from"@c15t/ui/styles/components/consent-dialog-trigger";import{followDevToolsDock as s}from"@c15t/ui/utils/devtools-launcher";import{useConsentLabels as a}from"../../../component-hooks/use-consent-labels.js";import{useDevToolsLauncher as d}from"../../../devtools-launcher.js";import{usePolicyRule as c}from"../../../hooks.js";import{useTheme as f}from"../../../hooks/use-theme.js";import{useUIConfig as m}from"../../../ui-config-context.js";import{mergeSlotProps as g}from"../../../utils/merge-slot-props.js";import{TriggerIcon as u}from"./icon.js";import{useTriggerContext as p}from"./root.js";import{DevToolsIcon as b}from"./trigger-icons.js";let h={"bottom-left":l.bottomLeft,"bottom-right":l.bottomRight,"top-left":l.topLeft,"top-right":l.topRight},k={lg:l.lg,md:l.md,sm:l.sm},y={focusId:"devtools",icon:e(b,{}),id:"devtools",kind:"devtools",label:"c15t DevTools"},v=function(){let e=c(),{preferenceControls:t}=a();return r(()=>{let o=e.rights.includes("opt-out")?"opt-out":"preferences";return{label:"opt-out"===o&&e.i18n?.messageProfile!=="preferences"?t["opt-out"]:t.preferences,right:o,rights:e.rights.join(" ")}},[e.rights,e.i18n?.messageProfile,t])},I=function(e,t,o,r,i,n){let l=e.map(e=>({...e,focusId:`custom:${e.id}`,kind:"custom"}));return(n&&l.push(y),i)?[...l,{className:t.className,focusId:"preferences",icon:t.icon??"branding",id:"preferences",kind:"preferences",label:t.label??r,onSelect:t.onSelect,right:o,style:t.style}]:l},j=function(e,t,o){let r=e.find(e=>"preferences"===e.kind),i=e.find(e=>"devtools"===e.kind);if(!r&&!i)return e;let n=e.filter(e=>"custom"===e.kind),l="horizontal"===t?o.endsWith("left"):o.startsWith("top"),s=r?[r]:[],a=i?[i]:[];return l?[...s,...n,...a]:[...a,...n,...s]},N=({actions:a,preferences:c,size:b="md",orientation:y="horizontal",ariaLabel:N="Privacy controls",showPreferences:S=!0,className:w,style:x,noStyle:C=!1})=>{let{components:D}=m(),{disableAnimation:T,noStyle:A}=f(),{label:L,right:P,rights:R}=v(),{corner:z,isDragging:E,isSnapping:H,wasDragged:W,handlers:B,dragStyle:F,openDialog:K}=p(),M=d(),O=null!==M,U=M?.dock,V=i(null),$=r(()=>j(I(a,c,P,L,S,O),y,z),[a,z,L,y,c,P,O,S]);o(()=>{let e=V.current;if(U&&e&&!E)return s(e,z,U)},[z,U,E]);let q=$.find(e=>!e.disabled)?.focusId,[G,J]=n(q),Q=i(new Map),X=C||A,Y=g(D?.trigger?.toolbar,{baseClassName:[l.toolbar,"vertical"===y&&l.toolbarVertical,h[z],E&&l.dragging,H&&l.snapping],className:w,noStyle:X,style:x}),Z=g(D?.trigger?.toolbarItem,{baseClassName:[l.toolbarItem,k[b]],noStyle:X}),_=g(D?.trigger?.toolbarIcon,{baseClassName:l.toolbarIcon,noStyle:X}),ee=$.some(e=>e.focusId===G&&!e.disabled)?G:q;return e("div",{...Y,"aria-label":N,"aria-orientation":y,"data-corner":z,"data-c15t-trigger-toolbar":"true","data-c15t-trigger":"true","data-disable-animation":T?"":void 0,"data-dragging":E||void 0,"data-snapping":H||void 0,dir:"ltr",onKeyDown:e=>{let t,o="vertical"===y?"ArrowDown":"ArrowRight",r="vertical"===y?"ArrowUp":"ArrowLeft";if(e.key!==o&&e.key!==r&&"Home"!==e.key&&"End"!==e.key)return;let i=$.filter(e=>!e.disabled);if(0===i.length)return;e.preventDefault();let n=i.findIndex(e=>e.focusId===ee);t="Home"===e.key?0:"End"===e.key?i.length-1:e.key===o?(n+1)%i.length:(n-1+i.length)%i.length;let l=i[t];l&&(J(l.focusId),Q.current.get(l.focusId)?.focus())},ref:V,role:"toolbar",style:{...Y.style,...F},tabIndex:-1,...B,children:$.map(o=>t("button",{...Z,key:o.focusId,ref:e=>{e?Q.current.set(o.focusId,e):Q.current.delete(o.focusId)},"aria-label":o.label,"aria-expanded":"devtools"===o.kind?M?.isOpen:void 0,"aria-pressed":"custom"===o.kind?o.pressed:void 0,className:[Z.className,o.className].filter(Boolean).join(" "),"data-c15t-rights":"preferences"===o.kind?R:void 0,"data-c15t-trigger-action":o.kind,"data-c15t-trigger-item":o.id,"data-right":"preferences"===o.kind?o.right:void 0,disabled:o.disabled,onClick:e=>((e,t)=>{if(!(0!==e.detail&&W())){if("devtools"===t.kind)return void M?.toggle();t.onSelect?.(),"preferences"===t.kind&&K()}})(e,o),onFocus:()=>J(o.focusId),style:{...Z.style,...o.style},tabIndex:o.focusId===ee?0:-1,type:"button"},e("span",{..._,"aria-hidden":"true",children:e(u,{icon:o.icon,noStyle:X})})))})};N.displayName="ConsentDialogTriggerToolbar";export{N as TriggerToolbar};
@@ -1 +1 @@
1
- import{jsx as t}from"react/jsx-runtime";import{forwardRef as e}from"react";import{useTranslations as n}from"../../../component-hooks/use-translations.js";import{ConsentButton as o}from"../../shared/primitives/button.js";let s=e(({children:e,...s},c)=>{let{common:i}=n();return t(o,{ref:c,size:"small",action:"accept-consent",consentAction:"accept",...s,slotKey:"button.secondary","data-testid":"consent-widget-footer-accept-all-button",closeConsentBanner:!0,closeConsentDialog:!0,children:e??i.acceptAll})});s.displayName="ConsentWidgetAcceptAllButton";let c=e(({children:e,...s},c)=>{let{common:i}=n();return t(o,{ref:c,action:"open-consent-dialog",consentAction:"customize",...s,slotKey:"button.secondary","data-testid":"consent-widget-footer-customize-button",children:e??i.customize})});c.displayName="ConsentWidgetCustomizeButton";let i=e(({children:e,...s},c)=>{let{common:i}=n();return t(o,{ref:c,action:"custom-consent",consentAction:"save",closeConsentDialog:!0,...s,slotKey:"button.primary","data-testid":"consent-widget-footer-save-button",children:e??i.save})});i.displayName="ConsentWidgetSaveButton";let a=e(({children:e,...s},c)=>{let{common:i}=n();return t(o,{ref:c,size:"small",action:"reject-consent",consentAction:"reject",...s,slotKey:"button.secondary","data-testid":"consent-widget-reject-button",closeConsentBanner:!0,closeConsentDialog:!0,children:e??i.rejectAll})});a.displayName="ConsentWidgetRejectButton";let l=s,r=c,u=i,d=a;export{l as AcceptAllButton,s as ConsentWidgetAcceptAllButton,c as ConsentWidgetCustomizeButton,a as ConsentWidgetRejectButton,i as ConsentWidgetSaveButton,r as CustomizeButton,d as RejectButton,u as SaveButton};
1
+ import{jsx as t}from"react/jsx-runtime";import{forwardRef as e}from"react";import{useConsentLabels as n}from"../../../component-hooks/use-consent-labels.js";import{ConsentButton as o}from"../../shared/primitives/button.js";let s=e(({children:e,...s},c)=>{let i=n();return t(o,{ref:c,size:"small",action:"accept-consent",consentAction:"accept",...s,slotKey:"button.secondary","data-testid":"consent-widget-footer-accept-all-button",closeConsentBanner:!0,closeConsentDialog:!0,children:e??i.actions.accept})});s.displayName="ConsentWidgetAcceptAllButton";let c=e(({children:e,...s},c)=>{let i=n();return t(o,{ref:c,action:"open-consent-dialog",consentAction:"customize",...s,slotKey:"button.secondary","data-testid":"consent-widget-footer-customize-button",children:e??i.actions.customize})});c.displayName="ConsentWidgetCustomizeButton";let i=e(({children:e,...s},c)=>{let i=n();return t(o,{ref:c,action:"custom-consent",consentAction:"save",closeConsentDialog:!0,...s,slotKey:"button.primary","data-testid":"consent-widget-footer-save-button",children:e??i.actions.save})});i.displayName="ConsentWidgetSaveButton";let a=e(({children:e,...s},c)=>{let i=n();return t(o,{ref:c,size:"small",action:"reject-consent",consentAction:"reject",...s,slotKey:"button.secondary","data-testid":"consent-widget-reject-button",closeConsentBanner:!0,closeConsentDialog:!0,children:e??i.actions.reject})});a.displayName="ConsentWidgetRejectButton";let l=s,r=c,u=i,d=a;export{l as AcceptAllButton,s as ConsentWidgetAcceptAllButton,c as ConsentWidgetCustomizeButton,a as ConsentWidgetRejectButton,i as ConsentWidgetSaveButton,r as CustomizeButton,d as RejectButton,u as SaveButton};
@@ -1 +1 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import{deniedVendorIds as o,vendorsListedUnder as n}from"@c15t/core";import r from"@c15t/ui/styles/components/vendor-list";import{useId as i,useMemo as s,useState as l}from"react";import{useTranslations as d}from"../../../component-hooks/use-translations.js";import{Box as a}from"../../shared/primitives/box.js";import{useConsentDraftSlice as c,useConsentDraftStore as h}from"../../../draft.js";import{useDeclaredVendors as m,useVendorChoice as y}from"../../../hooks.js";import{useTheme as p}from"../../../hooks/use-theme.js";import*as v from"../../shared/ui/preference-item/index.js";import*as g from"../../shared/ui/switch/index.js";let u=({open:o})=>t("svg",{"aria-hidden":"true",fill:"none",focusable:"false",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",viewBox:"0 0 24 24",children:[e("title",{children:o?"Close":"Open"}),o?e("path",{d:"M5 12h14"}):e("path",{d:"M5 12h14M12 5v14"})]}),C={disabledByCategory:"Turn on this category to choose vendors.",privacyPolicy:"Privacy policy",switchLabel:"Allow {vendor}",title:"Vendors ({count})"},b=({category:o,categoryOn:n,checked:r,copy:i,labelId:s,noStyle:d,onCheckedChange:c,styles:h,vendor:m})=>{let[y,p]=l(!1),C=m.name??m.id,b=!0!==m.disabled,f=!!(m.description||m.privacyPolicyUrl);return t(v.Root,{className:h?.item,"data-testid":`consent-widget-vendor-item-${o}-${m.id}`,disabled:!f,noStyle:d,onOpenChange:p,open:y,slotKey:"vendor-list.item",children:[t(a,{noStyle:d,baseClassName:h?.header,slotKey:"vendor-list.header",children:[t(v.Trigger,{className:h?.trigger,"data-testid":`consent-widget-vendor-trigger-${o}-${m.id}`,noStyle:d,slotKey:"vendor-list.trigger",children:[e(v.Leading,{className:h?.arrow,noStyle:d,children:e(u,{open:y})}),e(a,{asChild:!0,noStyle:d,baseClassName:h?.name,slotKey:"vendor-list.name",children:e("span",{"data-testid":`consent-widget-vendor-name-${o}-${m.id}`,id:s,children:C})})]}),b?e(a,{noStyle:d,baseClassName:h?.control,slotKey:"vendor-list.control",children:e(g.Root,{"aria-label":i.switchLabel.replace("{vendor}",C),"aria-describedby":s,checked:r,"data-testid":`consent-widget-vendor-switch-${o}-${m.id}`,disabled:!n,onCheckedChange:c,size:"small"})}):null]}),f?t(v.Content,{className:h?.content,"data-testid":`consent-widget-vendor-content-${o}-${m.id}`,innerClassName:h?.contentInner,noStyle:d,slotKey:"vendor-list.content",children:[m.description?e(a,{asChild:!0,noStyle:d,baseClassName:h?.description,slotKey:"vendor-list.description",children:e("p",{children:m.description})}):null,m.privacyPolicyUrl?e(a,{asChild:!0,noStyle:d,baseClassName:h?.link,slotKey:"vendor-list.link",children:e("a",{href:m.privacyPolicyUrl,rel:"noopener noreferrer",target:"_blank",children:i.privacyPolicy})}):null]}):null]})},f=({category:l,className:v,noStyle:g,...u})=>{let f=m(),$=y(),k=s(()=>n(f,l,o({vendorChoice:$,vendors:{declared:f,listVersion:null}})),[f,l,$]),w=h(),N=c(w,e=>!0===e.values[l]),S=c(w,e=>e.vendors),{consentManagerDialog:j}=d(),{noStyle:K}=p(),x=i(),L=g??K??!1;if(0===k.length)return null;let P={...C,...j.vendors},V=L?void 0:r,B=P.title.replace("{count}",String(k.length));return e(a,{asChild:!0,className:v,"data-testid":`consent-widget-vendor-list-${l}`,noStyle:L,baseClassName:V?.root,slotKey:"vendor-list.root",...u,children:t("section",{"aria-label":B,children:[N?null:e("p",{className:V?.hint,"data-testid":`consent-widget-vendor-hint-${l}`,children:P.disabledByCategory}),k.map(t=>e(b,{category:l,categoryOn:N,checked:S[t.id]??!0,copy:P,labelId:`${x}vendor-${l}-${t.id}`,noStyle:L,onCheckedChange:e=>w.setVendor(t.id,e),styles:V,vendor:t},t.id))]})})};export{f as ConsentWidgetVendorList};
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import n from"@c15t/ui/styles/components/vendor-list";import{useId as o,useState as r}from"react";import{useTranslations as i}from"../../../component-hooks/use-translations.js";import{useVendorsListedUnder as s}from"../../../component-hooks/use-vendors-listed-under.js";import{Box as l}from"../../shared/primitives/box.js";import{useConsentDraftSlice as d,useConsentDraftStore as a}from"../../../draft.js";import{useTheme as c}from"../../../hooks/use-theme.js";import*as h from"../../shared/ui/preference-item/index.js";import*as m from"../../shared/ui/switch/index.js";let y=({open:n})=>t("svg",{"aria-hidden":"true",fill:"none",focusable:"false",stroke:"currentColor",strokeLinecap:"round",strokeLinejoin:"round",strokeWidth:"2",viewBox:"0 0 24 24",children:[e("title",{children:n?"Close":"Open"}),n?e("path",{d:"M5 12h14"}):e("path",{d:"M5 12h14M12 5v14"})]}),p={disabledByCategory:"Turn on this category to choose vendors.",privacyPolicy:"Privacy policy",switchLabel:"Allow {vendor}",title:"Vendors ({count})"},v=({category:n,categoryOn:o,checked:i,copy:s,labelId:d,noStyle:a,onCheckedChange:c,styles:p,vendor:v})=>{let[g,u]=r(!1),C=v.name??v.id,b=!0!==v.disabled,f=!!(v.description||v.privacyPolicyUrl);return t(h.Root,{className:p?.item,"data-testid":`consent-widget-vendor-item-${n}-${v.id}`,disabled:!f,noStyle:a,onOpenChange:u,open:g,slotKey:"vendor-list.item",children:[t(l,{noStyle:a,baseClassName:p?.header,slotKey:"vendor-list.header",children:[t(h.Trigger,{className:p?.trigger,"data-testid":`consent-widget-vendor-trigger-${n}-${v.id}`,noStyle:a,slotKey:"vendor-list.trigger",children:[e(h.Leading,{className:p?.arrow,noStyle:a,children:e(y,{open:g})}),e(l,{asChild:!0,noStyle:a,baseClassName:p?.name,slotKey:"vendor-list.name",children:e("span",{"data-testid":`consent-widget-vendor-name-${n}-${v.id}`,id:d,children:C})})]}),b?e(l,{noStyle:a,baseClassName:p?.control,slotKey:"vendor-list.control",children:e(m.Root,{"aria-label":s.switchLabel.replace("{vendor}",C),"aria-describedby":d,checked:i,"data-testid":`consent-widget-vendor-switch-${n}-${v.id}`,disabled:!o,onCheckedChange:c,size:"small"})}):null]}),f?t(h.Content,{className:p?.content,"data-testid":`consent-widget-vendor-content-${n}-${v.id}`,innerClassName:p?.contentInner,noStyle:a,slotKey:"vendor-list.content",children:[v.description?e(l,{asChild:!0,noStyle:a,baseClassName:p?.description,slotKey:"vendor-list.description",children:e("p",{children:v.description})}):null,v.privacyPolicyUrl?e(l,{asChild:!0,noStyle:a,baseClassName:p?.link,slotKey:"vendor-list.link",children:e("a",{href:v.privacyPolicyUrl,rel:"noopener noreferrer",target:"_blank",children:s.privacyPolicy})}):null]}):null]})},g=({category:r,className:h,noStyle:m,...y})=>{let g=s(r),u=a(),C=d(u,e=>!0===e.values[r]),b=d(u,e=>e.vendors),{consentManagerDialog:f}=i(),{noStyle:$}=c(),k=o(),w=m??$??!1;if(0===g.length)return null;let N={...p,...f.vendors},S=w?void 0:n,j=N.title.replace("{count}",String(g.length));return e(l,{asChild:!0,className:h,"data-testid":`consent-widget-vendor-list-${r}`,noStyle:w,baseClassName:S?.root,slotKey:"vendor-list.root",...y,children:t("section",{"aria-label":j,children:[C?null:e("p",{className:S?.hint,"data-testid":`consent-widget-vendor-hint-${r}`,children:N.disabledByCategory}),g.map(t=>e(v,{category:r,categoryOn:C,checked:b[t.id]??!0,copy:N,labelId:`${k}vendor-${r}-${t.id}`,noStyle:w,onCheckedChange:e=>u.setVendor(t.id,e),styles:S,vendor:t},t.id))]})})};export{g as ConsentWidgetVendorList};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{Fragment as o,jsx as t,jsxs as r}from"react/jsx-runtime";import e from"@c15t/ui/styles/components/consent-banner";import{forwardRef as i,useMemo as s}from"react";import{useHeadlessConsentUI as n}from"../../../component-hooks/use-headless-consent-ui.js";import{ConsentTrackingContext as a}from"../../../context/consent-tracking-context.js";import{LocalThemeContext as l}from"../../../context/theme-context.js";import{useActiveUI as m,useHasConsentUI as c,useModel as p,usePolicyRule as d,useTranslations as u}from"../../../hooks.js";import{useIABPolicyGuard as f}from"../../../hooks/use-iab-policy-guard.js";import{useLateEntry as h}from"../../../hooks/use-late-entry.js";import{useTextDirection as j}from"../../../hooks/use-text-direction.js";import{useUIConfig as b}from"../../../ui-config-context.js";import{defaultTranslationConfig as v}from"../../../utils/default-translation-config.js";import{mergeSlotProps as g}from"../../../utils/merge-slot-props.js";import{FIRST_PAINT_SHEETS as k}from"../../shared/first-paint-sheets.js";import{StreamedSurface as y}from"../../shared/streamed-surface.js";import{SurfaceStyles as x,useSurfaceSheetsReady as N}from"../../shared/surface-styles.js";import{ConsentBannerSurfaceContext as S,mirrorDefaultPosition as C,useConsentBannerSurface as $}from"../surface-context.js";import{EarlyTapScript as L}from"./early-tap-script.js";import{Overlay as P}from"./overlay.js";let R=["opt-in","opt-out"],B=i(({asChild:i,children:s,className:n,style:a,className:l,disableAnimation:y,noStyle:S,models:B=R,...F},A)=>{f();let E=m(),{components:V}=b(),q=c(),w=p()??"opt-in",z=d(),D=$(),G=u(),H=j(G?.language??v.defaultLanguage),I=C(D.position,D.positionSource,D.variant,H),J=N(k,S)&&q&&"banner"===E&&B.includes(w),K=h(J)&&!y,M=g(V?.banner?.root,{baseClassName:e.root,className:n||l,noStyle:S,style:a,...F}),O=S?M.className||"":`${M.className||""} ${y?"":`${e.bannerVisible} ${e.bannerEntering}`}`;return J?r(o,{children:[t(L,{}),t(x,{sheets:k,noStyle:S}),t(P,{}),t("div",{ref:A,...M,className:O,"data-variant":D.variant,"data-position":I,"data-blocking":D.blocking?"true":void 0,"data-prompt":z.prompt,"data-model":z.model,"data-entry":K?"late":void 0,"data-testid":"consent-banner-root",dir:H,children:s})]}):null}),F=({children:o,className:r,noStyle:e,disableAnimation:i,scrollLock:m,trapFocus:c,models:p,uiSource:d,variant:u,position:f,blocking:h,...j})=>{let{banner:b}=n({prompt:{blocking:h,position:f,scrollLock:m,trapFocus:c,variant:u}}),v=b.scrollLock,g=b.trapFocus,k=s(()=>({disableAnimation:i,noStyle:e,scrollLock:v,trapFocus:g}),[i,e,v,g]),y=s(()=>({blocking:b.blocking,position:b.position,positionSource:b.positionSource,variant:b.variant}),[b.blocking,b.position,b.positionSource,b.variant]),x=s(()=>({uiSource:d??"banner"}),[d]);return t(a.Provider,{value:x,children:t(l.Provider,{value:k,children:t(S.Provider,{value:y,children:t(B,{disableAnimation:i,className:r,noStyle:e,models:p,...j,children:o})})})})};B.displayName="ConsentBannerRootChildren";let A=o=>t(y,{children:t(F,{...o})}),E=A;export{A as ConsentBannerRoot,E as Root};
2
+ import{Fragment as o,jsx as t,jsxs as r}from"react/jsx-runtime";import e from"@c15t/ui/styles/components/consent-banner";import{forwardRef as s,useMemo as i}from"react";import{useHeadlessConsentUI as n}from"../../../component-hooks/use-headless-consent-ui.js";import{ConsentTrackingContext as a}from"../../../context/consent-tracking-context.js";import{LocalThemeContext as l}from"../../../context/theme-context.js";import{useActiveUI as m,useHasConsentUI as c,useModel as p,usePolicyRule as d,useTranslations as u}from"../../../hooks.js";import{useIABPolicyGuard as f}from"../../../hooks/use-iab-policy-guard.js";import{useLateEntry as h}from"../../../hooks/use-late-entry.js";import{useTextDirection as j}from"../../../hooks/use-text-direction.js";import{useUIConfig as b}from"../../../ui-config-context.js";import{defaultTranslationConfig as g}from"../../../utils/default-translation-config.js";import{mergeSlotProps as v}from"../../../utils/merge-slot-props.js";import{FIRST_PAINT_SHEETS as k}from"../../shared/first-paint-sheets.js";import{StreamedConsentSurface as y}from"../../shared/streamed-surface.js";import{SurfaceStyles as x,useSurfaceSheetsReady as N}from"../../shared/surface-styles.js";import{ConsentBannerSurfaceContext as S,useConsentBannerSurface as C}from"../surface-context.js";import{EarlyTapScript as $}from"./early-tap-script.js";import{Overlay as L}from"./overlay.js";let P=["opt-in","opt-out"],R=s(({asChild:s,children:i,className:n,style:a,className:l,disableAnimation:y,noStyle:S,models:R=P,...B},F)=>{f();let A=m(),{components:E}=b(),V=c(),q=p()??"opt-in",w=d(),z=C(),D=u(),G=j(D?.language??g.defaultLanguage),H=N(k,S)&&V&&"banner"===A&&R.includes(q),I=h(H)&&!y,J=v(E?.banner?.root,{baseClassName:e.root,className:n||l,noStyle:S,style:a,...B}),K=S?J.className||"":`${J.className||""} ${y?"":`${e.bannerVisible} ${e.bannerEntering}`}`;return H?r(o,{children:[t($,{}),t(x,{sheets:k,noStyle:S}),t(L,{}),t("div",{ref:F,...J,className:K,"data-variant":z.variant,"data-position":z.position,"data-blocking":z.blocking?"true":void 0,"data-prompt":w.prompt,"data-model":w.model,"data-entry":I?"late":void 0,"data-testid":"consent-banner-root",dir:G,children:i})]}):null}),B=({children:o,className:r,noStyle:e,disableAnimation:s,scrollLock:m,trapFocus:c,models:p,uiSource:d,variant:u,position:f,blocking:h,...j})=>{let{banner:b}=n({prompt:{blocking:h,position:f,scrollLock:m,trapFocus:c,variant:u}}),g=b.scrollLock,v=b.trapFocus,k=i(()=>({disableAnimation:s,noStyle:e,scrollLock:g,trapFocus:v}),[s,e,g,v]),y=i(()=>({blocking:b.blocking,position:b.position,positionSource:b.positionSource,variant:b.variant}),[b.blocking,b.position,b.positionSource,b.variant]),x=i(()=>({uiSource:d??"banner"}),[d]);return t(a.Provider,{value:x,children:t(l.Provider,{value:k,children:t(S.Provider,{value:y,children:t(R,{disableAnimation:s,className:r,noStyle:e,models:p,...j,children:o})})})})};R.displayName="ConsentBannerRootChildren";let F=o=>t(y,{children:t(B,{...o})}),A=F;export{F as ConsentBannerRoot,A as Root};
@@ -1 +1 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import n from"@c15t/ui/styles/components/consent-actions";import o from"@c15t/ui/styles/components/consent-banner";import{forwardRef as r,isValidElement as s,useRef as a}from"react";import{useHeadlessConsentUI as i}from"../../component-hooks/use-headless-consent-ui.js";import{useTranslations as l}from"../../component-hooks/use-translations.js";import{useComposedRefs as c}from"../shared/libs/compose-refs.js";import{Slot as d}from"../shared/libs/slot.js";import{usePolicyRule as m,useSetActiveUI as p}from"../../hooks.js";import{useFocusTrap as u}from"../../hooks/use-focus-trap.js";import{useTheme as b}from"../../hooks/use-theme.js";import{useUIConfig as h}from"../../ui-config-context.js";import{getSlotProps as f,mergeSlotProps as C}from"../../utils/merge-slot-props.js";import{Box as B}from"../shared/primitives/box.js";import{ConsentButton as g}from"../shared/primitives/button.js";import{InlineLegalLinks as y}from"../shared/primitives/legal-links/index.js";import{useConsentBannerSurface as j}from"./surface-context.js";import{useBannerCopy as N}from"./use-banner-copy.js";let k={"opt-out":"Do not sell or share my data",preferences:"Manage preferences"},x=r(({children:t,asChild:n,...r},a)=>{let{title:i}=N();return e(B,{ref:a,baseClassName:o.title,"data-testid":"consent-banner-title",slotKey:"banner.title",...r,asChild:!0,children:n&&s(t)?t:e("h2",{children:t??i})})});x.displayName="ConsentBannerTitle";let A=r(({children:n,legalLinks:r,asChild:a,className:i,style:l,noStyle:c,...m},p)=>{let{description:u}=N(),{components:f}=h(),{noStyle:B}=b(),g="banner",j=C(f?.description?.[g],{baseClassName:o.description,className:i,"data-context":g,"data-testid":"consent-banner-description",noStyle:c??B,style:l,...m});return a&&s(n)?e(d,{ref:p,...j,children:n}):t("div",{ref:p,...j,children:[n??u,e(y,{links:r,context:"banner",testIdPrefix:"consent-banner-legal-link"})]})});A.displayName="ConsentBannerDescription";let R=r(({children:t,className:n,style:r,...s},a)=>{let{components:i}=h(),{noStyle:l}=b(),c=C(i?.banner?.actions,{baseClassName:n,noStyle:l,style:r,...s});return e(B,{ref:a,baseClassName:o.footer,"data-testid":"consent-banner-footer",slotKey:"banner.footer",...c,children:t})});R.displayName="ConsentBannerFooter";let S=r(({children:t,...n},r)=>{let{blocking:s}=j(),{title:i}=N(),l=a(null),d=c(l,r);return u(s,l),e(B,{ref:d,tabIndex:-1,baseClassName:o.card,"data-testid":"consent-banner-card",slotKey:"banner.card","aria-label":n["aria-label"]||i,"aria-modal":s?"true":void 0,role:s?"dialog":"region",...n,children:t})});S.displayName="ConsentBannerCard";let v=r(({children:t,...n},r)=>e(B,{ref:r,baseClassName:o.header,"data-testid":"consent-banner-header",slotKey:"banner.header",...n,children:t}));v.displayName="ConsentBannerHeader";let D=r(({children:t,...o},r)=>e(B,{ref:r,baseClassName:n.actionGroup,"data-testid":"consent-banner-footer-sub-group",slotKey:"banner.actionGroup",...o,children:t}));D.displayName="ConsentBannerFooterSubGroup";let z=r(({children:t,...n},o)=>{let{common:r}=l();return e(g,{ref:o,action:"reject-consent",consentAction:"reject","data-testid":"consent-banner-reject-button",closeConsentBanner:!0,...n,children:t??r.rejectAll})});z.displayName="ConsentBannerRejectButton";let F=r(({children:t,...n},o)=>{let{common:r}=l();return e(g,{ref:o,action:"open-consent-dialog",consentAction:"customize","data-testid":"consent-banner-customize-button",...n,children:t??r.customize})});F.displayName="ConsentBannerCustomizeButton";let K=r(({children:t,...n},o)=>{let{common:r}=l(),{noStyle:s}=b();return e(g,{ref:o,action:"accept-consent",consentAction:"accept","data-testid":"consent-banner-accept-button",closeConsentBanner:!0,noStyle:s,...n,children:t??r.acceptAll})});K.displayName="ConsentBannerAcceptButton";let G=r(({children:t,...n},o)=>{let{common:r}=l(),{noStyle:s}=b();return e(g,{ref:o,action:"dismiss-notice",consentAction:"dismiss","data-action":"dismiss","data-testid":"consent-banner-dismiss-button",noStyle:s,...n,children:t??r.acknowledge??r.dismiss})});G.displayName="ConsentBannerDismissButton";let L=r(({right:t,children:n,asChild:r,className:s,style:a,noStyle:i,onClick:c,...u},B)=>{let{rights:g}=l(),y=m(),{components:j}=h(),{noStyle:N}=b(),x=p(),A=("opt-out"===t?g?.optOut:g?.preferences)??k[t];return e(r?d:"button",{ref:B,type:r?void 0:"button",...C(f(j,"banner.rightLink"),{baseClassName:o.rightLink,className:s,noStyle:i??N,style:a}),"data-action":"right","data-right":t,"data-c15t-rights":y.rights.join(" "),"data-testid":`consent-banner-right-link-${t}`,onClick:e=>{c?.(e),e.defaultPrevented||x("dialog")},...u,children:n??A})});L.displayName="ConsentBannerRightLink";let H=r(({rights:t,children:n,...r},s)=>{let{banner:a}=i(),l=(t??a.preferenceControls).filter(e=>"disclosure"!==e);return void 0===n&&0===l.length?null:e(B,{ref:s,baseClassName:o.rights,"data-testid":"consent-banner-rights",slotKey:"banner.rights",...r,children:n??l.map(t=>e(L,{right:t},t))})});H.displayName="ConsentBannerRights";let T=x,I=A,P=R,w=D,M=S,O=v,$=z,q=F,E=K,J=G,Q=H,U=L;export{E as AcceptButton,M as Card,K as ConsentBannerAcceptButton,S as ConsentBannerCard,F as ConsentBannerCustomizeButton,A as ConsentBannerDescription,G as ConsentBannerDismissButton,R as ConsentBannerFooter,D as ConsentBannerFooterSubGroup,v as ConsentBannerHeader,z as ConsentBannerRejectButton,L as ConsentBannerRightLink,H as ConsentBannerRights,x as ConsentBannerTitle,q as CustomizeButton,I as Description,J as DismissButton,P as Footer,w as FooterSubGroup,O as Header,$ as RejectButton,U as RightLink,Q as Rights,T as Title};
1
+ import{jsx as e,jsxs as n}from"react/jsx-runtime";import t from"@c15t/ui/styles/components/consent-actions";import o from"@c15t/ui/styles/components/consent-banner";import{forwardRef as s,isValidElement as r,useRef as a}from"react";import{useConsentLabels as i}from"../../component-hooks/use-consent-labels.js";import{useHeadlessConsentUI as l}from"../../component-hooks/use-headless-consent-ui.js";import{useComposedRefs as c}from"../shared/libs/compose-refs.js";import{Slot as d}from"../shared/libs/slot.js";import{usePolicyRule as m,useSetActiveUI as p}from"../../hooks.js";import{useFocusTrap as u}from"../../hooks/use-focus-trap.js";import{useTheme as b}from"../../hooks/use-theme.js";import{useUIConfig as h}from"../../ui-config-context.js";import{getSlotProps as C,mergeSlotProps as f}from"../../utils/merge-slot-props.js";import{Box as B}from"../shared/primitives/box.js";import{ConsentButton as y}from"../shared/primitives/button.js";import{InlineLegalLinks as g}from"../shared/primitives/legal-links/index.js";import{useConsentBannerSurface as j}from"./surface-context.js";import{useBannerCopy as N}from"./use-banner-copy.js";let k=s(({children:n,asChild:t,...s},a)=>{let{title:i}=N();return e(B,{ref:a,baseClassName:o.title,"data-testid":"consent-banner-title",slotKey:"banner.title",...s,asChild:!0,children:t&&r(n)?n:e("h2",{children:n??i})})});k.displayName="ConsentBannerTitle";let x=s(({children:t,legalLinks:s,asChild:a,className:i,style:l,noStyle:c,...m},p)=>{let{description:u}=N(),{components:C}=h(),{noStyle:B}=b(),y="banner",j=f(C?.description?.[y],{baseClassName:o.description,className:i,"data-context":y,"data-testid":"consent-banner-description",noStyle:c??B,style:l,...m});return a&&r(t)?e(d,{ref:p,...j,children:t}):n("div",{ref:p,...j,children:[t??u,e(g,{links:s,context:"banner",testIdPrefix:"consent-banner-legal-link"})]})});x.displayName="ConsentBannerDescription";let R=s(({children:n,className:t,style:s,...r},a)=>{let{components:i}=h(),{noStyle:l}=b(),c=f(i?.banner?.actions,{baseClassName:t,noStyle:l,style:s,...r});return e(B,{ref:a,baseClassName:o.footer,"data-testid":"consent-banner-footer",slotKey:"banner.footer",...c,children:n})});R.displayName="ConsentBannerFooter";let S=s(({children:n,...t},s)=>{let{blocking:r}=j(),{title:i}=N(),l=a(null),d=c(l,s);return u(r,l),e(B,{ref:d,tabIndex:-1,baseClassName:o.card,"data-testid":"consent-banner-card",slotKey:"banner.card","aria-label":t["aria-label"]||i,"aria-modal":r?"true":void 0,role:r?"dialog":"region",...t,children:n})});S.displayName="ConsentBannerCard";let v=s(({children:n,...t},s)=>e(B,{ref:s,baseClassName:o.header,"data-testid":"consent-banner-header",slotKey:"banner.header",...t,children:n}));v.displayName="ConsentBannerHeader";let A=s(({children:n,...o},s)=>e(B,{ref:s,baseClassName:t.actionGroup,"data-testid":"consent-banner-footer-sub-group",slotKey:"banner.actionGroup",...o,children:n}));A.displayName="ConsentBannerFooterSubGroup";let z=s(({children:n,...t},o)=>{let s=i();return e(y,{ref:o,action:"reject-consent",consentAction:"reject","data-testid":"consent-banner-reject-button",closeConsentBanner:!0,...t,children:n??s.actions.reject})});z.displayName="ConsentBannerRejectButton";let D=s(({children:n,...t},o)=>{let s=i();return e(y,{ref:o,action:"open-consent-dialog",consentAction:"customize","data-testid":"consent-banner-customize-button",...t,children:n??s.actions.customize})});D.displayName="ConsentBannerCustomizeButton";let F=s(({children:n,...t},o)=>{let s=i(),{noStyle:r}=b();return e(y,{ref:o,action:"accept-consent",consentAction:"accept","data-testid":"consent-banner-accept-button",closeConsentBanner:!0,noStyle:r,...t,children:n??s.actions.accept})});F.displayName="ConsentBannerAcceptButton";let K=s(({children:n,...t},o)=>{let s=i(),{noStyle:r}=b();return e(y,{ref:o,action:"dismiss-notice",consentAction:"dismiss","data-action":"dismiss","data-testid":"consent-banner-dismiss-button",noStyle:r,...t,children:n??s.actions.dismiss})});K.displayName="ConsentBannerDismissButton";let G=s(({right:n,children:t,asChild:s,className:r,style:a,noStyle:l,onClick:c,...u},B)=>{let y=i(),g=m(),{components:j}=h(),{noStyle:N}=b(),k=p(),x=y.preferenceControls[n];return e(s?d:"button",{ref:B,type:s?void 0:"button",...f(C(j,"banner.rightLink"),{baseClassName:o.rightLink,className:r,noStyle:l??N,style:a}),"data-action":"right","data-right":n,"data-c15t-rights":g.rights.join(" "),"data-testid":`consent-banner-right-link-${n}`,onClick:e=>{c?.(e),e.defaultPrevented||k("dialog")},...u,children:t??x})});G.displayName="ConsentBannerRightLink";let L=s(({rights:n,children:t,...s},r)=>{let{banner:a}=l(),i=(n??a.preferenceControls).filter(e=>"disclosure"!==e);return void 0===t&&0===i.length?null:e(B,{ref:r,baseClassName:o.rights,"data-testid":"consent-banner-rights",slotKey:"banner.rights",...s,children:t??i.map(n=>e(G,{right:n},n))})});L.displayName="ConsentBannerRights";let H=k,T=x,I=R,P=A,$=S,q=v,w=z,E=D,J=F,M=K,O=L,Q=G;export{J as AcceptButton,$ as Card,F as ConsentBannerAcceptButton,S as ConsentBannerCard,D as ConsentBannerCustomizeButton,x as ConsentBannerDescription,K as ConsentBannerDismissButton,R as ConsentBannerFooter,A as ConsentBannerFooterSubGroup,v as ConsentBannerHeader,z as ConsentBannerRejectButton,G as ConsentBannerRightLink,L as ConsentBannerRights,k as ConsentBannerTitle,E as CustomizeButton,T as Description,M as DismissButton,I as Footer,P as FooterSubGroup,q as Header,w as RejectButton,Q as RightLink,O as Rights,H as Title};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{jsx as t}from"react/jsx-runtime";import{useHeadlessConsentUI as n}from"../../component-hooks/use-headless-consent-ui.js";import{PolicyActionsRenderer as e}from"../shared/policy-actions.js";import{ConsentBannerAcceptButton as o,ConsentBannerCustomizeButton as r,ConsentBannerDismissButton as s,ConsentBannerFooter as c,ConsentBannerFooterSubGroup as i,ConsentBannerRejectButton as a,ConsentBannerRights as d}from"./components.js";import{resolveBannerPrimaryActions as m}from"./resolve-banner-primary-actions.js";let u=function(n,e){let{key:c,consentAction:i,...d}=e;switch(n){case"accept":return t(o,{consentAction:i,"data-testid":"consent-banner-accept-button",...d},c);case"reject":return t(a,{consentAction:i,"data-testid":"consent-banner-reject-button",...d},c);case"customize":return t(r,{consentAction:i,"data-testid":"consent-banner-customize-button",...d},c);case"dismiss":return t(s,{consentAction:i,"data-testid":"consent-banner-dismiss-button",...d},c);case"save":return null;default:throw Error(`Unhandled consent banner action: ${n}`)}},l=({children:o,renderAction:r})=>{let{banner:s}=n();return t(e,{state:{...s,primaryActions:[...m(s.primaryActions,s.orderedActions)]},leading:void 0===o?t(d,{rights:s.preferenceControls}):o,Footer:c,FooterSubGroup:i,classNames:{},renderAction:r,renderDefaultAction:u})},p=l;export{l as ConsentBannerPolicyActions,p as PolicyActions};
2
+ import{jsx as t}from"react/jsx-runtime";import{useHeadlessConsentUI as e}from"../../component-hooks/use-headless-consent-ui.js";import{PolicyActionsRenderer as n}from"../shared/policy-actions.js";import{ConsentBannerAcceptButton as o,ConsentBannerCustomizeButton as s,ConsentBannerDismissButton as r,ConsentBannerFooter as c,ConsentBannerFooterSubGroup as a,ConsentBannerRejectButton as i,ConsentBannerRights as u}from"./components.js";let d=function(e,n){let{key:c,consentAction:a,...u}=n;switch(e){case"accept":return t(o,{consentAction:a,"data-testid":"consent-banner-accept-button",...u},c);case"reject":return t(i,{consentAction:a,"data-testid":"consent-banner-reject-button",...u},c);case"customize":return t(s,{consentAction:a,"data-testid":"consent-banner-customize-button",...u},c);case"dismiss":return t(r,{consentAction:a,"data-testid":"consent-banner-dismiss-button",...u},c);case"save":return null;default:throw Error(`Unhandled consent banner action: ${e}`)}},m=({children:o,renderAction:s})=>{let{banner:r}=e();return t(n,{state:r,leading:void 0===o?t(u,{rights:r.preferenceControls}):o,Footer:c,FooterSubGroup:a,classNames:{},renderAction:s,renderDefaultAction:d})},l=m;export{m as ConsentBannerPolicyActions,l as PolicyActions};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{jsx as t,jsxs as n}from"react/jsx-runtime";import e from"@c15t/ui/styles/components/consent-actions";import r from"@c15t/ui/styles/components/consent-banner";import{Fragment as o}from"react";import{useHeadlessConsentUI as i}from"../../component-hooks/use-headless-consent-ui.js";import{Box as s}from"../shared/primitives/box.js";import{BrandingLink as a}from"../shared/ui/branding.js";import{useComponentConfig as c}from"../../hooks/use-component-config.js";import{useIABPolicyGuard as l}from"../../hooks/use-iab-policy-guard.js";import{StreamedSurface as d}from"../shared/streamed-surface.js";import{ConsentBannerRoot as m}from"./atoms/root.js";import{ConsentBannerAcceptButton as p,ConsentBannerCard as u,ConsentBannerCustomizeButton as h,ConsentBannerDescription as b,ConsentBannerDismissButton as f,ConsentBannerFooter as y,ConsentBannerFooterSubGroup as g,ConsentBannerHeader as j,ConsentBannerRejectButton as k,ConsentBannerRights as A,ConsentBannerTitle as v}from"./components.js";import{ErrorBoundary as N}from"./error-boundary.js";import{resolveBannerPrimaryActions as S}from"./resolve-banner-primary-actions.js";let w=({noStyle:d,disableAnimation:w,scrollLock:C,trapFocus:L,title:x,description:z,rejectButtonText:F,customizeButtonText:P,acceptButtonText:B,dismissButtonText:$,legalLinks:G,hideBranding:E=!1,layout:K,direction:R,primaryButton:U,models:q,uiSource:D,variant:H,position:I,blocking:J})=>{l();let{banner:M}=i({prompt:{blocking:J,direction:R,layout:K,position:I,primaryActions:"string"==typeof U?[U]:U,scrollLock:C,trapFocus:L,variant:H}}),O=c({disableAnimation:w,noStyle:d,scrollLock:M.scrollLock,trapFocus:M.trapFocus}),{orderedActions:Q}=M,T=new Set(Q),V=S(M.primaryActions,Q),W=M.actionGroups,X=M.direction,{shouldFillActions:Y}=M,Z=(n,e)=>{if(!T.has(n))return null;let r=V.includes(n);switch(n){case"reject":return t(k,{consentAction:"reject",isPrimary:r,className:e,"data-action":"reject","data-testid":"consent-banner-reject-button",children:F});case"accept":return t(p,{consentAction:"accept",isPrimary:r,className:e,"data-action":"accept","data-testid":"consent-banner-accept-button",children:B});case"customize":return t(h,{consentAction:"customize",isPrimary:r,className:e,"data-action":"customize","data-testid":"consent-banner-customize-button",children:P});case"dismiss":return t(f,{consentAction:"dismiss",isPrimary:r,className:e,"data-action":"dismiss","data-testid":"consent-banner-dismiss-button",children:$});case"save":return null;default:throw Error(`Unhandled consent banner button type: ${n}`)}};return t(N,{fallback:t("div",{children:"Something went wrong with the Consent Banner."}),children:t(m,{...O,models:q,uiSource:D,variant:H,position:I,blocking:J,children:n(s,{baseClassName:r.cardShell,slotKey:"banner.cardShell",children:[t(a,{hideBranding:E,variant:"banner-tag",slotContext:"banner","data-testid":"consent-banner-branding"}),n(u,{children:[n(j,{children:[t(v,{children:x}),t(b,{legalLinks:G,children:z})]}),n(y,{className:e.actionRoot,"data-direction":X,"data-fill":!!Y||void 0,"data-split":W.length>1&&!Y||void 0,children:[t(A,{rights:M.preferenceControls}),W.map((n,r)=>{if(Array.isArray(n)){let i=n.filter(t=>T.has(t));if(0===i.length)return null;let s=n.join("-");return t(g,{className:e.actionGroup,"data-direction":X,"data-fill":!!Y||void 0,children:i.map(n=>t(o,{children:Z(n)},n))},s?`group-${s}`:`group-${r}`)}return T.has(n)?t(o,{children:Z(n)},n):null})]})]})]})})})},C=n=>t(d,{children:t(w,{...n})});export{C as ConsentBanner};
2
+ import{jsx as t,jsxs as n}from"react/jsx-runtime";import e from"@c15t/ui/styles/components/consent-actions";import r from"@c15t/ui/styles/components/consent-banner";import{Fragment as o}from"react";import{useHeadlessConsentUI as i}from"../../component-hooks/use-headless-consent-ui.js";import{Box as s}from"../shared/primitives/box.js";import{BrandingLink as a}from"../shared/ui/branding.js";import{useComponentConfig as c}from"../../hooks/use-component-config.js";import{useIABPolicyGuard as l}from"../../hooks/use-iab-policy-guard.js";import{StreamedConsentSurface as d}from"../shared/streamed-surface.js";import{ConsentBannerRoot as m}from"./atoms/root.js";import{ConsentBannerAcceptButton as u,ConsentBannerCard as p,ConsentBannerCustomizeButton as h,ConsentBannerDescription as b,ConsentBannerDismissButton as f,ConsentBannerFooter as y,ConsentBannerFooterSubGroup as g,ConsentBannerHeader as j,ConsentBannerRejectButton as k,ConsentBannerRights as A,ConsentBannerTitle as v}from"./components.js";import{ErrorBoundary as N}from"./error-boundary.js";let S=({noStyle:d,disableAnimation:S,scrollLock:w,trapFocus:C,title:L,description:x,rejectButtonText:z,customizeButtonText:F,acceptButtonText:P,dismissButtonText:B,legalLinks:$,hideBranding:G=!1,layout:E,direction:K,primaryButton:R,models:U,uiSource:q,variant:D,position:H,blocking:I})=>{l();let{banner:J}=i({prompt:{blocking:I,direction:K,layout:E,position:H,primaryActions:"string"==typeof R?[R]:R,scrollLock:w,trapFocus:C,variant:D}}),M=c({disableAnimation:S,noStyle:d,scrollLock:J.scrollLock,trapFocus:J.trapFocus}),{orderedActions:O}=J,Q=new Set(O),T=J.actionGroups,V=J.direction,{shouldFillActions:W}=J,X=(n,e)=>{if(!Q.has(n))return null;let r=J.primaryActions.includes(n);switch(n){case"reject":return t(k,{consentAction:"reject",isPrimary:r,className:e,"data-action":"reject","data-testid":"consent-banner-reject-button",children:z});case"accept":return t(u,{consentAction:"accept",isPrimary:r,className:e,"data-action":"accept","data-testid":"consent-banner-accept-button",children:P});case"customize":return t(h,{consentAction:"customize",isPrimary:r,className:e,"data-action":"customize","data-testid":"consent-banner-customize-button",children:F});case"dismiss":return t(f,{consentAction:"dismiss",isPrimary:r,className:e,"data-action":"dismiss","data-testid":"consent-banner-dismiss-button",children:B});case"save":return null;default:throw Error(`Unhandled consent banner button type: ${n}`)}};return t(N,{fallback:t("div",{children:"Something went wrong with the Consent Banner."}),children:t(m,{...M,models:U,uiSource:q,variant:D,position:H,blocking:I,children:n(s,{baseClassName:r.cardShell,slotKey:"banner.cardShell",children:[t(a,{hideBranding:G,variant:"banner-tag",slotContext:"banner","data-testid":"consent-banner-branding"}),n(p,{children:[n(j,{children:[t(v,{children:L}),t(b,{legalLinks:$,children:x})]}),n(y,{className:e.actionRoot,"data-direction":V,"data-fill":!!W||void 0,"data-split":T.length>1&&!W||void 0,children:[t(A,{rights:J.preferenceControls}),T.map((n,r)=>{if(Array.isArray(n)){let i=n.filter(t=>Q.has(t));if(0===i.length)return null;let s=n.join("-");return t(g,{className:e.actionGroup,"data-direction":V,"data-fill":!!W||void 0,children:i.map(n=>t(o,{children:X(n)},n))},s?`group-${s}`:`group-${r}`)}return Q.has(n)?t(o,{children:X(n)},n):null})]})]})]})})})},w=n=>t(d,{children:t(S,{...n})});export{w as ConsentBanner};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{createContext as t,useContext as e}from"react";let n={blocking:!1,position:"bottom-left",positionSource:"default",variant:"floating"},r=t(n),o=function(){return e(r)},i=function(t,e,n,r){return"host"===e||"rtl"!==r||"floating"!==n&&"widget"!==n?t:t.endsWith("-left")?t.replace("-left","-right"):t.endsWith("-right")?t.replace("-right","-left"):t};export{r as ConsentBannerSurfaceContext,n as DEFAULT_CONSENT_BANNER_SURFACE,i as mirrorDefaultPosition,o as useConsentBannerSurface};
2
+ import{createContext as n,useContext as e}from"react";let t={blocking:!1,position:"bottom-left",positionSource:"default",variant:"floating"},o=n(t),r=function(){return e(o)};export{o as ConsentBannerSurfaceContext,t as DEFAULT_CONSENT_BANNER_SURFACE,r as useConsentBannerSurface};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{useTranslations as t}from"../../component-hooks/use-translations.js";import{usePolicyRule as e}from"../../hooks.js";let o=function(){let{cookieBanner:o}=t(),{prompt:n}=e(),i="notice"===n;return{description:i?o.noticeDescription??o.description:o.description,prompt:n,title:i?o.noticeTitle??o.title:o.title}};export{o as useBannerCopy};
2
+ import{resolvePromptCopy as o}from"@c15t/core";import{useMemo as r}from"react";import{useTranslations as e}from"../../component-hooks/use-translations.js";import{usePolicyRule as t}from"../../hooks.js";let n=function(){let n=e(),{prompt:s}=t();return r(()=>({...o(n,s),prompt:s}),[n,s])};export{n as useBannerCopy};
@@ -1,2 +1,2 @@
1
1
  "use client";
2
- import{jsx as e}from"react/jsx-runtime";import{HydrationSnapshotContext as r,StreamedPrefetchContext as t}from"../../context.js";import{useKernel as o}from"../../kernel-selector.js";import*as l from"react";let u="u"<typeof document?l.useEffect:l.useLayoutEffect,n=function(e){if("fulfilled"===e.status)return e.value;throw e},a=function(e){let{use:r}=l;return r?r(e):n(e)},d=({children:l,streamed:n})=>{let d=o(),s=a(n.settled),c=n.snapshotFor(s);return u(()=>{n.adopt(s)},[n,s]),e(r.Provider,{value:c===d.getServerSnapshot()?null:c,children:e(t.Provider,{value:void 0,children:l})})},s=({children:r})=>{let o=(0,l.useContext)(t);return o?e(l.Suspense,{fallback:null,children:e(d,{streamed:o,children:r})}):r};export{s as StreamedSurface,n as readRecorded};
2
+ import{jsx as e}from"react/jsx-runtime";import{HydrationSnapshotContext as r,StreamedPrefetchContext as t}from"../../context.js";import{useKernel as o}from"../../kernel-selector.js";import*as n from"react";let l="u"<typeof document?n.useEffect:n.useLayoutEffect,u=function(e){if("fulfilled"===e.status)return e.value;throw e},a=function(e){let{use:r}=n;return r?r(e):u(e)},s=({children:n,streamed:u})=>{let s=o(),d=a(u.settled),c=u.snapshotFor(d);return l(()=>{u.adopt(d)},[u,d]),e(r.Provider,{value:c===s.getServerSnapshot()?null:c,children:e(t.Provider,{value:void 0,children:n})})},d=({children:r})=>{let o=(0,n.useContext)(t);return o?e(n.Suspense,{fallback:null,children:e(s,{streamed:o,children:r})}):r};export{d as StreamedConsentSurface,u as readRecorded};
package/dist/headless.js CHANGED
@@ -1 +1 @@
1
- export{defaultTranslationConfig,detectBrowserLanguage,mergeTranslationConfigs,prepareTranslationConfig}from"@c15t/core";export{useConsentDialogTrigger}from"./component-hooks/use-panel-trigger.js";export{useHeadlessConsentUI}from"./component-hooks/use-headless-consent-ui.js";export{useTranslations}from"./component-hooks/use-translations.js";export{useColorScheme}from"./hooks/use-color-scheme.js";export{useFocusTrap}from"./hooks/use-focus-trap.js";
1
+ export{defaultTranslationConfig,detectBrowserLanguage,mergeTranslationConfigs,prepareTranslationConfig,resolveActionLabels,resolvePreferenceControlLabels,resolvePromptCopy}from"@c15t/core";export{useConsentLabels}from"./component-hooks/use-consent-labels.js";export{useConsentTextDirection}from"./component-hooks/use-consent-text-direction.js";export{useConsentGate}from"./component-hooks/use-consent-gate.js";export{useConsentDialogTrigger}from"./component-hooks/use-panel-trigger.js";export{useHeadlessConsentUI}from"./component-hooks/use-headless-consent-ui.js";export{useTranslations}from"./component-hooks/use-translations.js";export{useVendorsListedUnder}from"./component-hooks/use-vendors-listed-under.js";export{useBannerCopy}from"./components/prompt/use-banner-copy.js";export{StreamedConsentSurface}from"./components/shared/streamed-surface.js";export{useColorScheme}from"./hooks/use-color-scheme.js";export{useFocusTrap}from"./hooks/use-focus-trap.js";export{useIABPolicyGuard}from"./hooks/use-iab-policy-guard.js";export{useScrollLock}from"./hooks/use-scroll-lock.js";
@@ -1 +1 @@
1
- export{offline}from"../transports/offline.js";
1
+ export{local,offline}from"../transports/offline.js";
package/dist/modes.js CHANGED
@@ -1 +1 @@
1
- export{hosted}from"./transports/hosted.js";export{manifest}from"./transports/manifest.js";export{offline}from"./transports/offline.js";
1
+ export{hosted}from"./transports/hosted.js";export{manifest}from"./transports/manifest.js";export{local,offline}from"./transports/offline.js";
@@ -1 +1 @@
1
- export{offline}from"@c15t/core";
1
+ export{local,offline}from"@c15t/core";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- let e="3.1.0";export{e as version};
1
+ let e="3.2.0-canary.0";export{e as version};
@@ -0,0 +1,102 @@
1
+ import type { AllConsentNames, Translations } from '@c15t/core';
2
+ /**
3
+ * What a consent gate should render for its category.
4
+ *
5
+ * - `'granted'`: consent is given and the page has hydrated. Render the
6
+ * children.
7
+ * - `'pending'`: consent is given but the page hasn't hydrated yet. Render
8
+ * nothing.
9
+ * - `'denied'`: consent isn't given. Render a placeholder with a grant
10
+ * action.
11
+ * - `'policy-blocked'`: strict policy scope excludes this category. Render
12
+ * the policy message and no grant action.
13
+ */
14
+ export type ConsentGateState = 'granted' | 'pending' | 'denied' | 'policy-blocked';
15
+ /**
16
+ * The state, copy and grant action for one consent-gated category.
17
+ */
18
+ export interface UseConsentGateResult {
19
+ /** What to render. See {@link ConsentGateState}. */
20
+ state: ConsentGateState;
21
+ /** Translated category title, falling back to the category name. */
22
+ categoryTitle: string;
23
+ /** Translated placeholder title with `{category}` filled in. */
24
+ title: string | undefined;
25
+ /** Translated grant-button label with `{category}` filled in. */
26
+ actionLabel: string | undefined;
27
+ /** Translated policy-blocked message, with the English fallback. */
28
+ policyBlockedMessage: string;
29
+ /**
30
+ * Opens the consent dialog on its usual view. Unlike the stock gate
31
+ * button, it doesn't start loading a deferred `ConsentDialog` on hover,
32
+ * focus or idle time.
33
+ */
34
+ requestConsent: () => void;
35
+ }
36
+ /**
37
+ * Shown when the translations have no `consentGate.policyBlocked` message.
38
+ *
39
+ * @internal
40
+ */
41
+ export declare const DEFAULT_POLICY_BLOCKED_MESSAGE = "This content is unavailable under your region's consent policy.";
42
+ /**
43
+ * The translated copy for a gate on one category. The stock `ConsentGate`
44
+ * parts read their default text from here, so the hook and the parts
45
+ * always agree.
46
+ *
47
+ * @param translations - The active translation bundle.
48
+ * @param category - The gated category.
49
+ * @returns The category title, placeholder title, button label and
50
+ * policy-blocked message.
51
+ * @internal
52
+ */
53
+ export declare const resolveConsentGateCopy: (translations: Translations, category: AllConsentNames) => Omit<UseConsentGateResult, 'state' | 'requestConsent'>;
54
+ /**
55
+ * Reads what a consent gate on one category should render, with its
56
+ * translated copy and an action that opens the consent dialog.
57
+ *
58
+ * Use it to build a custom gate around an iframe or widget. The stock
59
+ * `ConsentGate` uses the same hook, so a custom gate follows the same
60
+ * rules:
61
+ *
62
+ * - The state follows the category's effective permission, the value
63
+ * `useConsent(category)` returns.
64
+ * - Granted content waits for hydration. The server HTML never contains
65
+ * it, so an iframe inside a streamed `Suspense` boundary loads once.
66
+ * - Under a policy with `scopeMode: 'strict'`, a category outside the
67
+ * policy scope is `'policy-blocked'`. `necessary` is always in scope.
68
+ * - Server rendering and hydration evaluate the snapshot at its own
69
+ * evaluation time and never read the clock, so the gate can sit in a
70
+ * statically prerendered page.
71
+ *
72
+ * On mount the hook registers the category with the provider, so the
73
+ * consent dialog lists it.
74
+ *
75
+ * @param category - The consent category the content needs, such as
76
+ * `'marketing'`.
77
+ * @returns The gate state, the translated copy and `requestConsent`.
78
+ * @throws {Error} When called outside a `ConsentProvider`.
79
+ *
80
+ * @example
81
+ * ```tsx
82
+ * import { useConsentGate } from 'c15t/react/headless';
83
+ * import type { ReactNode } from 'react';
84
+ *
85
+ * function VideoGate({ children }: { children: ReactNode }) {
86
+ * const { state, title, actionLabel, policyBlockedMessage, requestConsent } =
87
+ * useConsentGate('marketing');
88
+ * if (state === 'granted') return children;
89
+ * if (state === 'pending') return null;
90
+ * if (state === 'policy-blocked') return <p>{policyBlockedMessage}</p>;
91
+ * return (
92
+ * <div>
93
+ * <p>{title}</p>
94
+ * <button type="button" onClick={requestConsent}>
95
+ * {actionLabel}
96
+ * </button>
97
+ * </div>
98
+ * );
99
+ * }
100
+ * ```
101
+ */
102
+ export declare const useConsentGate: (category: AllConsentNames) => UseConsentGateResult;
@@ -0,0 +1,32 @@
1
+ import type { PreferenceControl, PresentationAction } from '@c15t/core';
2
+ /** Labels for the controls a consent surface renders. */
3
+ export interface ConsentLabels {
4
+ /** Button label for each action in `actionGroups`. */
5
+ actions: Record<PresentationAction, string>;
6
+ /** Label for each entry in `preferenceControls`. */
7
+ preferenceControls: Record<PreferenceControl, string>;
8
+ }
9
+ /**
10
+ * Translated labels for every consent action and preference control.
11
+ *
12
+ * @remarks
13
+ * Pairs with the `banner` and `dialog` state from `useHeadlessConsentUI`, so
14
+ * a custom UI can label any resolved action without its own map. Keys the
15
+ * active language leaves out use English.
16
+ *
17
+ * @returns Labels keyed by action and by preference control.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * const { banner, performBannerAction } = useHeadlessConsentUI();
22
+ * const labels = useConsentLabels();
23
+ *
24
+ * banner.orderedActions.map((action) => (
25
+ * <button key={action} onClick={() => performBannerAction(action)}>
26
+ * {labels.actions[action]}
27
+ * </button>
28
+ * ));
29
+ * ```
30
+ * @public
31
+ */
32
+ export declare const useConsentLabels: () => ConsentLabels;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The text direction of the language c15t shows consent copy in, for a
3
+ * custom banner or preference center.
4
+ *
5
+ * @remarks
6
+ * c15t can pick the copy's language from the visitor's browser, so an
7
+ * Arabic or Hebrew visitor can read right-to-left copy on a left-to-right
8
+ * page. The stock banner and dialog set `dir` on their root from this
9
+ * language. Set it on yours the same way.
10
+ *
11
+ * @returns `'rtl'` for a right-to-left language, otherwise `'ltr'`.
12
+ *
13
+ * @example
14
+ * ```tsx
15
+ * function Banner({ children }: { children: React.ReactNode }) {
16
+ * const dir = useConsentTextDirection();
17
+ * return <section dir={dir}>{children}</section>;
18
+ * }
19
+ * ```
20
+ * @public
21
+ */
22
+ export declare const useConsentTextDirection: () => "ltr" | "rtl";
@@ -0,0 +1,30 @@
1
+ import type { AllConsentNames, ResolvedVendor } from '@c15t/core';
2
+ /**
3
+ * The vendors the stock preference center lists under one category, for a
4
+ * custom preference center that shows the same rows.
5
+ *
6
+ * @remarks
7
+ * A vendor known only from a script's `vendor` slug gets no row until the
8
+ * site declares a vendor in `vendors` or on the backend, unless the visitor
9
+ * turned it off. Then it stays listed so the visitor can turn it on again.
10
+ * Under an `iab` policy the list is empty.
11
+ *
12
+ * @param category - The category row being rendered.
13
+ * @returns The vendors to list, in declared order.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * function CategoryVendors({ category }: { category: AllConsentNames }) {
18
+ * const vendors = useVendorsListedUnder(category);
19
+ * return (
20
+ * <ul>
21
+ * {vendors.map((vendor) => (
22
+ * <li key={vendor.id}>{vendor.name ?? vendor.id}</li>
23
+ * ))}
24
+ * </ul>
25
+ * );
26
+ * }
27
+ * ```
28
+ * @public
29
+ */
30
+ export declare const useVendorsListedUnder: (category: AllConsentNames) => ResolvedVendor[];
@@ -62,8 +62,8 @@ interface ConsentBannerRootProps extends HTMLAttributes<HTMLDivElement> {
62
62
  /**
63
63
  * Placement of the prompt. Overrides `presentation.prompt.position`.
64
64
  * @remarks Must be valid for the resolved variant; otherwise the variant
65
- * default is used and a development diagnostic is logged. Host-chosen
66
- * positions are never mirrored for right-to-left text.
65
+ * default is used and a development diagnostic is logged. Right-to-left
66
+ * text does not move the prompt.
67
67
  */
68
68
  position?: PromptPosition;
69
69
  /**
@@ -75,7 +75,7 @@ interface ConsentBannerRootProps extends HTMLAttributes<HTMLDivElement> {
75
75
  }
76
76
  /**
77
77
  * The banner, rendered where the server can stream it: see
78
- * `StreamedSurface`.
78
+ * `StreamedConsentSurface`.
79
79
  */
80
80
  declare const ConsentBannerRoot: FC<ConsentBannerRootProps>;
81
81
  declare const Root: FC<ConsentBannerRootProps>;