@owlmeans/web-consent 0.1.18-rc.10

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 (44) hide show
  1. package/README.md +15 -0
  2. package/agent-meta/manifest.json +16 -0
  3. package/agent-meta/skills/web-consent/SKILL.md +189 -0
  4. package/build/consent/component.d.ts +12 -0
  5. package/build/consent/component.d.ts.map +1 -0
  6. package/build/consent/component.js +56 -0
  7. package/build/consent/component.js.map +1 -0
  8. package/build/consent/toggle.d.ts +13 -0
  9. package/build/consent/toggle.d.ts.map +1 -0
  10. package/build/consent/toggle.js +4 -0
  11. package/build/consent/toggle.js.map +1 -0
  12. package/build/hooks.d.ts +13 -0
  13. package/build/hooks.d.ts.map +1 -0
  14. package/build/hooks.js +32 -0
  15. package/build/hooks.js.map +1 -0
  16. package/build/index.d.ts +8 -0
  17. package/build/index.d.ts.map +1 -0
  18. package/build/index.js +6 -0
  19. package/build/index.js.map +1 -0
  20. package/build/lib/utils.d.ts +12 -0
  21. package/build/lib/utils.d.ts.map +1 -0
  22. package/build/lib/utils.js +15 -0
  23. package/build/lib/utils.js.map +1 -0
  24. package/build/policy/component.d.ts +16 -0
  25. package/build/policy/component.d.ts.map +1 -0
  26. package/build/policy/component.js +23 -0
  27. package/build/policy/component.js.map +1 -0
  28. package/build/types.d.ts +49 -0
  29. package/build/types.d.ts.map +1 -0
  30. package/build/types.js +2 -0
  31. package/build/types.js.map +1 -0
  32. package/package.json +53 -0
  33. package/src/consent/component.tsx +144 -0
  34. package/src/consent/toggle.tsx +47 -0
  35. package/src/hooks.ts +45 -0
  36. package/src/index.ts +18 -0
  37. package/src/lib/utils.ts +15 -0
  38. package/src/policy/component.tsx +72 -0
  39. package/src/types.ts +54 -0
  40. package/tests/consent.spec.ts +244 -0
  41. package/tests/context.ts +51 -0
  42. package/tests/harness/index.html +22 -0
  43. package/tests/harness/mount.tsx +77 -0
  44. package/tsconfig.json +16 -0
