@tickboxhq/banner-default 0.0.12 → 0.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/dist/{chunk-WJSFVAK4.js → chunk-DGDSIXCT.js} +14 -2
- package/dist/chunk-DGDSIXCT.js.map +1 -0
- package/dist/react/index.js +5 -13
- package/dist/react/index.js.map +1 -1
- package/dist/vue/index.d.ts +5 -0
- package/dist/vue/index.js +80 -64
- package/dist/vue/index.js.map +1 -1
- package/package.json +6 -6
- package/dist/chunk-WJSFVAK4.js.map +0 -1
package/README.md
CHANGED
|
@@ -98,6 +98,22 @@ The full set: `--tb-bg`, `--tb-fg`, `--tb-fg-muted`, `--tb-border`, `--tb-shadow
|
|
|
98
98
|
|
|
99
99
|
Light/dark follows `prefers-color-scheme`. Pass `theme="light"` or `theme="dark"` to override.
|
|
100
100
|
|
|
101
|
+
### Equal prominence — read this before re-styling
|
|
102
|
+
|
|
103
|
+
Accept All and Reject All on the first banner layer use the `.tb-btn-equal` class and look identical by default. This is deliberate. UK ICO and EU EDPB guidance treats unequal visual weight on those buttons as a dark pattern, and ICO has fined sites for it.
|
|
104
|
+
|
|
105
|
+
If you want to add brand colours, apply them to `.tb-btn-equal` so both buttons change together:
|
|
106
|
+
|
|
107
|
+
```css
|
|
108
|
+
.tb-root .tb-btn-equal {
|
|
109
|
+
background: #0070c4;
|
|
110
|
+
color: #fff;
|
|
111
|
+
border-color: #0070c4;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Do NOT use `--tb-primary-bg` to brand the Accept button alone — `--tb-primary-bg` is reserved for the modal Save button (a second-layer action where the user has already engaged with the customise flow, so equal-prominence rules don't apply the same way). Overriding it won't affect the first-layer banner buttons.
|
|
116
|
+
|
|
101
117
|
## Banner vs notice
|
|
102
118
|
|
|
103
119
|
Use the banner when you have any `consent`-mode categories — most EU sites. It's a bottom bar with Accept all / Reject all / Customise, and Customise opens a modal with per-category toggles.
|
|
@@ -180,6 +180,18 @@ var TICKBOX_STYLES = `
|
|
|
180
180
|
border-color: var(--tb-border);
|
|
181
181
|
}
|
|
182
182
|
.tb-btn-secondary:hover { background: var(--tb-secondary-bg-hover); }
|
|
183
|
+
/*
|
|
184
|
+
* Style for Accept All and Reject All on the first banner layer. They MUST
|
|
185
|
+
* look identical \u2014 UK ICO and EU EDPB treat unequal visual weight on those
|
|
186
|
+
* buttons as a dark pattern. Customisation should not break this symmetry;
|
|
187
|
+
* if you need to apply brand colours, apply them here, not to one button.
|
|
188
|
+
*/
|
|
189
|
+
.tb-btn-equal {
|
|
190
|
+
background: var(--tb-secondary-bg);
|
|
191
|
+
color: var(--tb-secondary-fg);
|
|
192
|
+
border-color: var(--tb-border);
|
|
193
|
+
}
|
|
194
|
+
.tb-btn-equal:hover { background: var(--tb-secondary-bg-hover); }
|
|
183
195
|
.tb-btn-ghost {
|
|
184
196
|
background: transparent;
|
|
185
197
|
color: var(--tb-fg-muted);
|
|
@@ -352,5 +364,5 @@ function injectStyles() {
|
|
|
352
364
|
}
|
|
353
365
|
|
|
354
366
|
export { DEFAULT_BANNER_COPY, DEFAULT_NOTICE_COPY, injectStyles };
|
|
355
|
-
//# sourceMappingURL=chunk-
|
|
356
|
-
//# sourceMappingURL=chunk-
|
|
367
|
+
//# sourceMappingURL=chunk-DGDSIXCT.js.map
|
|
368
|
+
//# sourceMappingURL=chunk-DGDSIXCT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/shared/copy.ts","../src/shared/styles.ts"],"names":[],"mappings":"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","file":"chunk-DGDSIXCT.js","sourcesContent":["export type BannerCopy = {\n title: string\n description: string\n acceptLabel: string\n rejectLabel: string\n customiseLabel: string\n saveLabel: string\n closeLabel: string\n policyLinkLabel: string\n requiredBadge: string\n}\n\nexport type NoticeCopy = {\n title: string\n description: string\n acknowledgeLabel: string\n optOutLabel: string\n policyLinkLabel: string\n}\n\nexport const DEFAULT_BANNER_COPY: BannerCopy = {\n title: 'Cookies and tracking',\n description:\n 'We use cookies to make this site work and, with your consent, to measure usage. You can choose what to allow.',\n acceptLabel: 'Accept all',\n rejectLabel: 'Reject all',\n customiseLabel: 'Customise',\n saveLabel: 'Save preferences',\n closeLabel: 'Close',\n policyLinkLabel: 'Privacy policy',\n requiredBadge: 'Required',\n}\n\nexport const DEFAULT_NOTICE_COPY: NoticeCopy = {\n title: 'A note about analytics',\n description:\n 'We use privacy-friendly analytics to understand how this site is used. No personal data is collected and no advertising profiles are built.',\n acknowledgeLabel: 'Got it',\n optOutLabel: 'Opt out',\n policyLinkLabel: 'Privacy policy',\n}\n","/**\n * Inline CSS for the default banner / notice / modal components.\n *\n * Uses CSS custom properties so users can re-theme without forking. Light\n * and dark themes are wired through `prefers-color-scheme` and the\n * `[data-tb-theme]` attribute.\n *\n * Visual style: GitHub-ish — system font, 6px corners, subtle border + soft\n * shadow, equal-prominence accept/reject buttons.\n */\nexport const TICKBOX_STYLES = `\n:where(.tb-root) {\n --tb-bg: #ffffff;\n --tb-fg: #1f2328;\n --tb-fg-muted: #59636e;\n --tb-border: #d1d9e0;\n --tb-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);\n --tb-primary-bg: #1f2328;\n --tb-primary-fg: #ffffff;\n --tb-primary-bg-hover: #000000;\n --tb-secondary-bg: #ffffff;\n --tb-secondary-fg: #1f2328;\n --tb-secondary-bg-hover: #f6f8fa;\n --tb-link: #0969da;\n --tb-radius: 6px;\n --tb-z: 2147483000;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica,\n Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";\n color: var(--tb-fg);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n :where(.tb-root:not([data-tb-theme=\"light\"])) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n }\n}\n:where(.tb-root[data-tb-theme=\"dark\"]) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n}\n\n.tb-banner {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 16px 20px;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-banner-text {\n flex: 1 1 320px;\n min-width: 0;\n}\n.tb-banner-title {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 14px;\n}\n.tb-banner-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n}\n.tb-banner-actions {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.tb-notice {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 14px 16px;\n max-width: 360px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-notice-title {\n font-weight: 600;\n margin: 0 0 4px;\n font-size: 14px;\n}\n.tb-notice-desc {\n margin: 0 0 10px;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-notice-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-link {\n color: var(--tb-link);\n text-decoration: none;\n font-size: 13px;\n margin-right: auto;\n}\n.tb-link:hover { text-decoration: underline; }\n\n.tb-btn {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--tb-radius);\n padding: 6px 14px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n line-height: 1.5;\n transition: background-color 80ms ease;\n white-space: nowrap;\n}\n.tb-btn:focus-visible {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n.tb-btn-primary {\n background: var(--tb-primary-bg);\n color: var(--tb-primary-fg);\n}\n.tb-btn-primary:hover { background: var(--tb-primary-bg-hover); }\n.tb-btn-secondary {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-secondary:hover { background: var(--tb-secondary-bg-hover); }\n/*\n * Style for Accept All and Reject All on the first banner layer. They MUST\n * look identical — UK ICO and EU EDPB treat unequal visual weight on those\n * buttons as a dark pattern. Customisation should not break this symmetry;\n * if you need to apply brand colours, apply them here, not to one button.\n */\n.tb-btn-equal {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-equal:hover { background: var(--tb-secondary-bg-hover); }\n.tb-btn-ghost {\n background: transparent;\n color: var(--tb-fg-muted);\n padding: 6px 10px;\n}\n.tb-btn-ghost:hover { color: var(--tb-fg); }\n\n.tb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 18, 24, 0.5);\n z-index: var(--tb-z);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-modal {\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n width: 100%;\n max-width: 520px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n}\n.tb-modal-head {\n padding: 14px 16px;\n border-bottom: 1px solid var(--tb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n.tb-modal-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n.tb-modal-body {\n padding: 12px 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.tb-modal-foot {\n padding: 12px 16px;\n border-top: 1px solid var(--tb-border);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-cat {\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n padding: 12px;\n display: flex;\n gap: 12px;\n align-items: flex-start;\n}\n.tb-cat-text { flex: 1; min-width: 0; }\n.tb-cat-name {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.tb-cat-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-badge {\n display: inline-block;\n font-size: 11px;\n font-weight: 500;\n color: var(--tb-fg-muted);\n background: var(--tb-secondary-bg-hover);\n border: 1px solid var(--tb-border);\n border-radius: 999px;\n padding: 1px 8px;\n}\n\n.tb-switch {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n.tb-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n.tb-switch-track {\n position: absolute;\n inset: 0;\n background: var(--tb-border);\n border-radius: 999px;\n transition: background-color 100ms ease;\n cursor: pointer;\n}\n.tb-switch-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--tb-bg);\n border-radius: 50%;\n transition: transform 100ms ease;\n}\n.tb-switch input:checked + .tb-switch-track {\n background: var(--tb-primary-bg);\n}\n.tb-switch input:checked + .tb-switch-track .tb-switch-thumb {\n transform: translateX(14px);\n}\n.tb-switch input:disabled + .tb-switch-track {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.tb-switch input:focus-visible + .tb-switch-track {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n\n@keyframes tb-fade-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (max-width: 640px) {\n .tb-banner {\n flex-direction: column;\n align-items: stretch;\n }\n .tb-banner-actions {\n flex-direction: column;\n }\n .tb-banner-actions .tb-btn { width: 100%; }\n}\n`\n\nconst STYLE_ID = 'tickbox-default-styles'\n\nlet injected = false\n\n/**\n * Insert the stylesheet into `<head>` exactly once per page. Safe to call\n * from every component mount — subsequent calls are no-ops. No-op on the\n * server (no `document`).\n */\nexport function injectStyles(): void {\n if (injected) return\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) {\n injected = true\n return\n }\n const el = document.createElement('style')\n el.id = STYLE_ID\n el.textContent = TICKBOX_STYLES\n document.head.appendChild(el)\n injected = true\n}\n"]}
|
package/dist/react/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { DEFAULT_BANNER_COPY, injectStyles, DEFAULT_NOTICE_COPY } from '../chunk-
|
|
1
|
+
import { DEFAULT_BANNER_COPY, injectStyles, DEFAULT_NOTICE_COPY } from '../chunk-DGDSIXCT.js';
|
|
2
2
|
import { ConsentBanner, ConsentNotice } from '@tickboxhq/react';
|
|
3
3
|
import { useState, useEffect, useId, useRef } from 'react';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -24,17 +24,9 @@ function BannerInner({
|
|
|
24
24
|
] }),
|
|
25
25
|
/* @__PURE__ */ jsxs("div", { className: "tb-banner-actions", children: [
|
|
26
26
|
props.policyUrl && /* @__PURE__ */ jsx("a", { className: "tb-link", href: props.policyUrl, children: copy.policyLinkLabel }),
|
|
27
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-
|
|
28
|
-
/* @__PURE__ */ jsx(
|
|
29
|
-
|
|
30
|
-
{
|
|
31
|
-
type: "button",
|
|
32
|
-
className: "tb-btn tb-btn-secondary",
|
|
33
|
-
onClick: () => setShowModal(true),
|
|
34
|
-
children: copy.customiseLabel
|
|
35
|
-
}
|
|
36
|
-
),
|
|
37
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-primary", onClick: () => api.grantAll(), children: copy.acceptLabel })
|
|
27
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-equal", onClick: () => api.denyAll(), children: copy.rejectLabel }),
|
|
28
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-ghost", onClick: () => setShowModal(true), children: copy.customiseLabel }),
|
|
29
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-equal", onClick: () => api.grantAll(), children: copy.acceptLabel })
|
|
38
30
|
] })
|
|
39
31
|
] }),
|
|
40
32
|
showModal && /* @__PURE__ */ jsx(
|
|
@@ -137,7 +129,7 @@ function CustomiseModal({
|
|
|
137
129
|
] }, cat.id);
|
|
138
130
|
}) }),
|
|
139
131
|
/* @__PURE__ */ jsxs("div", { className: "tb-modal-foot", children: [
|
|
140
|
-
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-
|
|
132
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-equal", onClick: () => api.denyAll(), children: copy.rejectLabel }),
|
|
141
133
|
/* @__PURE__ */ jsx("button", { type: "button", className: "tb-btn tb-btn-primary", onClick: () => api.save(), children: copy.saveLabel })
|
|
142
134
|
] })
|
|
143
135
|
]
|
package/dist/react/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/react/banner.tsx","../../src/react/notice.tsx"],"names":["jsx","useEffect","jsxs"],"mappings":";;;;;AAmCO,SAAS,qBAAqB,KAAA,EAAkC;AACrE,EAAA,uBAAO,GAAA,CAAC,iBAAe,QAAA,EAAA,CAAC,GAAA,yBAAS,WAAA,EAAA,EAAY,GAAA,EAAU,OAAc,CAAA,EAAG,CAAA;AAC1E;AAEA,SAAS,WAAA,CAAY;AAAA,EACnB,GAAA;AAAA,EACA;AACF,CAAA,EAGG;AACD,EAAA,MAAM,OAAmB,EAAE,GAAG,mBAAA,EAAqB,GAAG,MAAM,IAAA,EAAK;AACjE,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAEhD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,YAAA,EAAa;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,MAAM,KAAA,GAAQ,EAAE,iBAAiB,KAAA,CAAM,KAAA,KAAU,EAAC;AAErE,EAAA,uBACE,IAAA,CAAA,QAAA,EAAA,EACE,QAAA,EAAA;AAAA,oBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,mBAAA,EAAoB,IAAA,EAAK,UAAS,YAAA,EAAY,IAAA,CAAK,KAAA,EAAQ,GAAG,UAAA,EAC3E,QAAA,EAAA;AAAA,sBAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,gBAAA,EACb,QAAA,EAAA;AAAA,wBAAA,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iBAAA,EAAmB,QAAA,EAAA,IAAA,CAAK,KAAA,EAAM,CAAA;AAAA,wBAC3C,GAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,gBAAA,EAAkB,eAAK,WAAA,EAAY;AAAA,OAAA,EAClD,CAAA;AAAA,sBACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mBAAA,EACZ,QAAA,EAAA;AAAA,QAAA,KAAA,CAAM,SAAA,wBACJ,GAAA,EAAA,EAAE,SAAA,EAAU,WAAU,IAAA,EAAM,KAAA,CAAM,SAAA,EAChC,QAAA,EAAA,IAAA,CAAK,eAAA,EACR,CAAA;AAAA,wBAEF,GAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAU,yBAAA,EAA0B,OAAA,EAAS,MAAM,GAAA,CAAI,OAAA,EAAQ,EAClF,QAAA,EAAA,IAAA,CAAK,WAAA,EACR,CAAA;AAAA,wBACA,GAAA;AAAA,UAAC,QAAA;AAAA,UAAA;AAAA,YACC,IAAA,EAAK,QAAA;AAAA,YACL,SAAA,EAAU,yBAAA;AAAA,YACV,OAAA,EAAS,MAAM,YAAA,CAAa,IAAI,CAAA;AAAA,YAE/B,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,SACR;AAAA,wBACA,GAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAU,uBAAA,EAAwB,OAAA,EAAS,MAAM,GAAA,CAAI,QAAA,EAAS,EACjF,QAAA,EAAA,IAAA,CAAK,WAAA,EACR;AAAA,OAAA,EACF;AAAA,KAAA,EACF,CAAA;AAAA,IACC,SAAA,oBACC,GAAA;AAAA,MAAC,cAAA;AAAA,MAAA;AAAA,QACC,GAAA;AAAA,QACA,IAAA;AAAA,QACA,OAAO,KAAA,CAAM,KAAA;AAAA,QACb,OAAA,EAAS,MAAM,YAAA,CAAa,KAAK;AAAA;AAAA;AACnC,GAAA,EAEJ,CAAA;AAEJ;AAEA,SAAS,cAAA,CAAe;AAAA,EACtB,GAAA;AAAA,EACA,IAAA;AAAA,EACA,KAAA;AAAA,EACA;AACF,CAAA,EAKG;AACD,EAAA,MAAM,UAAU,KAAA,EAAM;AACtB,EAAA,MAAM,YAAA,GAAe,OAA8B,IAAI,CAAA;AAEvD,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,SAAS,MAAM,CAAA,EAAkB;AAC/B,MAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,OAAA,EAAQ;AAChC,MAAA,IAAI,EAAE,GAAA,KAAQ,KAAA,EAAO,SAAA,CAAU,CAAA,EAAG,aAAa,OAAO,CAAA;AAAA,IACxD;AACA,IAAA,QAAA,CAAS,gBAAA,CAAiB,WAAW,KAAK,CAAA;AAC1C,IAAA,MAAM,oBAAoB,QAAA,CAAS,aAAA;AACnC,IAAA,MAAM,KAAA,GAAQ,aAAa,OAAA,EAAS,aAAA;AAAA,MAClC;AAAA,KACF;AACA,IAAA,KAAA,EAAO,KAAA,EAAM;AACb,IAAA,OAAO,MAAM;AACX,MAAA,QAAA,CAAS,mBAAA,CAAoB,WAAW,KAAK,CAAA;AAC7C,MAAA,iBAAA,EAAmB,KAAA,IAAQ;AAAA,IAC7B,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,OAAO,CAAC,CAAA;AAEZ,EAAA,MAAM,aAAa,KAAA,GAAQ,EAAE,eAAA,EAAiB,KAAA,KAAU,EAAC;AAEzD,EAAA,uBACE,GAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,2BAAA;AAAA,MACV,OAAA,EAAS,OAAA;AAAA,MACT,SAAA,EAAW,CAAC,CAAA,KAAM;AAChB,QAAA,IAAI,CAAA,CAAE,GAAA,KAAQ,QAAA,EAAU,OAAA,EAAQ;AAAA,MAClC,CAAA;AAAA,MACA,IAAA,EAAK,cAAA;AAAA,MACJ,GAAG,UAAA;AAAA,MAEJ,QAAA,kBAAA,IAAA;AAAA,QAAC,KAAA;AAAA,QAAA;AAAA,UACC,GAAA,EAAK,YAAA;AAAA,UACL,SAAA,EAAU,UAAA;AAAA,UACV,IAAA,EAAK,QAAA;AAAA,UACL,YAAA,EAAW,MAAA;AAAA,UACX,iBAAA,EAAiB,OAAA;AAAA,UACjB,OAAA,EAAS,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,UAClC,SAAA,EAAW,CAAC,CAAA,KAAM,CAAA,CAAE,eAAA,EAAgB;AAAA,UAEpC,QAAA,EAAA;AAAA,4BAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,eAAA,EACb,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAG,EAAA,EAAI,OAAA,EAAS,SAAA,EAAU,gBAAA,EACxB,eAAK,cAAA,EACR,CAAA;AAAA,8BACA,GAAA;AAAA,gBAAC,QAAA;AAAA,gBAAA;AAAA,kBACC,IAAA,EAAK,QAAA;AAAA,kBACL,SAAA,EAAU,qBAAA;AAAA,kBACV,cAAY,IAAA,CAAK,UAAA;AAAA,kBACjB,OAAA,EAAS,OAAA;AAAA,kBACV,QAAA,EAAA;AAAA;AAAA;AAED,aAAA,EACF,CAAA;AAAA,4BACA,GAAA,CAAC,SAAI,SAAA,EAAU,eAAA,EACZ,cAAI,QAAA,CAAS,GAAA,CAAI,CAAC,GAAA,KAAQ;AACzB,cAAA,MAAM,OAAA,GAAU,GAAA,CAAI,SAAA,CAAU,GAAA,CAAI,EAAE,CAAA,KAAM,IAAA;AAC1C,cAAA,MAAM,EAAA,GAAK,CAAA,OAAA,EAAU,GAAA,CAAI,EAAE,CAAA,CAAA;AAC3B,cAAA,uBACE,IAAA,CAAC,KAAA,EAAA,EAAiB,SAAA,EAAU,QAAA,EAC1B,QAAA,EAAA;AAAA,gCAAA,IAAA,CAAC,KAAA,EAAA,EAAI,WAAU,aAAA,EACb,QAAA,EAAA;AAAA,kCAAA,IAAA,CAAC,GAAA,EAAA,EAAE,WAAU,aAAA,EACX,QAAA,EAAA;AAAA,oCAAA,GAAA,CAAC,OAAA,EAAA,EAAM,OAAA,EAAS,EAAA,EAAK,QAAA,EAAA,GAAA,CAAI,EAAA,EAAG,CAAA;AAAA,oBAC3B,IAAI,QAAA,oBAAY,GAAA,CAAC,UAAK,SAAA,EAAU,UAAA,EAAY,eAAK,aAAA,EAAc;AAAA,mBAAA,EAClE,CAAA;AAAA,kBACC,IAAI,WAAA,oBAAe,GAAA,CAAC,OAAE,SAAA,EAAU,aAAA,EAAe,cAAI,WAAA,EAAY;AAAA,iBAAA,EAClE,CAAA;AAAA,gCACA,IAAA,CAAC,OAAA,EAAA,EAAM,SAAA,EAAU,WAAA,EACf,QAAA,EAAA;AAAA,kCAAA,GAAA;AAAA,oBAAC,OAAA;AAAA,oBAAA;AAAA,sBACC,EAAA;AAAA,sBACA,IAAA,EAAK,UAAA;AAAA,sBACL,OAAA;AAAA,sBACA,UAAU,GAAA,CAAI,QAAA;AAAA,sBACd,QAAA,EAAU,CAAC,CAAA,KAAM;AACf,wBAAA,IAAI,EAAE,MAAA,CAAO,OAAA,EAAS,GAAA,CAAI,KAAA,CAAM,IAAI,EAAE,CAAA;AAAA,6BACjC,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,EAAE,CAAA;AAAA,sBACtB;AAAA;AAAA,mBACF;AAAA,kCACA,GAAA,CAAC,UAAK,SAAA,EAAU,iBAAA,EACd,8BAAC,MAAA,EAAA,EAAK,SAAA,EAAU,mBAAkB,CAAA,EACpC;AAAA,iBAAA,EACF;AAAA,eAAA,EAAA,EAtBQ,IAAI,EAuBd,CAAA;AAAA,YAEJ,CAAC,CAAA,EACH,CAAA;AAAA,4BACA,IAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,eAAA,EACb,QAAA,EAAA;AAAA,8BAAA,GAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAU,yBAAA,EAA0B,OAAA,EAAS,MAAM,GAAA,CAAI,OAAA,EAAQ,EAClF,QAAA,EAAA,IAAA,CAAK,WAAA,EACR,CAAA;AAAA,8BACA,GAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAU,uBAAA,EAAwB,OAAA,EAAS,MAAM,GAAA,CAAI,IAAA,EAAK,EAC7E,QAAA,EAAA,IAAA,CAAK,SAAA,EACR;AAAA,aAAA,EACF;AAAA;AAAA;AAAA;AACF;AAAA,GACF;AAEJ;AAEA,SAAS,SAAA,CAAU,GAAkB,SAAA,EAAkC;AACrE,EAAA,IAAI,CAAC,SAAA,EAAW;AAChB,EAAA,MAAM,aAAa,SAAA,CAAU,gBAAA;AAAA,IAC3B;AAAA,GACF;AACA,EAAA,IAAI,UAAA,CAAW,WAAW,CAAA,EAAG;AAC7B,EAAA,MAAM,KAAA,GAAQ,WAAW,CAAC,CAAA;AAC1B,EAAA,MAAM,IAAA,GAAO,UAAA,CAAW,UAAA,CAAW,MAAA,GAAS,CAAC,CAAA;AAC7C,EAAA,IAAI,CAAC,KAAA,IAAS,CAAC,IAAA,EAAM;AACrB,EAAA,MAAM,SAAS,QAAA,CAAS,aAAA;AACxB,EAAA,IAAI,CAAA,CAAE,QAAA,IAAY,MAAA,KAAW,KAAA,EAAO;AAClC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,IAAA,CAAK,KAAA,EAAM;AAAA,EACb,CAAA,MAAA,IAAW,CAAC,CAAA,CAAE,QAAA,IAAY,WAAW,IAAA,EAAM;AACzC,IAAA,CAAA,CAAE,cAAA,EAAe;AACjB,IAAA,KAAA,CAAM,KAAA,EAAM;AAAA,EACd;AACF;AC7LO,SAAS,qBAAqB,KAAA,EAAkC;AACrE,EAAA,uBAAOA,GAAAA,CAAC,aAAA,EAAA,EAAe,QAAA,EAAA,CAAC,GAAA,qBAAQA,GAAAA,CAAC,WAAA,EAAA,EAAY,GAAA,EAAU,KAAA,EAAc,CAAA,EAAG,CAAA;AAC1E;AAEA,SAAS,WAAA,CAAY,EAAE,GAAA,EAAK,KAAA,EAAM,EAA0D;AAC1F,EAAA,MAAM,OAAmB,EAAE,GAAG,mBAAA,EAAqB,GAAG,MAAM,IAAA,EAAK;AACjE,EAAA,MAAM,QAAA,GAAW,MAAM,gBAAA,IAAoB,WAAA;AAE3C,EAAAC,UAAU,MAAM;AACd,IAAA,YAAA,EAAa;AAAA,EACf,CAAA,EAAG,EAAE,CAAA;AAEL,EAAA,MAAM,UAAA,GAAa,MAAM,KAAA,GAAQ,EAAE,iBAAiB,KAAA,CAAM,KAAA,KAAU,EAAC;AAErE,EAAA,uBACEC,IAAAA;AAAA,IAAC,KAAA;AAAA,IAAA;AAAA,MACC,SAAA,EAAU,mBAAA;AAAA,MACV,IAAA,EAAK,QAAA;AAAA,MACL,WAAA,EAAU,QAAA;AAAA,MACV,cAAY,IAAA,CAAK,KAAA;AAAA,MAChB,GAAG,UAAA;AAAA,MAEJ,QAAA,EAAA;AAAA,wBAAAF,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,iBAAA,EAAmB,eAAK,KAAA,EAAM,CAAA;AAAA,wBAC3CA,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,gBAAA,EAAkB,eAAK,WAAA,EAAY,CAAA;AAAA,wBAChDE,IAAAA,CAAC,KAAA,EAAA,EAAI,SAAA,EAAU,mBAAA,EACZ,QAAA,EAAA;AAAA,UAAA,KAAA,CAAM,SAAA,oBACLF,GAAAA,CAAC,GAAA,EAAA,EAAE,SAAA,EAAU,WAAU,IAAA,EAAM,KAAA,CAAM,SAAA,EAChC,QAAA,EAAA,IAAA,CAAK,eAAA,EACR,CAAA;AAAA,0BAEFA,GAAAA;AAAA,YAAC,QAAA;AAAA,YAAA;AAAA,cACC,IAAA,EAAK,QAAA;AAAA,cACL,SAAA,EAAU,yBAAA;AAAA,cACV,SAAS,MAAM;AACb,gBAAA,IAAI,GAAA,CAAI,SAAS,IAAA,CAAK,CAAC,MAAM,CAAA,CAAE,EAAA,KAAO,QAAQ,CAAA,EAAG;AAC/C,kBAAA,GAAA,CAAI,KAAK,QAAQ,CAAA;AAAA,gBACnB;AACA,gBAAA,GAAA,CAAI,IAAA,EAAK;AAAA,cACX,CAAA;AAAA,cAEC,QAAA,EAAA,IAAA,CAAK;AAAA;AAAA,WACR;AAAA,0BACAA,GAAAA,CAAC,QAAA,EAAA,EAAO,IAAA,EAAK,QAAA,EAAS,SAAA,EAAU,uBAAA,EAAwB,OAAA,EAAS,MAAM,GAAA,CAAI,IAAA,EAAK,EAC7E,eAAK,gBAAA,EACR;AAAA,SAAA,EACF;AAAA;AAAA;AAAA,GACF;AAEJ","file":"index.js","sourcesContent":["import { type ConsentApi, ConsentBanner } from '@tickboxhq/react'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { type BannerCopy, DEFAULT_BANNER_COPY } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentBannerDefaultProps = {\n /**\n * Override individual labels and copy strings. Anything you don't pass\n * falls back to the English defaults.\n */\n copy?: Partial<BannerCopy>\n /**\n * URL of the privacy policy linked from the banner. If omitted, the link\n * is hidden. The Tickbox config's `policy.url` is the natural source —\n * pass it here.\n */\n policyUrl?: string | undefined\n /**\n * Force light or dark theme. By default the banner follows\n * `prefers-color-scheme`.\n */\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled consent banner. Mounts itself only when the headless\n * `<ConsentBanner>` says it should be open. Click \"Customise\" to expand\n * a per-category modal.\n *\n * @example\n * ```tsx\n * import config from './consent.config'\n * <ConsentBannerDefault policyUrl={config.policy?.url} />\n * ```\n */\nexport function ConsentBannerDefault(props: ConsentBannerDefaultProps) {\n return <ConsentBanner>{(api) => <BannerInner api={api} props={props} />}</ConsentBanner>\n}\n\nfunction BannerInner({\n api,\n props,\n}: {\n api: ConsentApi\n props: ConsentBannerDefaultProps\n}) {\n const copy: BannerCopy = { ...DEFAULT_BANNER_COPY, ...props.copy }\n const [showModal, setShowModal] = useState(false)\n\n useEffect(() => {\n injectStyles()\n }, [])\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return (\n <>\n <div className=\"tb-root tb-banner\" role=\"region\" aria-label={copy.title} {...themeAttrs}>\n <div className=\"tb-banner-text\">\n <p className=\"tb-banner-title\">{copy.title}</p>\n <p className=\"tb-banner-desc\">{copy.description}</p>\n </div>\n <div className=\"tb-banner-actions\">\n {props.policyUrl && (\n <a className=\"tb-link\" href={props.policyUrl}>\n {copy.policyLinkLabel}\n </a>\n )}\n <button type=\"button\" className=\"tb-btn tb-btn-secondary\" onClick={() => api.denyAll()}>\n {copy.rejectLabel}\n </button>\n <button\n type=\"button\"\n className=\"tb-btn tb-btn-secondary\"\n onClick={() => setShowModal(true)}\n >\n {copy.customiseLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-primary\" onClick={() => api.grantAll()}>\n {copy.acceptLabel}\n </button>\n </div>\n </div>\n {showModal && (\n <CustomiseModal\n api={api}\n copy={copy}\n theme={props.theme}\n onClose={() => setShowModal(false)}\n />\n )}\n </>\n )\n}\n\nfunction CustomiseModal({\n api,\n copy,\n theme,\n onClose,\n}: {\n api: ConsentApi\n copy: BannerCopy\n theme?: 'light' | 'dark'\n onClose: () => void\n}) {\n const titleId = useId()\n const containerRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') onClose()\n if (e.key === 'Tab') trapFocus(e, containerRef.current)\n }\n document.addEventListener('keydown', onKey)\n const previouslyFocused = document.activeElement as HTMLElement | null\n const first = containerRef.current?.querySelector<HTMLElement>(\n 'button, [href], input, [tabindex]:not([tabindex=\"-1\"])',\n )\n first?.focus()\n return () => {\n document.removeEventListener('keydown', onKey)\n previouslyFocused?.focus?.()\n }\n }, [onClose])\n\n const themeAttrs = theme ? { 'data-tb-theme': theme } : {}\n\n return (\n <div\n className=\"tb-root tb-modal-backdrop\"\n onClick={onClose}\n onKeyDown={(e) => {\n if (e.key === 'Escape') onClose()\n }}\n role=\"presentation\"\n {...themeAttrs}\n >\n <div\n ref={containerRef}\n className=\"tb-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <div className=\"tb-modal-head\">\n <h2 id={titleId} className=\"tb-modal-title\">\n {copy.customiseLabel}\n </h2>\n <button\n type=\"button\"\n className=\"tb-btn tb-btn-ghost\"\n aria-label={copy.closeLabel}\n onClick={onClose}\n >\n ✕\n </button>\n </div>\n <div className=\"tb-modal-body\">\n {api.resolved.map((cat) => {\n const checked = api.decisions[cat.id] === true\n const id = `tb-cat-${cat.id}`\n return (\n <div key={cat.id} className=\"tb-cat\">\n <div className=\"tb-cat-text\">\n <p className=\"tb-cat-name\">\n <label htmlFor={id}>{cat.id}</label>\n {cat.required && <span className=\"tb-badge\">{copy.requiredBadge}</span>}\n </p>\n {cat.description && <p className=\"tb-cat-desc\">{cat.description}</p>}\n </div>\n <label className=\"tb-switch\">\n <input\n id={id}\n type=\"checkbox\"\n checked={checked}\n disabled={cat.required}\n onChange={(e) => {\n if (e.target.checked) api.grant(cat.id)\n else api.deny(cat.id)\n }}\n />\n <span className=\"tb-switch-track\">\n <span className=\"tb-switch-thumb\" />\n </span>\n </label>\n </div>\n )\n })}\n </div>\n <div className=\"tb-modal-foot\">\n <button type=\"button\" className=\"tb-btn tb-btn-secondary\" onClick={() => api.denyAll()}>\n {copy.rejectLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-primary\" onClick={() => api.save()}>\n {copy.saveLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nfunction trapFocus(e: KeyboardEvent, container: HTMLDivElement | null) {\n if (!container) return\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (focusables.length === 0) return\n const first = focusables[0]\n const last = focusables[focusables.length - 1]\n if (!first || !last) return\n const active = document.activeElement\n if (e.shiftKey && active === first) {\n e.preventDefault()\n last.focus()\n } else if (!e.shiftKey && active === last) {\n e.preventDefault()\n first.focus()\n }\n}\n","import { type ConsentApi, ConsentNotice } from '@tickboxhq/react'\nimport { useEffect } from 'react'\nimport { DEFAULT_NOTICE_COPY, type NoticeCopy } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentNoticeDefaultProps = {\n /**\n * Override individual labels and copy strings. Anything you don't pass\n * falls back to the English defaults.\n */\n copy?: Partial<NoticeCopy>\n /** Privacy-policy URL. If omitted, the link is hidden. */\n policyUrl?: string | undefined\n /**\n * Category ID to deny when the user clicks \"Opt out\". Defaults to\n * `'analytics'` since that's the most common notice-mode category.\n */\n optOutCategoryId?: string\n /** Force light or dark theme. */\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled notice card for sites that have only `notice`-mode\n * categories (typically UK DUAA-exempt analytics like Plausible or\n * GoatCounter). Bottom-right toast.\n *\n * @example\n * ```tsx\n * import config from './consent.config'\n * <ConsentNoticeDefault policyUrl={config.policy?.url} />\n * ```\n */\nexport function ConsentNoticeDefault(props: ConsentNoticeDefaultProps) {\n return <ConsentNotice>{(api) => <NoticeInner api={api} props={props} />}</ConsentNotice>\n}\n\nfunction NoticeInner({ api, props }: { api: ConsentApi; props: ConsentNoticeDefaultProps }) {\n const copy: NoticeCopy = { ...DEFAULT_NOTICE_COPY, ...props.copy }\n const optOutId = props.optOutCategoryId ?? 'analytics'\n\n useEffect(() => {\n injectStyles()\n }, [])\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return (\n <div\n className=\"tb-root tb-notice\"\n role=\"status\"\n aria-live=\"polite\"\n aria-label={copy.title}\n {...themeAttrs}\n >\n <p className=\"tb-notice-title\">{copy.title}</p>\n <p className=\"tb-notice-desc\">{copy.description}</p>\n <div className=\"tb-notice-actions\">\n {props.policyUrl && (\n <a className=\"tb-link\" href={props.policyUrl}>\n {copy.policyLinkLabel}\n </a>\n )}\n <button\n type=\"button\"\n className=\"tb-btn tb-btn-secondary\"\n onClick={() => {\n if (api.resolved.some((r) => r.id === optOutId)) {\n api.deny(optOutId)\n }\n api.save()\n }}\n >\n {copy.optOutLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-primary\" onClick={() => api.save()}>\n {copy.acknowledgeLabel}\n </button>\n </div>\n </div>\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/react/banner.tsx","../../src/react/notice.tsx"],"names":["jsx","useEffect","jsxs"],"mappings":"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","file":"index.js","sourcesContent":["import { type ConsentApi, ConsentBanner } from '@tickboxhq/react'\nimport { useEffect, useId, useRef, useState } from 'react'\nimport { type BannerCopy, DEFAULT_BANNER_COPY } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentBannerDefaultProps = {\n /**\n * Override individual labels and copy strings. Anything you don't pass\n * falls back to the English defaults.\n */\n copy?: Partial<BannerCopy>\n /**\n * URL of the privacy policy linked from the banner. If omitted, the link\n * is hidden. The Tickbox config's `policy.url` is the natural source —\n * pass it here.\n */\n policyUrl?: string | undefined\n /**\n * Force light or dark theme. By default the banner follows\n * `prefers-color-scheme`.\n */\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled consent banner. Mounts itself only when the headless\n * `<ConsentBanner>` says it should be open. Click \"Customise\" to expand\n * a per-category modal.\n *\n * @example\n * ```tsx\n * import config from './consent.config'\n * <ConsentBannerDefault policyUrl={config.policy?.url} />\n * ```\n */\nexport function ConsentBannerDefault(props: ConsentBannerDefaultProps) {\n return <ConsentBanner>{(api) => <BannerInner api={api} props={props} />}</ConsentBanner>\n}\n\nfunction BannerInner({\n api,\n props,\n}: {\n api: ConsentApi\n props: ConsentBannerDefaultProps\n}) {\n const copy: BannerCopy = { ...DEFAULT_BANNER_COPY, ...props.copy }\n const [showModal, setShowModal] = useState(false)\n\n useEffect(() => {\n injectStyles()\n }, [])\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return (\n <>\n <div className=\"tb-root tb-banner\" role=\"region\" aria-label={copy.title} {...themeAttrs}>\n <div className=\"tb-banner-text\">\n <p className=\"tb-banner-title\">{copy.title}</p>\n <p className=\"tb-banner-desc\">{copy.description}</p>\n </div>\n <div className=\"tb-banner-actions\">\n {props.policyUrl && (\n <a className=\"tb-link\" href={props.policyUrl}>\n {copy.policyLinkLabel}\n </a>\n )}\n <button type=\"button\" className=\"tb-btn tb-btn-equal\" onClick={() => api.denyAll()}>\n {copy.rejectLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-ghost\" onClick={() => setShowModal(true)}>\n {copy.customiseLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-equal\" onClick={() => api.grantAll()}>\n {copy.acceptLabel}\n </button>\n </div>\n </div>\n {showModal && (\n <CustomiseModal\n api={api}\n copy={copy}\n theme={props.theme}\n onClose={() => setShowModal(false)}\n />\n )}\n </>\n )\n}\n\nfunction CustomiseModal({\n api,\n copy,\n theme,\n onClose,\n}: {\n api: ConsentApi\n copy: BannerCopy\n theme?: 'light' | 'dark'\n onClose: () => void\n}) {\n const titleId = useId()\n const containerRef = useRef<HTMLDivElement | null>(null)\n\n useEffect(() => {\n function onKey(e: KeyboardEvent) {\n if (e.key === 'Escape') onClose()\n if (e.key === 'Tab') trapFocus(e, containerRef.current)\n }\n document.addEventListener('keydown', onKey)\n const previouslyFocused = document.activeElement as HTMLElement | null\n const first = containerRef.current?.querySelector<HTMLElement>(\n 'button, [href], input, [tabindex]:not([tabindex=\"-1\"])',\n )\n first?.focus()\n return () => {\n document.removeEventListener('keydown', onKey)\n previouslyFocused?.focus?.()\n }\n }, [onClose])\n\n const themeAttrs = theme ? { 'data-tb-theme': theme } : {}\n\n return (\n <div\n className=\"tb-root tb-modal-backdrop\"\n onClick={onClose}\n onKeyDown={(e) => {\n if (e.key === 'Escape') onClose()\n }}\n role=\"presentation\"\n {...themeAttrs}\n >\n <div\n ref={containerRef}\n className=\"tb-modal\"\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n onClick={(e) => e.stopPropagation()}\n onKeyDown={(e) => e.stopPropagation()}\n >\n <div className=\"tb-modal-head\">\n <h2 id={titleId} className=\"tb-modal-title\">\n {copy.customiseLabel}\n </h2>\n <button\n type=\"button\"\n className=\"tb-btn tb-btn-ghost\"\n aria-label={copy.closeLabel}\n onClick={onClose}\n >\n ✕\n </button>\n </div>\n <div className=\"tb-modal-body\">\n {api.resolved.map((cat) => {\n const checked = api.decisions[cat.id] === true\n const id = `tb-cat-${cat.id}`\n return (\n <div key={cat.id} className=\"tb-cat\">\n <div className=\"tb-cat-text\">\n <p className=\"tb-cat-name\">\n <label htmlFor={id}>{cat.id}</label>\n {cat.required && <span className=\"tb-badge\">{copy.requiredBadge}</span>}\n </p>\n {cat.description && <p className=\"tb-cat-desc\">{cat.description}</p>}\n </div>\n <label className=\"tb-switch\">\n <input\n id={id}\n type=\"checkbox\"\n checked={checked}\n disabled={cat.required}\n onChange={(e) => {\n if (e.target.checked) api.grant(cat.id)\n else api.deny(cat.id)\n }}\n />\n <span className=\"tb-switch-track\">\n <span className=\"tb-switch-thumb\" />\n </span>\n </label>\n </div>\n )\n })}\n </div>\n <div className=\"tb-modal-foot\">\n <button type=\"button\" className=\"tb-btn tb-btn-equal\" onClick={() => api.denyAll()}>\n {copy.rejectLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-primary\" onClick={() => api.save()}>\n {copy.saveLabel}\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nfunction trapFocus(e: KeyboardEvent, container: HTMLDivElement | null) {\n if (!container) return\n const focusables = container.querySelectorAll<HTMLElement>(\n 'button:not([disabled]), [href], input:not([disabled]), [tabindex]:not([tabindex=\"-1\"])',\n )\n if (focusables.length === 0) return\n const first = focusables[0]\n const last = focusables[focusables.length - 1]\n if (!first || !last) return\n const active = document.activeElement\n if (e.shiftKey && active === first) {\n e.preventDefault()\n last.focus()\n } else if (!e.shiftKey && active === last) {\n e.preventDefault()\n first.focus()\n }\n}\n","import { type ConsentApi, ConsentNotice } from '@tickboxhq/react'\nimport { useEffect } from 'react'\nimport { DEFAULT_NOTICE_COPY, type NoticeCopy } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentNoticeDefaultProps = {\n /**\n * Override individual labels and copy strings. Anything you don't pass\n * falls back to the English defaults.\n */\n copy?: Partial<NoticeCopy>\n /** Privacy-policy URL. If omitted, the link is hidden. */\n policyUrl?: string | undefined\n /**\n * Category ID to deny when the user clicks \"Opt out\". Defaults to\n * `'analytics'` since that's the most common notice-mode category.\n */\n optOutCategoryId?: string\n /** Force light or dark theme. */\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled notice card for sites that have only `notice`-mode\n * categories (typically UK DUAA-exempt analytics like Plausible or\n * GoatCounter). Bottom-right toast.\n *\n * @example\n * ```tsx\n * import config from './consent.config'\n * <ConsentNoticeDefault policyUrl={config.policy?.url} />\n * ```\n */\nexport function ConsentNoticeDefault(props: ConsentNoticeDefaultProps) {\n return <ConsentNotice>{(api) => <NoticeInner api={api} props={props} />}</ConsentNotice>\n}\n\nfunction NoticeInner({ api, props }: { api: ConsentApi; props: ConsentNoticeDefaultProps }) {\n const copy: NoticeCopy = { ...DEFAULT_NOTICE_COPY, ...props.copy }\n const optOutId = props.optOutCategoryId ?? 'analytics'\n\n useEffect(() => {\n injectStyles()\n }, [])\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return (\n <div\n className=\"tb-root tb-notice\"\n role=\"status\"\n aria-live=\"polite\"\n aria-label={copy.title}\n {...themeAttrs}\n >\n <p className=\"tb-notice-title\">{copy.title}</p>\n <p className=\"tb-notice-desc\">{copy.description}</p>\n <div className=\"tb-notice-actions\">\n {props.policyUrl && (\n <a className=\"tb-link\" href={props.policyUrl}>\n {copy.policyLinkLabel}\n </a>\n )}\n <button\n type=\"button\"\n className=\"tb-btn tb-btn-secondary\"\n onClick={() => {\n if (api.resolved.some((r) => r.id === optOutId)) {\n api.deny(optOutId)\n }\n api.save()\n }}\n >\n {copy.optOutLabel}\n </button>\n <button type=\"button\" className=\"tb-btn tb-btn-primary\" onClick={() => api.save()}>\n {copy.acknowledgeLabel}\n </button>\n </div>\n </div>\n )\n}\n"]}
|
package/dist/vue/index.d.ts
CHANGED
|
@@ -12,6 +12,11 @@ type ConsentBannerDefaultProps = {
|
|
|
12
12
|
* `<ConsentBanner>` says it should be open. "Customise" opens a modal
|
|
13
13
|
* with per-category toggles.
|
|
14
14
|
*
|
|
15
|
+
* Equal-prominence design: Accept All and Reject All use identical button
|
|
16
|
+
* styling. UK ICO and EU EDPB guidance treats unequal visual weight on
|
|
17
|
+
* those buttons as a dark pattern. Customise is rendered as a ghost button
|
|
18
|
+
* so the two consent paths stay symmetrical.
|
|
19
|
+
*
|
|
15
20
|
* @example
|
|
16
21
|
* ```vue
|
|
17
22
|
* <script setup lang="ts">
|
package/dist/vue/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { injectStyles, DEFAULT_BANNER_COPY, DEFAULT_NOTICE_COPY } from '../chunk-
|
|
1
|
+
import { injectStyles, DEFAULT_BANNER_COPY, DEFAULT_NOTICE_COPY } from '../chunk-DGDSIXCT.js';
|
|
2
2
|
import { ConsentBanner, ConsentNotice } from '@tickboxhq/vue';
|
|
3
|
-
import { defineComponent, onMounted,
|
|
3
|
+
import { defineComponent, onMounted, h, ref } from 'vue';
|
|
4
4
|
|
|
5
5
|
var ConsentBannerDefault = defineComponent({
|
|
6
6
|
name: "ConsentBannerDefault",
|
|
@@ -11,68 +11,84 @@ var ConsentBannerDefault = defineComponent({
|
|
|
11
11
|
},
|
|
12
12
|
setup(props) {
|
|
13
13
|
onMounted(() => injectStyles());
|
|
14
|
-
const showModal = ref(false);
|
|
15
14
|
return () => h(ConsentBanner, null, {
|
|
16
|
-
default: (api) =>
|
|
15
|
+
default: (api) => h(BannerInner, {
|
|
16
|
+
api,
|
|
17
|
+
userCopy: props.copy ?? {},
|
|
18
|
+
policyUrl: props.policyUrl,
|
|
19
|
+
theme: props.theme
|
|
20
|
+
})
|
|
17
21
|
});
|
|
18
22
|
}
|
|
19
23
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
24
|
+
var BannerInner = defineComponent({
|
|
25
|
+
name: "ConsentBannerDefaultInner",
|
|
26
|
+
props: {
|
|
27
|
+
api: { type: Object, required: true },
|
|
28
|
+
userCopy: { type: Object, required: true },
|
|
29
|
+
policyUrl: { type: String, default: void 0 },
|
|
30
|
+
theme: { type: String, default: void 0 }
|
|
31
|
+
},
|
|
32
|
+
setup(props) {
|
|
33
|
+
const showModal = ref(false);
|
|
34
|
+
return () => {
|
|
35
|
+
const copy = { ...DEFAULT_BANNER_COPY, ...props.userCopy };
|
|
36
|
+
const themeAttrs = props.theme ? { "data-tb-theme": props.theme } : {};
|
|
37
|
+
return h("div", null, [
|
|
38
|
+
h(
|
|
39
|
+
"div",
|
|
40
|
+
{
|
|
41
|
+
class: "tb-root tb-banner",
|
|
42
|
+
role: "region",
|
|
43
|
+
"aria-label": copy.title,
|
|
44
|
+
...themeAttrs
|
|
45
|
+
},
|
|
46
|
+
[
|
|
47
|
+
h("div", { class: "tb-banner-text" }, [
|
|
48
|
+
h("p", { class: "tb-banner-title" }, copy.title),
|
|
49
|
+
h("p", { class: "tb-banner-desc" }, copy.description)
|
|
50
|
+
]),
|
|
51
|
+
h("div", { class: "tb-banner-actions" }, [
|
|
52
|
+
props.policyUrl ? h("a", { class: "tb-link", href: props.policyUrl }, copy.policyLinkLabel) : null,
|
|
53
|
+
h(
|
|
54
|
+
"button",
|
|
55
|
+
{
|
|
56
|
+
type: "button",
|
|
57
|
+
class: "tb-btn tb-btn-equal",
|
|
58
|
+
onClick: () => props.api.denyAll()
|
|
59
|
+
},
|
|
60
|
+
copy.rejectLabel
|
|
61
|
+
),
|
|
62
|
+
h(
|
|
63
|
+
"button",
|
|
64
|
+
{
|
|
65
|
+
type: "button",
|
|
66
|
+
class: "tb-btn tb-btn-ghost",
|
|
67
|
+
onClick: () => {
|
|
68
|
+
showModal.value = true;
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
copy.customiseLabel
|
|
72
|
+
),
|
|
73
|
+
h(
|
|
74
|
+
"button",
|
|
75
|
+
{
|
|
76
|
+
type: "button",
|
|
77
|
+
class: "tb-btn tb-btn-equal",
|
|
78
|
+
onClick: () => props.api.grantAll()
|
|
79
|
+
},
|
|
80
|
+
copy.acceptLabel
|
|
81
|
+
)
|
|
82
|
+
])
|
|
83
|
+
]
|
|
84
|
+
),
|
|
85
|
+
showModal.value ? renderModal(props.api, copy, props.theme, () => {
|
|
86
|
+
showModal.value = false;
|
|
87
|
+
}) : null
|
|
88
|
+
]);
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
});
|
|
76
92
|
function renderModal(api, copy, theme, onClose) {
|
|
77
93
|
const themeAttrs = theme ? { "data-tb-theme": theme } : {};
|
|
78
94
|
return h(
|
|
@@ -114,8 +130,8 @@ function renderModal(api, copy, theme, onClose) {
|
|
|
114
130
|
h(
|
|
115
131
|
"div",
|
|
116
132
|
{ class: "tb-modal-body" },
|
|
117
|
-
api.resolved.
|
|
118
|
-
const checked = api.decisions
|
|
133
|
+
api.resolved.map((cat) => {
|
|
134
|
+
const checked = api.decisions[cat.id] === true;
|
|
119
135
|
const id = `tb-cat-${cat.id}`;
|
|
120
136
|
return h("div", { key: cat.id, class: "tb-cat" }, [
|
|
121
137
|
h("div", { class: "tb-cat-text" }, [
|
|
@@ -149,7 +165,7 @@ function renderModal(api, copy, theme, onClose) {
|
|
|
149
165
|
"button",
|
|
150
166
|
{
|
|
151
167
|
type: "button",
|
|
152
|
-
class: "tb-btn tb-btn-
|
|
168
|
+
class: "tb-btn tb-btn-equal",
|
|
153
169
|
onClick: () => api.denyAll()
|
|
154
170
|
},
|
|
155
171
|
copy.rejectLabel
|
|
@@ -208,7 +224,7 @@ function renderNotice(api, props) {
|
|
|
208
224
|
type: "button",
|
|
209
225
|
class: "tb-btn tb-btn-secondary",
|
|
210
226
|
onClick: () => {
|
|
211
|
-
if (api.resolved.
|
|
227
|
+
if (api.resolved.some((r) => r.id === optOutId)) {
|
|
212
228
|
api.deny(optOutId);
|
|
213
229
|
}
|
|
214
230
|
api.save();
|
package/dist/vue/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vue/banner.ts","../../src/vue/notice.ts"],"names":["defineComponent","onMounted","h"],"mappings":"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","file":"index.js","sourcesContent":["import type { ConsentApi } from '@tickboxhq/vue'\nimport { ConsentBanner } from '@tickboxhq/vue'\nimport { type PropType, defineComponent, h, onMounted, ref } from 'vue'\nimport { type BannerCopy, DEFAULT_BANNER_COPY } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentBannerDefaultProps = {\n copy?: Partial<BannerCopy>\n policyUrl?: string\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled consent banner for Vue. Mounts only when the headless\n * `<ConsentBanner>` says it should be open. \"Customise\" opens a modal\n * with per-category toggles.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { ConsentBannerDefault } from '@tickboxhq/banner-default/vue'\n * import config from './consent.config'\n * </script>\n * <template>\n * <ConsentBannerDefault :policy-url=\"config.policy?.url\" />\n * </template>\n * ```\n */\nexport const ConsentBannerDefault = defineComponent({\n name: 'ConsentBannerDefault',\n props: {\n copy: { type: Object as PropType<Partial<BannerCopy>>, default: () => ({}) },\n policyUrl: { type: String as PropType<string | undefined>, default: undefined },\n theme: { type: String as PropType<'light' | 'dark' | undefined>, default: undefined },\n },\n setup(props) {\n onMounted(() => injectStyles())\n const showModal = ref(false)\n return () =>\n h(ConsentBanner, null, {\n default: (api: unknown) => renderBanner(api as ConsentApi, props, showModal),\n })\n },\n})\n\nfunction renderBanner(\n api: ConsentApi,\n props: ConsentBannerDefaultProps,\n showModal: ReturnType<typeof ref<boolean>>,\n) {\n const copy: BannerCopy = { ...DEFAULT_BANNER_COPY, ...(props.copy ?? {}) }\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return h('div', null, [\n h(\n 'div',\n {\n class: 'tb-root tb-banner',\n role: 'region',\n 'aria-label': copy.title,\n ...themeAttrs,\n },\n [\n h('div', { class: 'tb-banner-text' }, [\n h('p', { class: 'tb-banner-title' }, copy.title),\n h('p', { class: 'tb-banner-desc' }, copy.description),\n ]),\n h('div', { class: 'tb-banner-actions' }, [\n props.policyUrl\n ? h('a', { class: 'tb-link', href: props.policyUrl }, copy.policyLinkLabel)\n : null,\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-secondary',\n onClick: () => api.denyAll(),\n },\n copy.rejectLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-secondary',\n onClick: () => {\n showModal.value = true\n },\n },\n copy.customiseLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-primary',\n onClick: () => api.grantAll(),\n },\n copy.acceptLabel,\n ),\n ]),\n ],\n ),\n showModal.value\n ? renderModal(api, copy, props.theme, () => {\n showModal.value = false\n })\n : null,\n ])\n}\n\nfunction renderModal(\n api: ConsentApi,\n copy: BannerCopy,\n theme: 'light' | 'dark' | undefined,\n onClose: () => void,\n) {\n const themeAttrs = theme ? { 'data-tb-theme': theme } : {}\n\n return h(\n 'div',\n {\n class: 'tb-root tb-modal-backdrop',\n role: 'presentation',\n onClick: onClose,\n onKeydown: (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose()\n },\n ...themeAttrs,\n },\n [\n h(\n 'div',\n {\n class: 'tb-modal',\n role: 'dialog',\n 'aria-modal': 'true',\n 'aria-label': copy.customiseLabel,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n onKeydown: (e: KeyboardEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'tb-modal-head' }, [\n h('h2', { class: 'tb-modal-title' }, copy.customiseLabel),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-ghost',\n 'aria-label': copy.closeLabel,\n onClick: onClose,\n },\n '✕',\n ),\n ]),\n h(\n 'div',\n { class: 'tb-modal-body' },\n api.resolved.value.map((cat) => {\n const checked = api.decisions.value[cat.id] === true\n const id = `tb-cat-${cat.id}`\n return h('div', { key: cat.id, class: 'tb-cat' }, [\n h('div', { class: 'tb-cat-text' }, [\n h('p', { class: 'tb-cat-name' }, [\n h('label', { for: id }, cat.id),\n cat.required ? h('span', { class: 'tb-badge' }, copy.requiredBadge) : null,\n ]),\n cat.description ? h('p', { class: 'tb-cat-desc' }, cat.description) : null,\n ]),\n h('label', { class: 'tb-switch' }, [\n h('input', {\n id,\n type: 'checkbox',\n checked,\n disabled: cat.required,\n onChange: (e: Event) => {\n const next = (e.target as HTMLInputElement).checked\n if (next) api.grant(cat.id)\n else api.deny(cat.id)\n },\n }),\n h('span', { class: 'tb-switch-track' }, [\n h('span', { class: 'tb-switch-thumb' }),\n ]),\n ]),\n ])\n }),\n ),\n h('div', { class: 'tb-modal-foot' }, [\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-secondary',\n onClick: () => api.denyAll(),\n },\n copy.rejectLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-primary',\n onClick: () => api.save(),\n },\n copy.saveLabel,\n ),\n ]),\n ],\n ),\n ],\n )\n}\n","import type { ConsentApi } from '@tickboxhq/vue'\nimport { ConsentNotice } from '@tickboxhq/vue'\nimport { type PropType, defineComponent, h, onMounted } from 'vue'\nimport { DEFAULT_NOTICE_COPY, type NoticeCopy } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentNoticeDefaultProps = {\n copy?: Partial<NoticeCopy>\n policyUrl?: string\n optOutCategoryId?: string\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled notice card for sites with only `notice`-mode categories\n * (typically UK DUAA-exempt analytics). Bottom-right toast with\n * \"Got it\" / \"Opt out\" actions.\n */\nexport const ConsentNoticeDefault = defineComponent({\n name: 'ConsentNoticeDefault',\n props: {\n copy: { type: Object as PropType<Partial<NoticeCopy>>, default: () => ({}) },\n policyUrl: { type: String as PropType<string | undefined>, default: undefined },\n optOutCategoryId: { type: String, default: 'analytics' },\n theme: { type: String as PropType<'light' | 'dark' | undefined>, default: undefined },\n },\n setup(props) {\n onMounted(() => injectStyles())\n return () =>\n h(ConsentNotice, null, {\n default: (api: unknown) => renderNotice(api as ConsentApi, props),\n })\n },\n})\n\nfunction renderNotice(api: ConsentApi, props: ConsentNoticeDefaultProps) {\n const copy: NoticeCopy = { ...DEFAULT_NOTICE_COPY, ...(props.copy ?? {}) }\n const optOutId = props.optOutCategoryId ?? 'analytics'\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return h(\n 'div',\n {\n class: 'tb-root tb-notice',\n role: 'status',\n 'aria-live': 'polite',\n 'aria-label': copy.title,\n ...themeAttrs,\n },\n [\n h('p', { class: 'tb-notice-title' }, copy.title),\n h('p', { class: 'tb-notice-desc' }, copy.description),\n h('div', { class: 'tb-notice-actions' }, [\n props.policyUrl\n ? h('a', { class: 'tb-link', href: props.policyUrl }, copy.policyLinkLabel)\n : null,\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-secondary',\n onClick: () => {\n if (api.resolved.value.some((r) => r.id === optOutId)) {\n api.deny(optOutId)\n }\n api.save()\n },\n },\n copy.optOutLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-primary',\n onClick: () => api.save(),\n },\n copy.acknowledgeLabel,\n ),\n ]),\n ],\n )\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vue/banner.ts","../../src/vue/notice.ts"],"names":["defineComponent","onMounted","h"],"mappings":"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","file":"index.js","sourcesContent":["import type { ConsentSlotApi } from '@tickboxhq/vue'\nimport { ConsentBanner } from '@tickboxhq/vue'\nimport { type PropType, defineComponent, h, onMounted, ref } from 'vue'\nimport { type BannerCopy, DEFAULT_BANNER_COPY } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentBannerDefaultProps = {\n copy?: Partial<BannerCopy>\n policyUrl?: string\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled consent banner for Vue. Mounts only when the headless\n * `<ConsentBanner>` says it should be open. \"Customise\" opens a modal\n * with per-category toggles.\n *\n * Equal-prominence design: Accept All and Reject All use identical button\n * styling. UK ICO and EU EDPB guidance treats unequal visual weight on\n * those buttons as a dark pattern. Customise is rendered as a ghost button\n * so the two consent paths stay symmetrical.\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { ConsentBannerDefault } from '@tickboxhq/banner-default/vue'\n * import config from './consent.config'\n * </script>\n * <template>\n * <ConsentBannerDefault :policy-url=\"config.policy?.url\" />\n * </template>\n * ```\n */\nexport const ConsentBannerDefault = defineComponent({\n name: 'ConsentBannerDefault',\n props: {\n copy: { type: Object as PropType<Partial<BannerCopy>>, default: () => ({}) },\n policyUrl: { type: String as PropType<string | undefined>, default: undefined },\n theme: { type: String as PropType<'light' | 'dark' | undefined>, default: undefined },\n },\n setup(props) {\n onMounted(() => injectStyles())\n return () =>\n h(ConsentBanner, null, {\n default: (api: unknown) =>\n h(BannerInner, {\n api: api as ConsentSlotApi,\n userCopy: props.copy ?? {},\n policyUrl: props.policyUrl,\n theme: props.theme,\n }),\n })\n },\n})\n\n/**\n * Inner component that owns `showModal` state. Keeping it in the same\n * scope as the render guarantees Vue's reactivity wires up correctly\n * (slot-only render functions don't track refs from outer scopes).\n */\nconst BannerInner = defineComponent({\n name: 'ConsentBannerDefaultInner',\n props: {\n api: { type: Object as PropType<ConsentSlotApi>, required: true },\n userCopy: { type: Object as PropType<Partial<BannerCopy>>, required: true },\n policyUrl: { type: String as PropType<string | undefined>, default: undefined },\n theme: { type: String as PropType<'light' | 'dark' | undefined>, default: undefined },\n },\n setup(props) {\n const showModal = ref(false)\n\n return () => {\n const copy: BannerCopy = { ...DEFAULT_BANNER_COPY, ...props.userCopy }\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return h('div', null, [\n h(\n 'div',\n {\n class: 'tb-root tb-banner',\n role: 'region',\n 'aria-label': copy.title,\n ...themeAttrs,\n },\n [\n h('div', { class: 'tb-banner-text' }, [\n h('p', { class: 'tb-banner-title' }, copy.title),\n h('p', { class: 'tb-banner-desc' }, copy.description),\n ]),\n h('div', { class: 'tb-banner-actions' }, [\n props.policyUrl\n ? h('a', { class: 'tb-link', href: props.policyUrl }, copy.policyLinkLabel)\n : null,\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-equal',\n onClick: () => props.api.denyAll(),\n },\n copy.rejectLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-ghost',\n onClick: () => {\n showModal.value = true\n },\n },\n copy.customiseLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-equal',\n onClick: () => props.api.grantAll(),\n },\n copy.acceptLabel,\n ),\n ]),\n ],\n ),\n showModal.value\n ? renderModal(props.api, copy, props.theme, () => {\n showModal.value = false\n })\n : null,\n ])\n }\n },\n})\n\nfunction renderModal(\n api: ConsentSlotApi,\n copy: BannerCopy,\n theme: 'light' | 'dark' | undefined,\n onClose: () => void,\n) {\n const themeAttrs = theme ? { 'data-tb-theme': theme } : {}\n\n return h(\n 'div',\n {\n class: 'tb-root tb-modal-backdrop',\n role: 'presentation',\n onClick: onClose,\n onKeydown: (e: KeyboardEvent) => {\n if (e.key === 'Escape') onClose()\n },\n ...themeAttrs,\n },\n [\n h(\n 'div',\n {\n class: 'tb-modal',\n role: 'dialog',\n 'aria-modal': 'true',\n 'aria-label': copy.customiseLabel,\n onClick: (e: MouseEvent) => e.stopPropagation(),\n onKeydown: (e: KeyboardEvent) => e.stopPropagation(),\n },\n [\n h('div', { class: 'tb-modal-head' }, [\n h('h2', { class: 'tb-modal-title' }, copy.customiseLabel),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-ghost',\n 'aria-label': copy.closeLabel,\n onClick: onClose,\n },\n '✕',\n ),\n ]),\n h(\n 'div',\n { class: 'tb-modal-body' },\n api.resolved.map((cat) => {\n const checked = api.decisions[cat.id] === true\n const id = `tb-cat-${cat.id}`\n return h('div', { key: cat.id, class: 'tb-cat' }, [\n h('div', { class: 'tb-cat-text' }, [\n h('p', { class: 'tb-cat-name' }, [\n h('label', { for: id }, cat.id),\n cat.required ? h('span', { class: 'tb-badge' }, copy.requiredBadge) : null,\n ]),\n cat.description ? h('p', { class: 'tb-cat-desc' }, cat.description) : null,\n ]),\n h('label', { class: 'tb-switch' }, [\n h('input', {\n id,\n type: 'checkbox',\n checked,\n disabled: cat.required,\n onChange: (e: Event) => {\n const next = (e.target as HTMLInputElement).checked\n if (next) api.grant(cat.id)\n else api.deny(cat.id)\n },\n }),\n h('span', { class: 'tb-switch-track' }, [\n h('span', { class: 'tb-switch-thumb' }),\n ]),\n ]),\n ])\n }),\n ),\n h('div', { class: 'tb-modal-foot' }, [\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-equal',\n onClick: () => api.denyAll(),\n },\n copy.rejectLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-primary',\n onClick: () => api.save(),\n },\n copy.saveLabel,\n ),\n ]),\n ],\n ),\n ],\n )\n}\n","import type { ConsentSlotApi } from '@tickboxhq/vue'\nimport { ConsentNotice } from '@tickboxhq/vue'\nimport { type PropType, defineComponent, h, onMounted } from 'vue'\nimport { DEFAULT_NOTICE_COPY, type NoticeCopy } from '../shared/copy.js'\nimport { injectStyles } from '../shared/styles.js'\n\nexport type ConsentNoticeDefaultProps = {\n copy?: Partial<NoticeCopy>\n policyUrl?: string\n optOutCategoryId?: string\n theme?: 'light' | 'dark'\n}\n\n/**\n * Drop-in styled notice card for sites with only `notice`-mode categories\n * (typically UK DUAA-exempt analytics). Bottom-right toast with\n * \"Got it\" / \"Opt out\" actions.\n */\nexport const ConsentNoticeDefault = defineComponent({\n name: 'ConsentNoticeDefault',\n props: {\n copy: { type: Object as PropType<Partial<NoticeCopy>>, default: () => ({}) },\n policyUrl: { type: String as PropType<string | undefined>, default: undefined },\n optOutCategoryId: { type: String, default: 'analytics' },\n theme: { type: String as PropType<'light' | 'dark' | undefined>, default: undefined },\n },\n setup(props) {\n onMounted(() => injectStyles())\n return () =>\n h(ConsentNotice, null, {\n default: (api: unknown) => renderNotice(api as ConsentSlotApi, props),\n })\n },\n})\n\nfunction renderNotice(api: ConsentSlotApi, props: ConsentNoticeDefaultProps) {\n const copy: NoticeCopy = { ...DEFAULT_NOTICE_COPY, ...(props.copy ?? {}) }\n const optOutId = props.optOutCategoryId ?? 'analytics'\n\n const themeAttrs = props.theme ? { 'data-tb-theme': props.theme } : {}\n\n return h(\n 'div',\n {\n class: 'tb-root tb-notice',\n role: 'status',\n 'aria-live': 'polite',\n 'aria-label': copy.title,\n ...themeAttrs,\n },\n [\n h('p', { class: 'tb-notice-title' }, copy.title),\n h('p', { class: 'tb-notice-desc' }, copy.description),\n h('div', { class: 'tb-notice-actions' }, [\n props.policyUrl\n ? h('a', { class: 'tb-link', href: props.policyUrl }, copy.policyLinkLabel)\n : null,\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-secondary',\n onClick: () => {\n if (api.resolved.some((r) => r.id === optOutId)) {\n api.deny(optOutId)\n }\n api.save()\n },\n },\n copy.optOutLabel,\n ),\n h(\n 'button',\n {\n type: 'button',\n class: 'tb-btn tb-btn-primary',\n onClick: () => api.save(),\n },\n copy.acknowledgeLabel,\n ),\n ]),\n ],\n )\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tickboxhq/banner-default",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.13",
|
|
4
4
|
"description": "Drop-in styled consent banner and notice components for Tickbox",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://tickbox.dev",
|
|
@@ -29,13 +29,13 @@
|
|
|
29
29
|
"access": "public"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@tickboxhq/core": "0.0.
|
|
32
|
+
"@tickboxhq/core": "0.0.13"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "^18.0.0 || ^19.0.0",
|
|
36
36
|
"vue": "^3.4.0",
|
|
37
|
-
"@tickboxhq/
|
|
38
|
-
"@tickboxhq/
|
|
37
|
+
"@tickboxhq/vue": "0.0.13",
|
|
38
|
+
"@tickboxhq/react": "0.0.13"
|
|
39
39
|
},
|
|
40
40
|
"peerDependenciesMeta": {
|
|
41
41
|
"@tickboxhq/react": {
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"typescript": "^5.7.2",
|
|
62
62
|
"vitest": "^2.1.8",
|
|
63
63
|
"vue": "^3.5.13",
|
|
64
|
-
"@tickboxhq/react": "0.0.
|
|
65
|
-
"@tickboxhq/vue": "0.0.
|
|
64
|
+
"@tickboxhq/react": "0.0.13",
|
|
65
|
+
"@tickboxhq/vue": "0.0.13"
|
|
66
66
|
},
|
|
67
67
|
"scripts": {
|
|
68
68
|
"build": "tsup",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/shared/copy.ts","../src/shared/styles.ts"],"names":[],"mappings":"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","file":"chunk-WJSFVAK4.js","sourcesContent":["export type BannerCopy = {\n title: string\n description: string\n acceptLabel: string\n rejectLabel: string\n customiseLabel: string\n saveLabel: string\n closeLabel: string\n policyLinkLabel: string\n requiredBadge: string\n}\n\nexport type NoticeCopy = {\n title: string\n description: string\n acknowledgeLabel: string\n optOutLabel: string\n policyLinkLabel: string\n}\n\nexport const DEFAULT_BANNER_COPY: BannerCopy = {\n title: 'Cookies and tracking',\n description:\n 'We use cookies to make this site work and, with your consent, to measure usage. You can choose what to allow.',\n acceptLabel: 'Accept all',\n rejectLabel: 'Reject all',\n customiseLabel: 'Customise',\n saveLabel: 'Save preferences',\n closeLabel: 'Close',\n policyLinkLabel: 'Privacy policy',\n requiredBadge: 'Required',\n}\n\nexport const DEFAULT_NOTICE_COPY: NoticeCopy = {\n title: 'A note about analytics',\n description:\n 'We use privacy-friendly analytics to understand how this site is used. No personal data is collected and no advertising profiles are built.',\n acknowledgeLabel: 'Got it',\n optOutLabel: 'Opt out',\n policyLinkLabel: 'Privacy policy',\n}\n","/**\n * Inline CSS for the default banner / notice / modal components.\n *\n * Uses CSS custom properties so users can re-theme without forking. Light\n * and dark themes are wired through `prefers-color-scheme` and the\n * `[data-tb-theme]` attribute.\n *\n * Visual style: GitHub-ish — system font, 6px corners, subtle border + soft\n * shadow, equal-prominence accept/reject buttons.\n */\nexport const TICKBOX_STYLES = `\n:where(.tb-root) {\n --tb-bg: #ffffff;\n --tb-fg: #1f2328;\n --tb-fg-muted: #59636e;\n --tb-border: #d1d9e0;\n --tb-shadow: 0 8px 24px rgba(140, 149, 159, 0.2);\n --tb-primary-bg: #1f2328;\n --tb-primary-fg: #ffffff;\n --tb-primary-bg-hover: #000000;\n --tb-secondary-bg: #ffffff;\n --tb-secondary-fg: #1f2328;\n --tb-secondary-bg-hover: #f6f8fa;\n --tb-link: #0969da;\n --tb-radius: 6px;\n --tb-z: 2147483000;\n font-family:\n -apple-system, BlinkMacSystemFont, \"Segoe UI\", \"Noto Sans\", Helvetica,\n Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\";\n color: var(--tb-fg);\n font-size: 14px;\n line-height: 1.5;\n -webkit-font-smoothing: antialiased;\n}\n@media (prefers-color-scheme: dark) {\n :where(.tb-root:not([data-tb-theme=\"light\"])) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n }\n}\n:where(.tb-root[data-tb-theme=\"dark\"]) {\n --tb-bg: #0d1117;\n --tb-fg: #f0f6fc;\n --tb-fg-muted: #9198a1;\n --tb-border: #30363d;\n --tb-shadow: 0 8px 24px rgba(1, 4, 9, 0.85);\n --tb-primary-bg: #f0f6fc;\n --tb-primary-fg: #0d1117;\n --tb-primary-bg-hover: #ffffff;\n --tb-secondary-bg: #15191f;\n --tb-secondary-fg: #f0f6fc;\n --tb-secondary-bg-hover: #1f2328;\n --tb-link: #4493f8;\n}\n\n.tb-banner {\n position: fixed;\n left: 16px;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 16px 20px;\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-banner-text {\n flex: 1 1 320px;\n min-width: 0;\n}\n.tb-banner-title {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 14px;\n}\n.tb-banner-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n}\n.tb-banner-actions {\n display: flex;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.tb-notice {\n position: fixed;\n right: 16px;\n bottom: 16px;\n z-index: var(--tb-z);\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n padding: 14px 16px;\n max-width: 360px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-notice-title {\n font-weight: 600;\n margin: 0 0 4px;\n font-size: 14px;\n}\n.tb-notice-desc {\n margin: 0 0 10px;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-notice-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-link {\n color: var(--tb-link);\n text-decoration: none;\n font-size: 13px;\n margin-right: auto;\n}\n.tb-link:hover { text-decoration: underline; }\n\n.tb-btn {\n appearance: none;\n border: 1px solid transparent;\n border-radius: var(--tb-radius);\n padding: 6px 14px;\n font-size: 13px;\n font-weight: 500;\n font-family: inherit;\n cursor: pointer;\n line-height: 1.5;\n transition: background-color 80ms ease;\n white-space: nowrap;\n}\n.tb-btn:focus-visible {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n.tb-btn-primary {\n background: var(--tb-primary-bg);\n color: var(--tb-primary-fg);\n}\n.tb-btn-primary:hover { background: var(--tb-primary-bg-hover); }\n.tb-btn-secondary {\n background: var(--tb-secondary-bg);\n color: var(--tb-secondary-fg);\n border-color: var(--tb-border);\n}\n.tb-btn-secondary:hover { background: var(--tb-secondary-bg-hover); }\n.tb-btn-ghost {\n background: transparent;\n color: var(--tb-fg-muted);\n padding: 6px 10px;\n}\n.tb-btn-ghost:hover { color: var(--tb-fg); }\n\n.tb-modal-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(15, 18, 24, 0.5);\n z-index: var(--tb-z);\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 20px;\n animation: tb-fade-in 160ms ease-out;\n}\n.tb-modal {\n background: var(--tb-bg);\n color: var(--tb-fg);\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n box-shadow: var(--tb-shadow);\n width: 100%;\n max-width: 520px;\n max-height: 85vh;\n display: flex;\n flex-direction: column;\n}\n.tb-modal-head {\n padding: 14px 16px;\n border-bottom: 1px solid var(--tb-border);\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n}\n.tb-modal-title {\n margin: 0;\n font-size: 15px;\n font-weight: 600;\n}\n.tb-modal-body {\n padding: 12px 16px;\n overflow-y: auto;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n.tb-modal-foot {\n padding: 12px 16px;\n border-top: 1px solid var(--tb-border);\n display: flex;\n gap: 8px;\n justify-content: flex-end;\n flex-wrap: wrap;\n}\n\n.tb-cat {\n border: 1px solid var(--tb-border);\n border-radius: var(--tb-radius);\n padding: 12px;\n display: flex;\n gap: 12px;\n align-items: flex-start;\n}\n.tb-cat-text { flex: 1; min-width: 0; }\n.tb-cat-name {\n font-weight: 600;\n margin: 0 0 2px;\n font-size: 13px;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n.tb-cat-desc {\n margin: 0;\n color: var(--tb-fg-muted);\n font-size: 13px;\n}\n.tb-badge {\n display: inline-block;\n font-size: 11px;\n font-weight: 500;\n color: var(--tb-fg-muted);\n background: var(--tb-secondary-bg-hover);\n border: 1px solid var(--tb-border);\n border-radius: 999px;\n padding: 1px 8px;\n}\n\n.tb-switch {\n position: relative;\n display: inline-block;\n width: 32px;\n height: 18px;\n flex-shrink: 0;\n margin-top: 2px;\n}\n.tb-switch input {\n opacity: 0;\n width: 0;\n height: 0;\n position: absolute;\n}\n.tb-switch-track {\n position: absolute;\n inset: 0;\n background: var(--tb-border);\n border-radius: 999px;\n transition: background-color 100ms ease;\n cursor: pointer;\n}\n.tb-switch-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--tb-bg);\n border-radius: 50%;\n transition: transform 100ms ease;\n}\n.tb-switch input:checked + .tb-switch-track {\n background: var(--tb-primary-bg);\n}\n.tb-switch input:checked + .tb-switch-track .tb-switch-thumb {\n transform: translateX(14px);\n}\n.tb-switch input:disabled + .tb-switch-track {\n opacity: 0.5;\n cursor: not-allowed;\n}\n.tb-switch input:focus-visible + .tb-switch-track {\n outline: 2px solid var(--tb-link);\n outline-offset: 2px;\n}\n\n@keyframes tb-fade-in {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n@media (max-width: 640px) {\n .tb-banner {\n flex-direction: column;\n align-items: stretch;\n }\n .tb-banner-actions {\n flex-direction: column;\n }\n .tb-banner-actions .tb-btn { width: 100%; }\n}\n`\n\nconst STYLE_ID = 'tickbox-default-styles'\n\nlet injected = false\n\n/**\n * Insert the stylesheet into `<head>` exactly once per page. Safe to call\n * from every component mount — subsequent calls are no-ops. No-op on the\n * server (no `document`).\n */\nexport function injectStyles(): void {\n if (injected) return\n if (typeof document === 'undefined') return\n if (document.getElementById(STYLE_ID)) {\n injected = true\n return\n }\n const el = document.createElement('style')\n el.id = STYLE_ID\n el.textContent = TICKBOX_STYLES\n document.head.appendChild(el)\n injected = true\n}\n"]}
|