package/README.md ADDED
@@ -0,0 +1,15 @@
1
+ <!-- owlmeans:agent-guidance:start -->
2
+ ## Agent guidance
3
+
4
+ This package ships embedded agent skills under `agent-meta/`. After installing your
5
+ `@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
6
+ your project's skill store (`.agents/skills/`):
7
+
8
+ ```sh
9
+ npx @owlmeans/agent-skills@^0.1.18-rc.15
10
+ ```
11
+
12
+ The embedded files are version-matched to this package release. Do not edit them
13
+ directly — they are regenerated on each publish. To contribute guidance edits,
14
+ open a PR against the source monorepo.
15
+ <!-- owlmeans:agent-guidance:end -->
@@ -0,0 +1,16 @@
1
+ {
2
+ "schemaVersion": 2,
3
+ "package": "@owlmeans/web-consent",
4
+ "version": "0.1.18-rc.10",
5
+ "generatedAt": "2026-09-12T08:39:52.727Z",
6
+ "canonicalRepo": "https://github.com/owlmeans/common",
7
+ "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "web-consent",
11
+ "category": "package-specific",
12
+ "file": "skills/web-consent/SKILL.md",
13
+ "canonicalPath": ".agents/skills/web-consent/SKILL.md"
14
+ }
15
+ ]
16
+ }
@@ -0,0 +1,189 @@
1
+ ---
2
+ name: web-consent
3
+ description: How to use @owlmeans/web-consent — the React cookie-consent dialog, its re-open button, the generated cookie-policy page and the useConsent hooks, plus the Tailwind @source line every consumer must add. Auto-invoked when mounting a consent dialog, rendering a cookie policy, gating a feature on a consent category, or importing CookieConsent, CookiePolicy or useConsent.
4
+ user-invocable: false
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # @owlmeans/web-consent
9
+
10
+ **Layer:** Web (React)
11
+ **Install:** `"@owlmeans/web-consent": "^0.1.18-rc.10"` in `dependencies`
12
+
13
+ The browser components of the consent set. The model — categories, storage, migration, the store,
14
+ Consent Mode signalling — is `@owlmeans/consent`, and this package re-exports a **named selection**
15
+ of it (listed under Key Exports) so an application usually has one import. Five public
16
+ `@owlmeans/consent` exports are deliberately not in that list — `makeConsentStore`,
17
+ `normalizeLocale`, `CONSENT_SETUP_FLAG`, `CONSENT_SIGNAL_DEFAULTS` and the `ConsentListener` type —
18
+ so a caller that needs one of them imports it from `@owlmeans/consent` directly. **Read the
19
+ `consent` skill for the model; read this one for the components.**
20
+
21
+ ## Deliberately not a shadcn package
22
+
23
+ It uses **no shadcn primitive and no `@` alias**, and owns a four-line `cn` of its own. The `@`
24
+ contract exists so a consumer's THEME and primitives win — `cn` has neither in it, and requiring an
25
+ alias for it would mean adopting an OwlMeans UI contract before you may render a consent notice.
26
+ One of the surfaces that needs this dialog most is a static site with its own component library and
27
+ no such alias, where that would simply fail to build.
28
+
29
+ For the same reason it is not part of `@owlmeans/web-panel`: that would drag ~30 packages onto a
30
+ cookie notice. `@owlmeans/web-panel/consent` is the thin binding to OwlMeans i18n on top of these
31
+ components — see below.
32
+
33
+ ## Key Exports
34
+
35
+ | Export | Description |
36
+ |--------|-------------|
37
+ | `CookieConsent` | The preferences dialog **and** the persistent re-open button |
38
+ | `CookiePolicy` | The cookie-policy page, generated from the configuration in force |
39
+ | `ConsentToggle` | One category row — locked and labelled when the category is required |
40
+ | `useConsent(opts?)` | This document's consent state and the actions over it (`UseConsentModel`) — **it also initialises the store on mount**, see below |
41
+ | `useConsentCategory(key)` | Whether one category is granted, for a component gating a single thing |
42
+ | `CookieConsentProps` / `CookiePolicyProps` / `ConsentLink` | The component props |
43
+ | Re-exports from `@owlmeans/consent` | The complete list: `consentStore`, `openConsent`, `isConsented`, `readConsent`, `writeConsent`, `clearConsent`, `migrateConsent`, `applyConsent`, `pushConsentDefaults`, `consentBootstrapScript`, `consentDefaults`, `consentUpdate`, `gtagConsent`, `DEFAULT_CONSENT_CATEGORIES`, `DEFAULT_CONSENT_MESSAGES`, `defaultConsentTranslate`, `interpolate`, `CONSENT_KEY`, `CONSENT_COOKIE_DAYS`, `CONSENT_SCHEMA_VERSION`, `CONSENT_LOCALES`, `CONSENT_ESSENTIAL` / `CONSENT_ANALYTICS` / `CONSENT_MARKETING`, and the types `ConsentCategory`, `ConsentOptions`, `ConsentReason`, `ConsentRecord`, `ConsentSignal`, `ConsentState`, `ConsentStore`, `ConsentLocale` |
44
+
45
+ ## Mounting the dialog
46
+
47
+ `CookieConsent` is mounted **once**, at the application root or in the layout. It opens itself when
48
+ no decision is stored, renders nothing but the re-open button once one is, and needs no state from
49
+ the caller:
50
+
51
+ ```tsx
52
+ import { CookieConsent } from '@owlmeans/web-consent'
53
+
54
+ <CookieConsent
55
+ policyHref="/legal/cookies"
56
+ links={[{ href: '/legal/privacy', labelKey: 'privacy', defaultLabel: 'Privacy Policy' }]}
57
+ />
58
+ ```
59
+
60
+ - **The category set the dialog renders is the set it saves.** Pass `categories` here and pass the
61
+ same set to whatever stamps the head snippet, or the two disagree about what was asked. The same
62
+ goes for `storageKey`, `cookieDays` and `cookieDomain`.
63
+ - `locale` picks the packaged language; leave it unset and English is used, so an app with a
64
+ language of its own passes it (or mounts `PanelCookieConsent`, which does). A region tag is
65
+ reduced to its base (`pl-PL` → `pl`), and anything outside the seven falls back to English.
66
+ - `policyHref` is a plain string, so an app-resolved path, a framework route and a raw href all
67
+ work — the component must not know how its host does routing.
68
+ - `noReopenButton` hides the floating button for an app that offers a footer link instead; that link
69
+ calls `openConsent('reopen')`.
70
+ - `silent` skips every `dataLayer` and global write. It is for tests and for an app that runs no
71
+ tags at all.
72
+ - The draft is **re-seeded from storage every time the dialog opens**, not from the last render — a
73
+ visitor reopening preferences must see the answer they gave.
74
+
75
+ **When the dialog was raised by something waiting on it** — `reason === 'login'`, which the sign-in
76
+ precondition in `@owlmeans/client-iam` raises — it says so and relabels the primary action *Accept
77
+ & continue*. That is what makes the interruption legible instead of looking like the page asking
78
+ twice. Word that path as an acknowledgement, never as "you must consent to essential cookies": a
79
+ required category is disclosure, not a question.
80
+
81
+ ## Reading the decision
82
+
83
+ ```tsx
84
+ import { useConsent, useConsentCategory, isConsented } from '@owlmeans/web-consent'
85
+
86
+ const consent = useConsent() // { record, open, reason, granted, save, acceptAll, openDialog, close }
87
+ const analytics = useConsentCategory('analytics') // one category, for a component gating one thing
88
+ if (isConsented('analytics')) { /* outside React — a click handler, a service */ }
89
+ ```
90
+
91
+ Both hooks subscribe through `useSyncExternalStore` over the module-singleton store, because consent
92
+ is a property of the DOCUMENT rather than of a component tree: the dialog, the re-open button, the
93
+ policy page and whatever an app gates on it all read one value, and local copies would disagree the
94
+ moment one of them saved. During server rendering the snapshot is "no record, closed", so the dialog
95
+ never flashes into static HTML before hydration corrects it.
96
+
97
+ **`useConsent` is not a pure reader — it runs `consentStore.init(opts)` on mount.** Two consequences
98
+ a component that only wanted to read has to plan for:
99
+
100
+ - **It opens the dialog.** `init` reads storage, and with no stored record it publishes
101
+ `open: true, reason: 'initial'`. So a `useConsent()` in a card that merely wanted `granted('analytics')`
102
+ gates the page for a first-time visitor. Read a single category with `useConsentCategory(key)` —
103
+ that hook subscribes and does **not** init — or `isConsented(key)` outside React.
104
+ - **The first `useConsent` to mount fixes the Consent Mode defaults.** `init` calls
105
+ `pushConsentDefaults`, which is idempotent through the `CONSENT_SETUP_FLAG` window flag: whoever
106
+ gets there first declares the `consent/default` signals from *its* categories, and every later
107
+ call returns immediately. A bare `useConsent()` mounting before `CookieConsent` therefore declares
108
+ `DEFAULT_CONSENT_CATEGORIES`' signals and the app's own `categories` never declare theirs — which
109
+ silently breaks the parity rule above. Pass the app's `opts` wherever `useConsent` is called, or
110
+ do not call it outside the dialog.
111
+
112
+ `opts` are read on the mounting pass only (the effect's dependency list is empty), so changing them
113
+ in a later render has no effect on that mount.
114
+
115
+ ## The policy page
116
+
117
+ `CookiePolicy` states only what the widget provably does — the categories in force, the storage key,
118
+ the dual storage, the retention — all read from the same configuration the dialog renders. That is
119
+ why it is generated rather than written: a hand-written policy drifts the first time a category
120
+ changes, and nobody notices because nobody reads it until it matters.
121
+
122
+ ```tsx
123
+ <CookiePolicy operator="Example Sp. z o.o." privacyHref="/legal/privacy" termsHref="/legal/terms" />
124
+ ```
125
+
126
+ Everything OwlMeans cannot assert on the operator's behalf — who the controller is, the lawful
127
+ basis, how to exercise rights — is deferred to those two links. It also renders a *Manage
128
+ preferences* control, so the policy page is a way back into the decision.
129
+
130
+ ## Tailwind — one `@source` line, pointing at `src`
131
+
132
+ The components emit Tailwind classes that exist nowhere in the consumer's own sources, and
133
+ Tailwind's scanner reads the CSS root plus `@source` directives only, excluding `node_modules`. Add
134
+ the line to the app's Tailwind entry:
135
+
136
+ ```css
137
+ @import "tailwindcss";
138
+
139
+ @source "<relative path to node_modules>/@owlmeans/web-consent/src";
140
+ ```
141
+
142
+ **Point it at `src`, never at `build`.** The scanner applies the `.gitignore` of whatever repository
143
+ a path resolves into, and under a linked workspace the `node_modules` entry is a symlink into a
144
+ monorepo where every package's build output is ignored. A `build` source there scans **zero files
145
+ and reports nothing**: the build succeeds, the CSS is emitted, and the dialog renders *half*-styled
146
+ — the utilities the app happens to use elsewhere still exist while the ones only this package asks
147
+ for (`max-w-lg`, `bg-black/70`, `z-[999998]`) do not. What reaches the screen is a full-width,
148
+ backdrop-less dialog with the page bleeding through, and nothing in the app's sources looks wrong.
149
+ `src` is tracked and ships in the published tarball, so one path serves both a linked checkout and
150
+ an npm install.
151
+
152
+ The failure is silent, so verify rather than assume — grep the emitted stylesheet for a class only
153
+ this package uses:
154
+
155
+ ```sh
156
+ grep -c 'max-w-lg' dist/assets/*.css
157
+ ```
158
+
159
+ A `*/` inside a CSS comment ends it early, so a comment above an `@source` that spells out a glob
160
+ closes the comment at that star-slash and turns the next `@source` into an invalid declaration.
161
+ Describe a glob in words, or keep it out of the comment.
162
+
163
+ ## i18n
164
+
165
+ Given no `translate` prop, the components resolve every string through the packaged bundle for
166
+ `locale` — seven languages, the same set the framework supports. **Once a `translate` prop is given,
167
+ the packaged bundle is not consulted at all**, so a wrapper that forwards a framework resolver alone
168
+ renders the English default for every key the application has not overridden, in every language. The
169
+ resolver must fall through to `defaultConsentTranslate(locale)` for the default — which is exactly
170
+ what `@owlmeans/web-panel/consent` does, and why an app inside the panel family mounts
171
+ `PanelCookieConsent` / `PanelCookiePolicy` rather than these components directly.
172
+
173
+ `ConsentToggle` is exported for a host that builds its own dialog body; the wording it shows is the
174
+ caller's, already resolved.
175
+
176
+ ## Depends On
177
+
178
+ - `@owlmeans/consent` — the model; the named selection above is re-exported from this package's root
179
+ - Peers (app-provided): `react`, `tailwindcss`, `tailwind-merge`, `clsx`, `lucide-react`
180
+
181
+ ## Related
182
+
183
+ - `consent` — the model: categories, `globalVar`, storage and migration, Consent Mode v2, the
184
+ ordering rule. Read it before changing anything a category means
185
+ - `web-gtm` — the head snippet that carries a stored decision to the tag manager
186
+ - `astro` — stamping that snippet from a static site's layout
187
+ - `login-methods` / `login-plugins` — the sign-in precondition that raises this dialog with
188
+ `reason: 'login'`
189
+ - `web-panel` — its `./consent` subpath, the OwlMeans-i18n binding of these components
@@ -0,0 +1,12 @@
1
+ import type { FC } from 'react';
2
+ import type { CookieConsentProps } from '../types.js';
3
+ /**
4
+ * The cookie preferences dialog, and the button that brings it back.
5
+ *
6
+ * Deliberately built from raw elements rather than shadcn primitives. One of the three surfaces
7
+ * this serves is an Astro island on a site that vendors its own component library, and requiring a
8
+ * consumer to install a UI family in order to render a consent notice would put the notice out of
9
+ * reach of the site that needs it most.
10
+ */
11
+ export declare const CookieConsent: FC<CookieConsentProps>;
12
+ //# sourceMappingURL=component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAS/B,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAErD;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CA2HhD,CAAA"}
@@ -0,0 +1,56 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useCallback, useEffect, useState } from 'react';
3
+ import { Cookie } from 'lucide-react';
4
+ import { DEFAULT_CONSENT_CATEGORIES, defaultConsentTranslate, readConsent, } from '@owlmeans/consent';
5
+ import { cn } from '../lib/utils.js';
6
+ import { useConsent } from '../hooks.js';
7
+ import { ConsentToggle } from './toggle.js';
8
+ /**
9
+ * The cookie preferences dialog, and the button that brings it back.
10
+ *
11
+ * Deliberately built from raw elements rather than shadcn primitives. One of the three surfaces
12
+ * this serves is an Astro island on a site that vendors its own component library, and requiring a
13
+ * consumer to install a UI family in order to render a consent notice would put the notice out of
14
+ * reach of the site that needs it most.
15
+ */
16
+ export const CookieConsent = props => {
17
+ const categories = props.categories ?? DEFAULT_CONSENT_CATEGORIES;
18
+ const t = props.translate ?? defaultConsentTranslate(props.locale);
19
+ const consent = useConsent({
20
+ categories,
21
+ ...(props.storageKey != null ? { storageKey: props.storageKey } : {}),
22
+ ...(props.cookieDays != null ? { cookieDays: props.cookieDays } : {}),
23
+ ...(props.cookieDomain != null ? { cookieDomain: props.cookieDomain } : {}),
24
+ ...(props.silent != null ? { silent: props.silent } : {}),
25
+ });
26
+ const optional = categories.filter(category => category.required !== true);
27
+ const [draft, setDraft] = useState({});
28
+ // Re-seed the draft whenever the dialog opens, from what is actually stored: a visitor who opens
29
+ // preferences a second time must see the answer they gave, not the one the last render held.
30
+ useEffect(() => {
31
+ if (!consent.open) {
32
+ return;
33
+ }
34
+ const stored = consent.record ?? readConsent({
35
+ ...(props.storageKey != null ? { storageKey: props.storageKey } : {}),
36
+ });
37
+ setDraft(Object.fromEntries(optional.map(category => [category.key, stored?.[category.key] === true])));
38
+ }, [consent.open, consent.record]);
39
+ const persist = useCallback((values) => {
40
+ const record = Object.fromEntries([
41
+ ...categories.filter(c => c.required === true).map(c => [c.key, true]),
42
+ ...optional.map(c => [c.key, values[c.key] === true]),
43
+ ]);
44
+ consent.save(record);
45
+ }, [categories, consent]);
46
+ const onSave = useCallback(() => persist(draft), [draft, persist]);
47
+ const onAcceptAll = useCallback(() => persist(Object.fromEntries(optional.map(c => [c.key, true]))), [optional, persist]);
48
+ // The dialog was raised by something that needs an answer before it can continue — signing in,
49
+ // today. Saying so, and relabelling the primary action, is what makes the interruption make
50
+ // sense rather than look like the page asking twice.
51
+ const gated = consent.reason === 'login';
52
+ return _jsxs(_Fragment, { children: [consent.open && _jsx("div", { className: "fixed inset-0 z-[999998] flex items-center justify-center overflow-y-auto bg-black/70 px-4 py-6 backdrop-blur-md", "aria-modal": "true", role: "dialog", "aria-labelledby": "cc-title", "aria-describedby": "cc-desc", "data-consent-dialog": true, children: _jsxs("div", { className: cn('relative z-[999999] w-full max-w-lg rounded-2xl border border-border bg-popover/95 p-6 shadow-[0_25px_60px_-12px_rgba(0,0,0,0.5)] ring-1 ring-primary/20 backdrop-blur-xl sm:p-8', props.className), children: [_jsx("div", { className: "absolute inset-x-0 top-0 h-1 rounded-t-2xl bg-gradient-to-r from-primary to-secondary" }), _jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-primary/10 text-primary", children: _jsx(Cookie, { className: "h-5 w-5", "aria-hidden": "true" }) }), _jsx("h2", { id: "cc-title", className: "text-xl font-bold text-popover-foreground sm:text-2xl", children: t('title', 'Cookie Preferences') })] }), _jsx("p", { id: "cc-desc", className: "mb-6 text-sm leading-relaxed text-muted-foreground", children: t('description', 'We use cookies to enhance your browsing experience, serve personalized ads or content, and analyze our traffic.') }), _jsx("div", { className: "space-y-3", children: categories.map(category => _jsx(ConsentToggle, { id: `cc-${category.key}`, label: t(category.labelKey, category.key), description: t(category.descriptionKey, ''), checked: draft[category.key] === true, ...(category.required === true ? { required: true } : {}), requiredLabel: t('required', 'Required'), onChange: value => setDraft(current => ({ ...current, [category.key]: value })) }, category.key)) }), gated && _jsx("p", { role: "status", "data-consent-reason": true, className: "mt-4 text-sm text-popover-foreground", children: t('loginReason', 'Signing in stores a session cookie. Accept essential cookies to continue.') }), _jsxs("div", { className: "mt-8 flex flex-col gap-3 sm:flex-row sm:gap-4", children: [_jsx("button", { type: "button", onClick: onSave, "data-consent-save": true, className: "flex-1 rounded-lg border border-border bg-transparent px-4 py-3 text-sm font-semibold text-popover-foreground transition-colors hover:bg-accent focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: t('savePreferences', 'Save Preferences') }), _jsx("button", { type: "button", onClick: onAcceptAll, "data-consent-accept-all": true, className: "flex-1 rounded-lg bg-primary px-4 py-3 text-sm font-bold text-primary-foreground shadow-lg shadow-primary/25 transition-all hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2", children: gated
53
+ ? t('acceptAndContinue', 'Accept & continue')
54
+ : t('acceptAll', 'Accept All') })] }), (props.policyHref != null || props.links != null) && _jsxs("div", { className: "mt-4 flex flex-wrap justify-center gap-3 text-xs text-muted-foreground", children: [props.policyHref != null && _jsx("a", { href: props.policyHref, target: "_blank", rel: "noopener noreferrer", className: "underline underline-offset-2 transition-colors hover:text-popover-foreground", children: t('policyLink', 'Cookie Policy') }), props.links?.map(link => _jsx("a", { href: link.href, target: "_blank", rel: "noopener noreferrer", className: "underline underline-offset-2 transition-colors hover:text-popover-foreground", children: t(link.labelKey, link.defaultLabel) }, link.href))] })] }) }), props.noReopenButton !== true && consent.record != null && !consent.open && _jsx("button", { type: "button", onClick: () => consent.openDialog('reopen'), "aria-label": t('openPreferences', 'Cookie preferences'), "data-consent-reopen": true, className: "fixed bottom-5 left-5 z-[999997] flex h-12 w-12 items-center justify-center rounded-full border border-border bg-popover text-primary shadow-lg transition-transform hover:scale-110 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: _jsx(Cookie, { className: "h-5 w-5", "aria-hidden": "true" }) })] });
55
+ };
56
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/consent/component.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { FC } from 'react';
2
+ export interface ConsentToggleProps {
3
+ id: string;
4
+ label: string;
5
+ description: string;
6
+ checked: boolean;
7
+ /** A required category is disclosure, not a question: locked on, and labelled as such. */
8
+ required?: boolean;
9
+ requiredLabel: string;
10
+ onChange: (value: boolean) => void;
11
+ }
12
+ export declare const ConsentToggle: FC<ConsentToggleProps>;
13
+ //# sourceMappingURL=toggle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle.d.ts","sourceRoot":"","sources":["../../src/consent/toggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAG/B,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;IAChB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACnC;AAED,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAgChD,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from '../lib/utils.js';
3
+ export const ConsentToggle = ({ id, label, description, checked, required, requiredLabel, onChange }) => (_jsxs("div", { className: "flex items-start justify-between rounded-lg border border-border/50 bg-muted/30 p-3", children: [_jsxs("div", { className: "flex-1 pr-4", children: [_jsxs("div", { className: "mb-0.5 flex items-center gap-2 text-sm font-semibold text-popover-foreground", children: [label, required === true && _jsx("span", { className: "rounded bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium uppercase tracking-wide text-primary", children: requiredLabel })] }), _jsx("p", { className: "text-xs text-muted-foreground", children: description })] }), _jsxs("label", { className: cn('relative h-6 w-11 flex-shrink-0', required === true ? 'cursor-not-allowed opacity-70' : 'cursor-pointer'), children: [_jsx("input", { id: id, type: "checkbox", className: "sr-only", checked: checked || required === true, disabled: required === true, onChange: event => onChange(event.target.checked) }), _jsx("span", { className: cn('absolute inset-0 rounded-full transition-colors duration-300', checked || required === true ? 'bg-primary' : 'bg-muted-foreground/30') }), _jsx("span", { className: cn('absolute top-0.5 h-5 w-5 rounded-full bg-white shadow transition-transform duration-300', checked || required === true ? 'translate-x-5' : 'translate-x-0.5') })] })] }));
4
+ //# sourceMappingURL=toggle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toggle.js","sourceRoot":"","sources":["../../src/consent/toggle.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AAapC,MAAM,CAAC,MAAM,aAAa,GAA2B,CACnD,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,EACtE,EAAE,CAAC,CACH,eAAK,SAAS,EAAC,qFAAqF,aAClG,eAAK,SAAS,EAAC,aAAa,aAC1B,eAAK,SAAS,EAAC,8EAA8E,aAC1F,KAAK,EACL,QAAQ,KAAK,IAAI,IAAI,eAAM,SAAS,EAAC,kGAAkG,YACrI,aAAa,GACT,IACH,EACN,YAAG,SAAS,EAAC,+BAA+B,YAAE,WAAW,GAAK,IAC1D,EACN,iBAAO,SAAS,EAAE,EAAE,CAAC,iCAAiC,EAAE,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,gBAAgB,CAAC,aAC7H,gBACE,EAAE,EAAE,EAAE,EACN,IAAI,EAAC,UAAU,EACf,SAAS,EAAC,SAAS,EACnB,OAAO,EAAE,OAAO,IAAI,QAAQ,KAAK,IAAI,EACrC,QAAQ,EAAE,QAAQ,KAAK,IAAI,EAC3B,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,GACjD,EACF,eAAM,SAAS,EAAE,EAAE,CACjB,8DAA8D,EAC9D,OAAO,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,wBAAwB,CACvE,GAAI,EACL,eAAM,SAAS,EAAE,EAAE,CACjB,yFAAyF,EACzF,OAAO,IAAI,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,iBAAiB,CACnE,GAAI,IACC,IACJ,CACP,CAAA"}
@@ -0,0 +1,13 @@
1
+ import type { ConsentOptions } from '@owlmeans/consent';
2
+ import type { UseConsentModel } from './types.js';
3
+ /**
4
+ * Subscribe to this document's consent state.
5
+ *
6
+ * `useSyncExternalStore` rather than local state, because consent is one thing per document and
7
+ * several components read it — the dialog, the re-open button, the policy page, and whatever a
8
+ * host app gates on it. Local copies would disagree the moment one of them saved.
9
+ */
10
+ export declare const useConsent: (opts?: ConsentOptions) => UseConsentModel;
11
+ /** Whether one category is granted. For a component that gates a single thing. */
12
+ export declare const useConsentCategory: (key: string) => boolean;
13
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AACvD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,UAAU,UAAW,cAAc,KAAG,eAqBlD,CAAA;AAED,kFAAkF;AAClF,eAAO,MAAM,kBAAkB,QAAS,MAAM,KAAG,OAQhD,CAAA"}
package/build/hooks.js ADDED
@@ -0,0 +1,32 @@
1
+ import { useEffect, useSyncExternalStore } from 'react';
2
+ import { consentStore } from '@owlmeans/consent';
3
+ /**
4
+ * Subscribe to this document's consent state.
5
+ *
6
+ * `useSyncExternalStore` rather than local state, because consent is one thing per document and
7
+ * several components read it — the dialog, the re-open button, the policy page, and whatever a
8
+ * host app gates on it. Local copies would disagree the moment one of them saved.
9
+ */
10
+ export const useConsent = (opts) => {
11
+ const state = useSyncExternalStore(listener => consentStore.subscribe(listener), () => consentStore.get(),
12
+ // On the server there is no browser to have an opinion, and rendering the dialog into static
13
+ // HTML would flash it for every visitor before hydration corrected them.
14
+ () => ({ record: null, open: false, reason: null }));
15
+ useEffect(() => { consentStore.init(opts); }, []);
16
+ return {
17
+ record: state.record,
18
+ open: state.open,
19
+ reason: state.reason,
20
+ granted: key => consentStore.granted(key),
21
+ save: record => consentStore.save(record),
22
+ acceptAll: () => consentStore.acceptAll(),
23
+ openDialog: reason => consentStore.open(reason),
24
+ close: () => consentStore.close(),
25
+ };
26
+ };
27
+ /** Whether one category is granted. For a component that gates a single thing. */
28
+ export const useConsentCategory = (key) => {
29
+ const state = useSyncExternalStore(listener => consentStore.subscribe(listener), () => consentStore.get(), () => ({ record: null, open: false, reason: null }));
30
+ return state.record != null && consentStore.granted(key);
31
+ };
32
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAIhD;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAqB,EAAmB,EAAE;IACnE,MAAM,KAAK,GAAG,oBAAoB,CAChC,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,EAC5C,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE;IACxB,6FAA6F;IAC7F,yEAAyE;IACzE,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CACpD,CAAA;IAED,SAAS,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEhD,OAAO;QACL,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,MAAM,EAAE,KAAK,CAAC,MAAM;QACpB,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC;QACzC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QACzC,SAAS,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE;QACzC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC;QAC/C,KAAK,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE;KAClC,CAAA;AACH,CAAC,CAAA;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAAW,EAAW,EAAE;IACzD,MAAM,KAAK,GAAG,oBAAoB,CAChC,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,CAAC,QAAQ,CAAC,EAC5C,GAAG,EAAE,CAAC,YAAY,CAAC,GAAG,EAAE,EACxB,GAAG,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CACpD,CAAA;IAED,OAAO,KAAK,CAAC,MAAM,IAAI,IAAI,IAAI,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;AAC1D,CAAC,CAAA"}
@@ -0,0 +1,8 @@
1
+ export type * from './types.js';
2
+ export * from './hooks.js';
3
+ export * from './consent/component.js';
4
+ export * from './consent/toggle.js';
5
+ export * from './policy/component.js';
6
+ export { consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, migrateConsent, applyConsent, pushConsentDefaults, consentBootstrapScript, consentDefaults, consentUpdate, gtagConsent, DEFAULT_CONSENT_CATEGORIES, DEFAULT_CONSENT_MESSAGES, defaultConsentTranslate, interpolate, CONSENT_KEY, CONSENT_COOKIE_DAYS, CONSENT_SCHEMA_VERSION, CONSENT_LOCALES, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, } from '@owlmeans/consent';
7
+ export type { ConsentCategory, ConsentOptions, ConsentReason, ConsentRecord, ConsentSignal, ConsentState, ConsentStore, ConsentLocale, } from '@owlmeans/consent';
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,YAAY,CAAA;AAC1B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AAErC,OAAO,EACL,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAC/E,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EACzE,eAAe,EAAE,aAAa,EAAE,WAAW,EAC3C,0BAA0B,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,WAAW,EAC1F,WAAW,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,eAAe,EACzE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,GACxD,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAC1F,YAAY,EAAE,aAAa,GAC5B,MAAM,mBAAmB,CAAA"}
package/build/index.js ADDED
@@ -0,0 +1,6 @@
1
+ export * from './hooks.js';
2
+ export * from './consent/component.js';
3
+ export * from './consent/toggle.js';
4
+ export * from './policy/component.js';
5
+ export { consentStore, openConsent, isConsented, readConsent, writeConsent, clearConsent, migrateConsent, applyConsent, pushConsentDefaults, consentBootstrapScript, consentDefaults, consentUpdate, gtagConsent, DEFAULT_CONSENT_CATEGORIES, DEFAULT_CONSENT_MESSAGES, defaultConsentTranslate, interpolate, CONSENT_KEY, CONSENT_COOKIE_DAYS, CONSENT_SCHEMA_VERSION, CONSENT_LOCALES, CONSENT_ESSENTIAL, CONSENT_ANALYTICS, CONSENT_MARKETING, } from '@owlmeans/consent';
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,cAAc,YAAY,CAAA;AAC1B,cAAc,wBAAwB,CAAA;AACtC,cAAc,qBAAqB,CAAA;AACnC,cAAc,uBAAuB,CAAA;AAErC,OAAO,EACL,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,YAAY,EAC/E,cAAc,EAAE,YAAY,EAAE,mBAAmB,EAAE,sBAAsB,EACzE,eAAe,EAAE,aAAa,EAAE,WAAW,EAC3C,0BAA0B,EAAE,wBAAwB,EAAE,uBAAuB,EAAE,WAAW,EAC1F,WAAW,EAAE,mBAAmB,EAAE,sBAAsB,EAAE,eAAe,EACzE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB,GACxD,MAAM,mBAAmB,CAAA"}
@@ -0,0 +1,12 @@
1
+ import { type ClassValue } from 'clsx';
2
+ /**
3
+ * Class-name merge, owned by this package rather than reached through the `@` alias contract.
4
+ *
5
+ * The `@` contract exists so a consumer's own THEME and shadcn primitives win over a package's
6
+ * copies. `cn` has neither in it — it is four lines of string merging — and requiring an alias for
7
+ * it would mean every consumer of a consent dialog must first adopt an OwlMeans UI contract. One
8
+ * of the sites that needs this dialog most is an Astro site with its own component library and no
9
+ * such alias, so the dialog would simply fail to build there.
10
+ */
11
+ export declare function cn(...inputs: ClassValue[]): string;
12
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,UAAU,EAAE,MAAM,MAAM,CAAA;AAG5C;;;;;;;;GAQG;AACH,wBAAgB,EAAE,CAAC,GAAG,MAAM,EAAE,UAAU,EAAE,UAEzC"}
@@ -0,0 +1,15 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+ /**
4
+ * Class-name merge, owned by this package rather than reached through the `@` alias contract.
5
+ *
6
+ * The `@` contract exists so a consumer's own THEME and shadcn primitives win over a package's
7
+ * copies. `cn` has neither in it — it is four lines of string merging — and requiring an alias for
8
+ * it would mean every consumer of a consent dialog must first adopt an OwlMeans UI contract. One
9
+ * of the sites that needs this dialog most is an Astro site with its own component library and no
10
+ * such alias, so the dialog would simply fail to build there.
11
+ */
12
+ export function cn(...inputs) {
13
+ return twMerge(clsx(inputs));
14
+ }
15
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/lib/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAmB,MAAM,MAAM,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AAExC;;;;;;;;GAQG;AACH,MAAM,UAAU,EAAE,CAAC,GAAG,MAAoB;IACxC,OAAO,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAA;AAC9B,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { FC } from 'react';
2
+ import type { CookiePolicyProps } from '../types.js';
3
+ /**
4
+ * A cookie policy that states only what this application can actually assert.
5
+ *
6
+ * Which categories are in force, what each one drives, where the choice is stored and for how
7
+ * long — all of it read from the same configuration the dialog renders, so the page cannot claim
8
+ * something the widget does not do. That is the whole reason it is generated rather than written:
9
+ * a hand-written policy drifts from the code the first time a category changes, and nobody notices
10
+ * because nobody reads it until it matters.
11
+ *
12
+ * Everything OwlMeans cannot assert on the operator's behalf — who the controller is, what the
13
+ * lawful basis is, how to exercise rights — is deferred to their own privacy policy and terms.
14
+ */
15
+ export declare const CookiePolicy: FC<CookiePolicyProps>;
16
+ //# sourceMappingURL=component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/policy/component.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAM/B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAEpD;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,YAAY,EAAE,EAAE,CAAC,iBAAiB,CAmD9C,CAAA"}
@@ -0,0 +1,23 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CONSENT_COOKIE_DAYS, CONSENT_KEY, DEFAULT_CONSENT_CATEGORIES, defaultConsentTranslate, interpolate, openConsent, } from '@owlmeans/consent';
3
+ import { cn } from '../lib/utils.js';
4
+ /**
5
+ * A cookie policy that states only what this application can actually assert.
6
+ *
7
+ * Which categories are in force, what each one drives, where the choice is stored and for how
8
+ * long — all of it read from the same configuration the dialog renders, so the page cannot claim
9
+ * something the widget does not do. That is the whole reason it is generated rather than written:
10
+ * a hand-written policy drifts from the code the first time a category changes, and nobody notices
11
+ * because nobody reads it until it matters.
12
+ *
13
+ * Everything OwlMeans cannot assert on the operator's behalf — who the controller is, what the
14
+ * lawful basis is, how to exercise rights — is deferred to their own privacy policy and terms.
15
+ */
16
+ export const CookiePolicy = props => {
17
+ const categories = props.categories ?? DEFAULT_CONSENT_CATEGORIES;
18
+ const t = props.translate ?? defaultConsentTranslate(props.locale);
19
+ const storageKey = props.storageKey ?? CONSENT_KEY;
20
+ const days = props.cookieDays ?? CONSENT_COOKIE_DAYS;
21
+ return _jsxs("article", { className: cn('prose prose-sm max-w-2xl', props.className), "data-cookie-policy": true, children: [_jsx("h1", { children: t('policyTitle', 'Cookie Policy') }), _jsx("p", { children: t('policyIntro', 'This page describes the cookies and similar storage this application uses, and how you can control them.') }), _jsx("ul", { children: categories.map(category => _jsxs("li", { children: [_jsx("strong", { children: t(category.labelKey, category.key) }), category.required === true && _jsxs(_Fragment, { children: [" \u2014 ", t('required', 'Required')] }), _jsx("br", {}), t(category.descriptionKey, '')] }, category.key)) }), _jsx("p", { children: interpolate(t('policyStorage', 'Your choice is stored in this browser under "{{key}}", both in local storage and as a cookie.'), { key: storageKey }) }), _jsx("p", { children: interpolate(t('policyRetention', 'The record is kept for {{days}} days, after which you will be asked again.'), { days }) }), props.operator != null && props.operator !== '' && _jsxs("p", { children: [interpolate(t('policyOperator', 'This application is operated by {{operator}}.'), { operator: props.operator }), ' ', t('policyContact', 'See their privacy policy and terms for how they handle your data.')] }), _jsxs("p", { children: [props.privacyHref != null && props.privacyHref !== '' && _jsx("a", { href: props.privacyHref, target: "_blank", rel: "noopener noreferrer", children: t('privacy', 'Privacy Policy') }), props.privacyHref != null && props.termsHref != null && ' · ', props.termsHref != null && props.termsHref !== '' && _jsx("a", { href: props.termsHref, target: "_blank", rel: "noopener noreferrer", children: t('terms', 'Terms & Conditions') })] }), _jsx("p", { children: _jsx("button", { type: "button", onClick: () => openConsent('reopen'), "data-cookie-policy-manage": true, children: t('manage', 'Manage preferences') }) })] });
22
+ };
23
+ //# sourceMappingURL=component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"component.js","sourceRoot":"","sources":["../../src/policy/component.tsx"],"names":[],"mappings":";AACA,OAAO,EACL,mBAAmB,EAAE,WAAW,EAAE,0BAA0B,EAC5D,uBAAuB,EAAE,WAAW,EAAE,WAAW,GAClD,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAAE,EAAE,EAAE,MAAM,iBAAiB,CAAA;AAGpC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,YAAY,GAA0B,KAAK,CAAC,EAAE;IACzD,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,0BAA0B,CAAA;IACjE,MAAM,CAAC,GAAG,KAAK,CAAC,SAAS,IAAI,uBAAuB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IAClE,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,IAAI,WAAW,CAAA;IAClD,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,IAAI,mBAAmB,CAAA;IAEpD,OAAO,mBAAS,SAAS,EAAE,EAAE,CAAC,0BAA0B,EAAE,KAAK,CAAC,SAAS,CAAC,yCACxE,uBAAK,CAAC,CAAC,aAAa,EAAE,eAAe,CAAC,GAAM,EAC5C,sBAAI,CAAC,CAAC,aAAa,EAAE,0GAA0G,CAAC,GAAK,EAErI,uBACG,UAAU,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAC,yBAC1B,2BAAS,CAAC,CAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,GAAU,EACpD,QAAQ,CAAC,QAAQ,KAAK,IAAI,IAAI,0CAAM,CAAC,CAAC,UAAU,EAAE,UAAU,CAAC,IAAI,EAClE,cAAM,EACL,CAAC,CAAC,QAAQ,CAAC,cAAc,EAAE,EAAE,CAAC,KAJI,QAAQ,CAAC,GAAG,CAK5C,CAAC,GACH,EAEL,sBAAI,WAAW,CACb,CAAC,CAAC,eAAe,EAAE,+FAA+F,CAAC,EACnH,EAAE,GAAG,EAAE,UAAU,EAAE,CACpB,GAAK,EACN,sBAAI,WAAW,CACb,CAAC,CAAC,iBAAiB,EAAE,4EAA4E,CAAC,EAClG,EAAE,IAAI,EAAE,CACT,GAAK,EAEL,KAAK,CAAC,QAAQ,IAAI,IAAI,IAAI,KAAK,CAAC,QAAQ,KAAK,EAAE,IAAI,wBACjD,WAAW,CAAC,CAAC,CAAC,gBAAgB,EAAE,+CAA+C,CAAC,EAC/E,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC,EAC9B,GAAG,EACH,CAAC,CAAC,eAAe,EAAE,mEAAmE,CAAC,IACtF,EAEJ,wBACG,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,KAAK,CAAC,WAAW,KAAK,EAAE,IAAI,YACxD,IAAI,EAAE,KAAK,CAAC,WAAW,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,YAClE,CAAC,CAAC,SAAS,EAAE,gBAAgB,CAAC,GAAK,EACpC,KAAK,CAAC,WAAW,IAAI,IAAI,IAAI,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,KAAK,EAC7D,KAAK,CAAC,SAAS,IAAI,IAAI,IAAI,KAAK,CAAC,SAAS,KAAK,EAAE,IAAI,YACpD,IAAI,EAAE,KAAK,CAAC,SAAS,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,YAChE,CAAC,CAAC,OAAO,EAAE,oBAAoB,CAAC,GAAK,IACrC,EAEJ,sBACE,iBAAQ,IAAI,EAAC,QAAQ,EAAC,OAAO,EAAE,GAAG,EAAE,CAAC,WAAW,CAAC,QAAQ,CAAC,+CACvD,CAAC,CAAC,QAAQ,EAAE,oBAAoB,CAAC,GAC3B,GACP,IACI,CAAA;AACZ,CAAC,CAAA"}
@@ -0,0 +1,49 @@
1
+ import type { ConsentCategory, ConsentReason, ConsentRecord } from '@owlmeans/consent';
2
+ export interface ConsentLink {
3
+ href: string;
4
+ labelKey: string;
5
+ defaultLabel: string;
6
+ }
7
+ export interface CookieConsentProps {
8
+ locale?: string;
9
+ categories?: ConsentCategory[];
10
+ /** `(key, defaultValue) => string`. Defaults to the built-in seven-language bundle. */
11
+ translate?: (key: string, defaultValue: string) => string;
12
+ /**
13
+ * The cookie-policy page. A plain string, so an Astro route, a framework-resolved path and a raw
14
+ * href all work — this component must not know how its host does routing.
15
+ */
16
+ policyHref?: string;
17
+ /** Anything else worth linking from the dialog: privacy, terms. */
18
+ links?: ConsentLink[];
19
+ storageKey?: string;
20
+ cookieDays?: number;
21
+ cookieDomain?: string;
22
+ silent?: boolean;
23
+ /** Hide the persistent re-open button, for an app that offers a footer link instead. */
24
+ noReopenButton?: boolean;
25
+ className?: string;
26
+ }
27
+ export interface CookiePolicyProps {
28
+ locale?: string;
29
+ translate?: (key: string, defaultValue: string) => string;
30
+ categories?: ConsentCategory[];
31
+ /** Who operates this application — the branding record supplies it in a generated app. */
32
+ operator?: string;
33
+ privacyHref?: string;
34
+ termsHref?: string;
35
+ storageKey?: string;
36
+ cookieDays?: number;
37
+ className?: string;
38
+ }
39
+ export interface UseConsentModel {
40
+ record: ConsentRecord | null;
41
+ open: boolean;
42
+ reason: ConsentReason | null;
43
+ granted: (key: string) => boolean;
44
+ save: (record: ConsentRecord) => void;
45
+ acceptAll: () => void;
46
+ openDialog: (reason?: ConsentReason) => void;
47
+ close: () => void;
48
+ }
49
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EAAE,aAAa,EAAE,aAAa,EAC9C,MAAM,mBAAmB,CAAA;AAE1B,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,uFAAuF;IACvF,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACzD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,mEAAmE;IACnE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wFAAwF;IACxF,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,MAAM,CAAA;IACzD,UAAU,CAAC,EAAE,eAAe,EAAE,CAAA;IAC9B,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;IACjC,IAAI,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,UAAU,EAAE,CAAC,MAAM,CAAC,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB"}
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}