react-cookie-consent-popup 1.0.5 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,9 +3,23 @@
3
3
  [![npm version](https://img.shields.io/npm/v/react-cookie-consent-popup)](https://www.npmjs.com/package/react-cookie-consent-popup)
4
4
  [![npm package size](https://img.shields.io/npm/unpacked-size/react-cookie-consent-popup)](https://www.npmjs.com/package/react-cookie-consent-popup)
5
5
  [![npm downloads](https://img.shields.io/npm/dw/react-cookie-consent-popup)](https://www.npmjs.com/package/react-cookie-consent-popup)
6
+ [![CI](https://github.com/Mas-HJ/react-cookie-consent-popup/actions/workflows/ci.yml/badge.svg)](https://github.com/Mas-HJ/react-cookie-consent-popup/actions/workflows/ci.yml)
6
7
  [![license](https://img.shields.io/npm/l/react-cookie-consent-popup)](https://github.com/Mas-HJ/react-cookie-consent-popup/blob/main/LICENSE)
7
8
 
8
- A React cookie consent popup component with a centered modal dialog. Supports service management, script loading, cookie/localStorage/sessionStorage cleanup, hash-based consent invalidation, a settings modal with toggles, and light/dark themes.
9
+ A **zero-dependency** React cookie consent popup with GDPR-compliant service management, automatic script loading, storage cleanup, and light/dark themes.
10
+
11
+ ## Features
12
+
13
+ - **Zero runtime dependencies** — only React as a peer dependency
14
+ - **Service management** — define scripts, cookies, localStorage and sessionStorage per service
15
+ - **Automatic cleanup** — scripts, cookies and storage are removed when consent is revoked
16
+ - **Hash-based invalidation** — popup reappears automatically when your service config changes
17
+ - **Settings modal** — per-service toggles with mandatory service support
18
+ - **Light & dark themes** — built-in themes with full CSS custom property support
19
+ - **Accessible** — focus trapping, keyboard navigation, `Escape` to close, ARIA labels
20
+ - **SSR compatible** — works with Next.js, Remix, and other server-rendered frameworks
21
+ - **Fully typed** — written in strict TypeScript with exported types
22
+ - **Tiny footprint** — ~8 KB minified (JS + CSS)
9
23
 
10
24
  ---
11
25
 
@@ -29,7 +43,7 @@ yarn add react-cookie-consent-popup
29
43
 
30
44
  ```tsx
31
45
  import { ConsentPopup, ConsentProvider } from 'react-cookie-consent-popup';
32
- import 'react-cookie-consent-popup/dist/styles/style.css';
46
+ import 'react-cookie-consent-popup/styles';
33
47
 
34
48
  const services = [
35
49
  {
@@ -44,7 +58,13 @@ const services = [
44
58
  description: 'Helps us understand how visitors use our site.',
45
59
  scripts: [
46
60
  { id: 'gtag', src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXX' },
47
- { id: 'gtag-init', code: `window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXX');` },
61
+ {
62
+ id: 'gtag-init',
63
+ code: `window.dataLayer = window.dataLayer || [];
64
+ function gtag(){dataLayer.push(arguments);}
65
+ gtag('js', new Date());
66
+ gtag('config', 'G-XXXXXXX');`,
67
+ },
48
68
  ],
49
69
  cookies: [{ pattern: /^_ga/ }],
50
70
  },
@@ -63,7 +83,13 @@ const services = [
63
83
 
64
84
  function App() {
65
85
  return (
66
- <ConsentProvider options={{ services, theme: 'light' }}>
86
+ <ConsentProvider
87
+ options={{
88
+ services,
89
+ theme: 'light',
90
+ onConsentChange: (consent) => console.log('Consent updated:', consent),
91
+ }}
92
+ >
67
93
  <main>
68
94
  <h1>My Website</h1>
69
95
  </main>
@@ -92,11 +118,12 @@ Wraps your application and provides consent state to all child components.
92
118
 
93
119
  #### `ConsentOptions`
94
120
 
95
- | Field | Type | Required | Description |
96
- |--------------|----------------------|----------|-------------------------------------------------------------------------------------------------------------------|
97
- | `services` | `ConsentService[]` | Yes | Array of services requiring consent |
98
- | `theme` | `'light' \| 'dark'` | No | Color theme (default: `'light'`) |
99
- | `customHash` | `string` | No | Override the auto-generated config hash. When this changes, stored consent is invalidated and the popup reappears |
121
+ | Field | Type | Required | Description |
122
+ |--------------------|-------------------------------|----------|-------------------------------------------------------------------------------------------------------------------|
123
+ | `services` | `ConsentService[]` | Yes | Array of services requiring consent |
124
+ | `theme` | `'light' \| 'dark'` | No | Color theme (default: `'light'`) |
125
+ | `customHash` | `string` | No | Override the auto-generated config hash. When this changes, stored consent is invalidated and the popup reappears |
126
+ | `onConsentChange` | `(consent: string[]) => void` | No | Callback fired whenever consent changes. Receives the array of consented service IDs |
100
127
 
101
128
  #### `ConsentService`
102
129
 
@@ -180,7 +207,7 @@ function MyComponent() {
180
207
 
181
208
  ## Hash-Based Invalidation
182
209
 
183
- Consent is persisted in `localStorage`. A hash is computed from your service configuration. If you add, remove, or rename services, the hash changes automatically and the popup will reappear, prompting users to re-consent.
210
+ Consent is persisted in `localStorage`. A hash is computed from your service configuration (`id` and `name` fields). If you add, remove, or rename services, the hash changes automatically and the popup will reappear, prompting users to re-consent.
184
211
 
185
212
  You can also force re-consent by providing a `customHash`:
186
213
 
@@ -196,6 +223,40 @@ Supports `'light'` and `'dark'` themes out of the box via CSS custom properties.
196
223
  <ConsentProvider options={{ services, theme: 'dark' }}>
197
224
  ```
198
225
 
226
+ ### Custom Theme
227
+
228
+ Override any CSS variable to match your brand:
229
+
230
+ ```css
231
+ [data-theme='light'] {
232
+ --rcc-text: #1a1a2e;
233
+ --rcc-bg: #ffffff;
234
+ --rcc-backdrop: rgb(0 0 0 / 45%);
235
+ --rcc-border: #e0e0e0;
236
+ --rcc-btn-primary-bg: #2563eb;
237
+ --rcc-btn-primary-text: #ffffff;
238
+ --rcc-btn-secondary-bg: transparent;
239
+ --rcc-btn-secondary-text: #2563eb;
240
+ --rcc-btn-secondary-border: #2563eb;
241
+ --rcc-scrollbar-track: #f0f0f0;
242
+ --rcc-scrollbar-thumb: #c0c0c0;
243
+ }
244
+ ```
245
+
246
+ ## Accessibility
247
+
248
+ The popup and settings modal include:
249
+
250
+ - **Focus trapping** — Tab and Shift+Tab cycle within the modal
251
+ - **Escape key** — closes the settings modal
252
+ - **Focus restoration** — returns focus to the previously focused element on close
253
+ - **ARIA attributes** — `role="dialog"`, `aria-modal`, `aria-labelledby` on both modals
254
+ - **Keyboard-friendly toggles** — custom toggle switches with focus-visible outlines
255
+
256
+ ## SSR / Next.js
257
+
258
+ All browser API access (`document`, `window`, `localStorage`, `sessionStorage`) is guarded with runtime checks. The library works out of the box with Next.js, Remix, Gatsby, and other server-rendered React frameworks.
259
+
199
260
  ## Development
200
261
 
201
262
  ```bash
@@ -218,6 +279,14 @@ yarn dev
218
279
  yarn coverage
219
280
  ```
220
281
 
282
+ ## Contributing
283
+
284
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
285
+
286
+ ## Changelog
287
+
288
+ See [CHANGELOG.md](CHANGELOG.md) for release history.
289
+
221
290
  ## License
222
291
 
223
292
  MIT
@@ -26,6 +26,7 @@ export interface ConsentOptions {
26
26
  services: ConsentService[];
27
27
  customHash?: string;
28
28
  theme?: Theme;
29
+ onConsentChange?: (consent: string[]) => void;
29
30
  }
30
31
  export interface ConsentSettingsModalLabels {
31
32
  title?: string | ReactNode;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- "use strict";var ne=Object.create;var _=Object.defineProperty;var se=Object.getOwnPropertyDescriptor;var ie=Object.getOwnPropertyNames;var re=Object.getPrototypeOf,ce=Object.prototype.hasOwnProperty;var ae=(t,e)=>{for(var o in e)_(t,o,{get:e[o],enumerable:!0})},L=(t,e,o,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of ie(e))!ce.call(t,i)&&i!==o&&_(t,i,{get:()=>e[i],enumerable:!(n=se(e,i))||n.enumerable});return t};var T=(t,e,o)=>(o=t!=null?ne(re(t)):{},L(e||!t||!t.__esModule?_(o,"default",{value:t,enumerable:!0}):o,t)),pe=t=>L(_({},"__esModule",{value:!0}),t);var Ce={};ae(Ce,{ConsentContext:()=>S,ConsentPopup:()=>H,ConsentProvider:()=>ee,ConsentSettings:()=>I,useConsent:()=>l});module.exports=pe(Ce);var M=require("react");var A=require("react"),le={consent:[],services:[],theme:"light",isPopupVisible:!0,isSettingsVisible:!1,setConsent:()=>{},hasConsent:()=>!1,showPopup:()=>{},hidePopup:()=>{},toggleSettings:()=>{}},S=(0,A.createContext)(le);function l(){return(0,M.useContext)(S)}var y=require("react");function N(){let{services:t,setConsent:e,hidePopup:o}=l(),n=(0,y.useCallback)(()=>{e(t.map(s=>s.id)),o()},[t,e,o]),i=(0,y.useCallback)(s=>{e(s),o()},[e,o]),r=(0,y.useCallback)(()=>{let s=t.filter(c=>c.mandatory).map(c=>c.id);e(s),o()},[t,e,o]);return{approveAll:n,approveSelected:i,declineAll:r}}var P=require("react");function O(){let{consent:t,services:e}=l(),[o,n]=(0,P.useState)(()=>{let r=e.filter(s=>s.mandatory).map(s=>s.id);return[...new Set([...t,...r])]}),i=(0,P.useCallback)((r,s)=>{n(c=>s?[...c,r]:c.filter(u=>u!==r))},[]);return{selectedIds:o,toggleService:i}}var $=T(require("react-toggle"));var C=require("react/jsx-runtime"),de=$.default;function w({serviceId:t,name:e,description:o,mandatory:n,onChange:i}){let{hasConsent:r}=l(),s=c=>{i(t,c.target.checked)};return(0,C.jsxs)("div",{className:"rcc-settings__item",children:[(0,C.jsxs)("div",{className:"rcc-settings__item__info",children:[(0,C.jsx)("label",{htmlFor:`rcc-toggle-${t}`,className:"rcc-settings__item__name",children:e}),o&&(0,C.jsx)("p",{className:"rcc-settings__item__description",children:o})]}),(0,C.jsx)(de,{id:`rcc-toggle-${t}`,defaultChecked:n||r(t),disabled:n,onChange:s,"aria-label":`Toggle ${e}`})]})}var d=require("react/jsx-runtime");function I({onClose:t,modal:e}){let{services:o}=l(),{approveSelected:n,declineAll:i}=N(),{selectedIds:r,toggleService:s}=O(),c=()=>{n(r),t()},u=()=>{i(),t()},f=e?.title??"Cookie Settings",v=e?.approve??"Save Selection",b=e?.decline??"Decline All",h=e?.close??"Close";return(0,d.jsx)("div",{className:"rcc-settings",role:"dialog","aria-modal":"true","aria-label":"Cookie Settings",children:(0,d.jsxs)("div",{className:"rcc-settings__content",children:[(0,d.jsxs)("header",{className:"rcc-settings__header",children:[(0,d.jsx)("h2",{className:"rcc-settings__title",children:f}),(0,d.jsx)("button",{type:"button",className:"rcc-settings__close",onClick:t,"aria-label":"Close settings",children:h})]}),(0,d.jsx)("main",{className:"rcc-settings__body",children:o.map(g=>(0,d.jsx)(w,{serviceId:g.id,name:g.name,description:g.description,mandatory:g.mandatory,onChange:s},g.id))}),(0,d.jsxs)("footer",{className:"rcc-settings__footer",children:[(0,d.jsx)("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--secondary",onClick:u,children:b}),(0,d.jsx)("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--primary",onClick:c,children:v})]})]})})}var m=require("react/jsx-runtime");function H({children:t,settings:e,decline:o,approve:n}){let{isPopupVisible:i,isSettingsVisible:r,toggleSettings:s,theme:c}=l(),{approveAll:u,declineAll:f}=N();if(!i)return null;if(r)return(0,m.jsx)("div",{className:"rcc-popup","data-theme":c,children:(0,m.jsx)(I,{onClose:s,modal:e?.modal})});let v=e?.label??"Settings",b=o?.label??"Decline",h=n?.label??"Accept All";return(0,m.jsx)("div",{className:"rcc-popup","data-theme":c,children:(0,m.jsxs)("div",{className:"rcc-popup__card",role:"dialog","aria-modal":"true","aria-label":"Cookie consent",children:[(0,m.jsx)("div",{className:"rcc-popup__message",children:t??"This website uses cookies to improve your experience."}),(0,m.jsxs)("div",{className:"rcc-popup__actions",children:[!e?.hidden&&(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:s,children:v}),!o?.hidden&&(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:f,children:b}),(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--primary",onClick:u,children:h})]})]})})}var a=require("react"),Z=T(require("object-hash"));var E="rcc-popup",k="rcc-popup-consent";function V(t){let e=localStorage.getItem(k);if(!e)return{consent:[],isValid:!1};try{let o=JSON.parse(e);return o.hash!==t?{consent:[],isValid:!1}:{consent:o.consent,isValid:!0}}catch{return{consent:[],isValid:!1}}}function D(t){return"src"in t}function F(t){return"code"in t}function G(t,e){let o=document.createElement("script");o.id=t,o.src=e,o.async=!0,document.body.appendChild(o)}function B(t,e){let o=document.createElement("script");o.id=t,o.innerHTML=e,document.body.appendChild(o)}function me(t,e){return`${E}-${t}-${e}`}function J(t,e){for(let o of e){let n=me(t,o.id);document.getElementById(n)||(D(o)?G(n,o.src):F(o)&&B(n,o.code))}}function R(t){for(let e of t)e.scripts?.length&&J(e.id,e.scripts)}function K(t,e){for(let o of e){let n=`${E}-${t}-${o.id}`,i=document.getElementById(n);i&&i.remove()}}function ue(){return document.cookie.split(";").map(t=>t.trim().split("=")[0]).filter(Boolean)}function X(t){document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`}function Y(t){let e=ue();for(let{pattern:o}of t)if(typeof o=="string")X(o);else for(let n of e)o.test(n)&&X(n)}function q(t){for(let e of t)localStorage.removeItem(e)}function z(t){for(let e of t)sessionStorage.removeItem(e)}function Q(t){for(let e of t)e.scripts?.length&&K(e.id,e.scripts),e.cookies?.length&&Y(e.cookies),e.localStorage?.length&&q(e.localStorage),e.sessionStorage?.length&&z(e.sessionStorage)}function U(t,e){let o={consent:t,hash:e,timestamp:Date.now()};localStorage.setItem(k,JSON.stringify(o))}function W(t,e,o,n){let i=t.filter(s=>o.includes(s.id)&&!e.includes(s.id)),r=t.filter(s=>!o.includes(s.id)&&e.includes(s.id));Q(r),R(i),U(o,n)}function ge(t){return t.customHash?t.customHash:(0,Z.default)(t.services.map(e=>({id:e.id,name:e.name})))}function j(t){let e=ge(t),{services:o}=t,[n,i]=(0,a.useState)(()=>{let{consent:p,isValid:x}=V(e);return x?p:[]}),[r,s]=(0,a.useState)(()=>{let{isValid:p}=V(e);return!p}),[c,u]=(0,a.useState)(!1),f=(0,a.useRef)(n);(0,a.useEffect)(()=>{let p=o.filter(x=>n.includes(x.id));R(p)},[]);let v=(0,a.useCallback)(p=>{let x=f.current;W(o,x,p,e),i(p),f.current=p},[o,e]),b=(0,a.useCallback)(p=>n.includes(p),[n]),h=(0,a.useCallback)(()=>s(!0),[]),g=(0,a.useCallback)(()=>s(!1),[]),oe=(0,a.useCallback)(()=>u(p=>!p),[]);return{consent:n,services:o,theme:t.theme??"light",isPopupVisible:r,isSettingsVisible:c,setConsent:v,hasConsent:b,showPopup:h,hidePopup:g,toggleSettings:oe}}var te=require("react/jsx-runtime");function ee({options:t,children:e}){let o=j(t);return(0,te.jsx)(S.Provider,{value:o,children:e})}
1
+ "use strict";var V=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var ae=Object.prototype.hasOwnProperty;var pe=(t,e)=>{for(var n in e)V(t,n,{get:e[n],enumerable:!0})},le=(t,e,n,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of ce(e))!ae.call(t,r)&&r!==n&&V(t,r,{get:()=>e[r],enumerable:!(s=ie(e,r))||s.enumerable});return t};var de=t=>le(V({},"__esModule",{value:!0}),t);var Se={};pe(Se,{ConsentContext:()=>v,ConsentPopup:()=>K,ConsentProvider:()=>oe,ConsentSettings:()=>k,useConsent:()=>l});module.exports=de(Se);var F=require("react");var M=require("react");var A=require("react"),me={consent:[],services:[],theme:"light",isPopupVisible:!0,isSettingsVisible:!1,setConsent:()=>{},hasConsent:()=>!1,showPopup:()=>{},hidePopup:()=>{},toggleSettings:()=>{}},v=(0,A.createContext)(me);function l(){return(0,M.useContext)(v)}var E=require("react");function N(){let{services:t,setConsent:e,hidePopup:n}=l(),s=(0,E.useCallback)(()=>{e(t.map(o=>o.id)),n()},[t,e,n]),r=(0,E.useCallback)(o=>{e(o),n()},[e,n]),i=(0,E.useCallback)(()=>{let o=t.filter(c=>c.mandatory).map(c=>c.id);e(o),n()},[t,e,n]);return{approveAll:s,approveSelected:r,declineAll:i}}var x=require("react"),O='a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';function H(t){let e=(0,x.useRef)(null),n=(0,x.useRef)(null);return(0,x.useEffect)(()=>{if(!t||typeof document>"u")return;n.current=document.activeElement;let s=e.current;s&&s.querySelector(O)?.focus();function r(i){if(i.key!=="Tab"||!s)return;let o=s.querySelectorAll(O);if(o.length===0)return;let c=o[0],u=o[o.length-1];i.shiftKey?document.activeElement===c&&(i.preventDefault(),u.focus()):document.activeElement===u&&(i.preventDefault(),c.focus())}return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r),n.current?.focus()}},[t]),e}var P=require("react");function D(){let{consent:t,services:e}=l(),[n,s]=(0,P.useState)(()=>{let i=e.filter(o=>o.mandatory).map(o=>o.id);return[...new Set([...t,...i])]}),r=(0,P.useCallback)((i,o)=>{s(c=>o?[...c,i]:c.filter(u=>u!==i))},[]);return{selectedIds:n,toggleService:r}}var g=require("react/jsx-runtime");function $({serviceId:t,name:e,description:n,mandatory:s,onChange:r}){let{hasConsent:i}=l(),o=c=>{r(t,c.target.checked)};return(0,g.jsxs)("div",{className:"rcc-settings__item",children:[(0,g.jsxs)("div",{className:"rcc-settings__item__info",children:[(0,g.jsx)("label",{htmlFor:`rcc-toggle-${t}`,className:"rcc-settings__item__name",children:e}),n&&(0,g.jsx)("p",{className:"rcc-settings__item__description",children:n})]}),(0,g.jsxs)("label",{className:"rcc-toggle","aria-label":`Toggle ${e}`,children:[(0,g.jsx)("input",{type:"checkbox",id:`rcc-toggle-${t}`,className:"rcc-toggle__input",defaultChecked:s||i(t),disabled:s,onChange:o}),(0,g.jsx)("span",{className:"rcc-toggle__track"})]})]})}var d=require("react/jsx-runtime");function k({onClose:t,modal:e}){let{services:n}=l(),{approveSelected:s,declineAll:r}=N(),{selectedIds:i,toggleService:o}=D(),c=()=>{s(i),t()},u=()=>{r(),t()},b=e?.title??"Cookie Settings",C=e?.approve??"Save Selection",S=e?.decline??"Decline All",h=e?.close??"Close";return(0,d.jsx)("div",{className:"rcc-settings",role:"dialog","aria-modal":"true","aria-labelledby":"rcc-settings-title",children:(0,d.jsxs)("div",{className:"rcc-settings__content",children:[(0,d.jsxs)("header",{className:"rcc-settings__header",children:[(0,d.jsx)("h2",{id:"rcc-settings-title",className:"rcc-settings__title",children:b}),(0,d.jsx)("button",{type:"button",className:"rcc-settings__close",onClick:t,"aria-label":"Close settings",children:h})]}),(0,d.jsx)("main",{className:"rcc-settings__body",children:n.map(f=>(0,d.jsx)($,{serviceId:f.id,name:f.name,description:f.description,mandatory:f.mandatory,onChange:o},f.id))}),(0,d.jsxs)("footer",{className:"rcc-settings__footer",children:[(0,d.jsx)("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--secondary",onClick:u,children:S}),(0,d.jsx)("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--primary",onClick:c,children:C})]})]})})}var m=require("react/jsx-runtime");function K({children:t,settings:e,decline:n,approve:s}){let{isPopupVisible:r,isSettingsVisible:i,toggleSettings:o,theme:c}=l(),{approveAll:u,declineAll:b}=N(),C=H(r);if((0,F.useEffect)(()=>{if(!r)return;function _(T){T.key==="Escape"&&i&&o()}return document.addEventListener("keydown",_),()=>document.removeEventListener("keydown",_)},[r,i,o]),!r)return null;if(i)return(0,m.jsx)("div",{className:"rcc-popup","data-theme":c,ref:C,children:(0,m.jsx)(k,{onClose:o,modal:e?.modal})});let S=e?.label??"Settings",h=n?.label??"Decline",f=s?.label??"Accept All";return(0,m.jsx)("div",{className:"rcc-popup","data-theme":c,ref:C,children:(0,m.jsxs)("div",{className:"rcc-popup__card",role:"dialog","aria-modal":"true","aria-labelledby":"rcc-popup-heading",children:[(0,m.jsx)("div",{id:"rcc-popup-heading",className:"rcc-popup__message",children:t??"This website uses cookies to improve your experience."}),(0,m.jsxs)("div",{className:"rcc-popup__actions",children:[!e?.hidden&&(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:o,children:S}),!n?.hidden&&(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:b,children:h}),(0,m.jsx)("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--primary",onClick:u,children:f})]})]})})}var a=require("react");function ue(t){let e=5381;for(let n=0;n<t.length;n++)e=e*33^t.charCodeAt(n);return(e>>>0).toString(16)}function B(t){let e=JSON.stringify(t.map(n=>({id:n.id,name:n.name})));return ue(e)}var R="rcc-popup",I="rcc-popup-consent";function w(t){if(typeof window>"u")return{consent:[],isValid:!1};let e=localStorage.getItem(I);if(!e)return{consent:[],isValid:!1};try{let n=JSON.parse(e);return n.hash!==t?{consent:[],isValid:!1}:{consent:n.consent,isValid:!0}}catch{return{consent:[],isValid:!1}}}function G(t){return"src"in t}function J(t){return"code"in t}function X(t,e){if(typeof document>"u")return;let n=document.createElement("script");n.id=t,n.src=e,n.async=!0,document.body.appendChild(n)}function Y(t,e){if(typeof document>"u")return;let n=document.createElement("script");n.id=t,n.innerHTML=e,document.body.appendChild(n)}function fe(t,e){return`${R}-${t}-${e}`}function q(t,e){if(!(typeof document>"u"))for(let n of e){let s=fe(t,n.id);document.getElementById(s)||(G(n)?X(s,n.src):J(n)&&Y(s,n.code))}}function L(t){for(let e of t)e.scripts?.length&&q(e.id,e.scripts)}function U(t,e){if(!(typeof document>"u"))for(let n of e){let s=`${R}-${t}-${n.id}`,r=document.getElementById(s);r&&r.remove()}}function ge(){return typeof document>"u"?[]:document.cookie.split(";").map(t=>t.trim().split("=")[0]).filter(Boolean)}function z(t){typeof document>"u"||(document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`)}function Q(t){let e=ge();for(let{pattern:n}of t)if(typeof n=="string")z(n);else for(let s of e)n.test(s)&&z(s)}function W(t){if(!(typeof window>"u"))for(let e of t)localStorage.removeItem(e)}function Z(t){if(!(typeof window>"u"))for(let e of t)sessionStorage.removeItem(e)}function j(t){for(let e of t)e.scripts?.length&&U(e.id,e.scripts),e.cookies?.length&&Q(e.cookies),e.localStorage?.length&&W(e.localStorage),e.sessionStorage?.length&&Z(e.sessionStorage)}function ee(t,e){if(typeof window>"u")return;let n={consent:t,hash:e,timestamp:Date.now()};localStorage.setItem(I,JSON.stringify(n))}function te(t,e,n,s){let r=t.filter(o=>n.includes(o.id)&&!e.includes(o.id)),i=t.filter(o=>!n.includes(o.id)&&e.includes(o.id));j(i),L(r),ee(n,s)}function Ce(t){return t.customHash?t.customHash:B(t.services)}function ne(t){let e=Ce(t),{services:n,onConsentChange:s}=t,[r,i]=(0,a.useState)(()=>{let{consent:p,isValid:y}=w(e);return y?p:[]}),[o,c]=(0,a.useState)(()=>{let{isValid:p}=w(e);return!p}),[u,b]=(0,a.useState)(!1),C=(0,a.useRef)(r),S=(0,a.useRef)(s);S.current=s,(0,a.useEffect)(()=>{let p=n.filter(y=>r.includes(y.id));L(p)},[]);let h=(0,a.useCallback)(p=>{let y=C.current;te(n,y,p,e),i(p),C.current=p,S.current?.(p)},[n,e]),f=(0,a.useCallback)(p=>r.includes(p),[r]),_=(0,a.useCallback)(()=>c(!0),[]),T=(0,a.useCallback)(()=>c(!1),[]),re=(0,a.useCallback)(()=>b(p=>!p),[]);return{consent:r,services:n,theme:t.theme??"light",isPopupVisible:o,isSettingsVisible:u,setConsent:h,hasConsent:f,showPopup:_,hidePopup:T,toggleSettings:re}}var se=require("react/jsx-runtime");function oe({options:t,children:e}){let n=ne(t);return(0,se.jsx)(v.Provider,{value:n,children:e})}
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import{useContext as Z}from"react";import{createContext as U}from"react";var W={consent:[],services:[],theme:"light",isPopupVisible:!0,isSettingsVisible:!1,setConsent:()=>{},hasConsent:()=>!1,showPopup:()=>{},hidePopup:()=>{},toggleSettings:()=>{}},b=U(W);function p(){return Z(b)}import{useCallback as P}from"react";function x(){let{services:t,setConsent:e,hidePopup:o}=p(),n=P(()=>{e(t.map(s=>s.id)),o()},[t,e,o]),i=P(s=>{e(s),o()},[e,o]),r=P(()=>{let s=t.filter(c=>c.mandatory).map(c=>c.id);e(s),o()},[t,e,o]);return{approveAll:n,approveSelected:i,declineAll:r}}import{useState as j,useCallback as ee}from"react";function L(){let{consent:t,services:e}=p(),[o,n]=j(()=>{let r=e.filter(s=>s.mandatory).map(s=>s.id);return[...new Set([...t,...r])]}),i=ee((r,s)=>{n(c=>s?[...c,r]:c.filter(l=>l!==r))},[]);return{selectedIds:o,toggleService:i}}import te from"react-toggle";import{jsx as I,jsxs as T}from"react/jsx-runtime";var oe=te;function A({serviceId:t,name:e,description:o,mandatory:n,onChange:i}){let{hasConsent:r}=p(),s=c=>{i(t,c.target.checked)};return T("div",{className:"rcc-settings__item",children:[T("div",{className:"rcc-settings__item__info",children:[I("label",{htmlFor:`rcc-toggle-${t}`,className:"rcc-settings__item__name",children:e}),o&&I("p",{className:"rcc-settings__item__description",children:o})]}),I(oe,{id:`rcc-toggle-${t}`,defaultChecked:n||r(t),disabled:n,onChange:s,"aria-label":`Toggle ${e}`})]})}import{jsx as m,jsxs as E}from"react/jsx-runtime";function k({onClose:t,modal:e}){let{services:o}=p(),{approveSelected:n,declineAll:i}=x(),{selectedIds:r,toggleService:s}=L(),c=()=>{n(r),t()},l=()=>{i(),t()},g=e?.title??"Cookie Settings",C=e?.approve??"Save Selection",f=e?.decline??"Decline All",S=e?.close??"Close";return m("div",{className:"rcc-settings",role:"dialog","aria-modal":"true","aria-label":"Cookie Settings",children:E("div",{className:"rcc-settings__content",children:[E("header",{className:"rcc-settings__header",children:[m("h2",{className:"rcc-settings__title",children:g}),m("button",{type:"button",className:"rcc-settings__close",onClick:t,"aria-label":"Close settings",children:S})]}),m("main",{className:"rcc-settings__body",children:o.map(d=>m(A,{serviceId:d.id,name:d.name,description:d.description,mandatory:d.mandatory,onChange:s},d.id))}),E("footer",{className:"rcc-settings__footer",children:[m("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--secondary",onClick:l,children:f}),m("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--primary",onClick:c,children:C})]})]})})}import{jsx as u,jsxs as M}from"react/jsx-runtime";function ne({children:t,settings:e,decline:o,approve:n}){let{isPopupVisible:i,isSettingsVisible:r,toggleSettings:s,theme:c}=p(),{approveAll:l,declineAll:g}=x();if(!i)return null;if(r)return u("div",{className:"rcc-popup","data-theme":c,children:u(k,{onClose:s,modal:e?.modal})});let C=e?.label??"Settings",f=o?.label??"Decline",S=n?.label??"Accept All";return u("div",{className:"rcc-popup","data-theme":c,children:M("div",{className:"rcc-popup__card",role:"dialog","aria-modal":"true","aria-label":"Cookie consent",children:[u("div",{className:"rcc-popup__message",children:t??"This website uses cookies to improve your experience."}),M("div",{className:"rcc-popup__actions",children:[!e?.hidden&&u("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:s,children:C}),!o?.hidden&&u("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:g,children:f}),u("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--primary",onClick:l,children:S})]})]})})}import{useState as V,useCallback as h,useEffect as re,useRef as ce}from"react";import ae from"object-hash";var _="rcc-popup",y="rcc-popup-consent";function R(t){let e=localStorage.getItem(y);if(!e)return{consent:[],isValid:!1};try{let o=JSON.parse(e);return o.hash!==t?{consent:[],isValid:!1}:{consent:o.consent,isValid:!0}}catch{return{consent:[],isValid:!1}}}function O(t){return"src"in t}function $(t){return"code"in t}function w(t,e){let o=document.createElement("script");o.id=t,o.src=e,o.async=!0,document.body.appendChild(o)}function H(t,e){let o=document.createElement("script");o.id=t,o.innerHTML=e,document.body.appendChild(o)}function se(t,e){return`${_}-${t}-${e}`}function D(t,e){for(let o of e){let n=se(t,o.id);document.getElementById(n)||(O(o)?w(n,o.src):$(o)&&H(n,o.code))}}function N(t){for(let e of t)e.scripts?.length&&D(e.id,e.scripts)}function F(t,e){for(let o of e){let n=`${_}-${t}-${o.id}`,i=document.getElementById(n);i&&i.remove()}}function ie(){return document.cookie.split(";").map(t=>t.trim().split("=")[0]).filter(Boolean)}function G(t){document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`}function B(t){let e=ie();for(let{pattern:o}of t)if(typeof o=="string")G(o);else for(let n of e)o.test(n)&&G(n)}function J(t){for(let e of t)localStorage.removeItem(e)}function K(t){for(let e of t)sessionStorage.removeItem(e)}function X(t){for(let e of t)e.scripts?.length&&F(e.id,e.scripts),e.cookies?.length&&B(e.cookies),e.localStorage?.length&&J(e.localStorage),e.sessionStorage?.length&&K(e.sessionStorage)}function Y(t,e){let o={consent:t,hash:e,timestamp:Date.now()};localStorage.setItem(y,JSON.stringify(o))}function q(t,e,o,n){let i=t.filter(s=>o.includes(s.id)&&!e.includes(s.id)),r=t.filter(s=>!o.includes(s.id)&&e.includes(s.id));X(r),N(i),Y(o,n)}function pe(t){return t.customHash?t.customHash:ae(t.services.map(e=>({id:e.id,name:e.name})))}function z(t){let e=pe(t),{services:o}=t,[n,i]=V(()=>{let{consent:a,isValid:v}=R(e);return v?a:[]}),[r,s]=V(()=>{let{isValid:a}=R(e);return!a}),[c,l]=V(!1),g=ce(n);re(()=>{let a=o.filter(v=>n.includes(v.id));N(a)},[]);let C=h(a=>{let v=g.current;q(o,v,a,e),i(a),g.current=a},[o,e]),f=h(a=>n.includes(a),[n]),S=h(()=>s(!0),[]),d=h(()=>s(!1),[]),Q=h(()=>l(a=>!a),[]);return{consent:n,services:o,theme:t.theme??"light",isPopupVisible:r,isSettingsVisible:c,setConsent:C,hasConsent:f,showPopup:S,hidePopup:d,toggleSettings:Q}}import{jsx as de}from"react/jsx-runtime";function le({options:t,children:e}){let o=z(t);return de(b.Provider,{value:o,children:e})}export{b as ConsentContext,ne as ConsentPopup,le as ConsentProvider,k as ConsentSettings,p as useConsent};
1
+ import{useEffect as pe}from"react";import{useContext as re}from"react";import{createContext as oe}from"react";var se={consent:[],services:[],theme:"light",isPopupVisible:!0,isSettingsVisible:!1,setConsent:()=>{},hasConsent:()=>!1,showPopup:()=>{},hidePopup:()=>{},toggleSettings:()=>{}},b=oe(se);function p(){return re(b)}import{useCallback as R}from"react";function x(){let{services:t,setConsent:e,hidePopup:n}=p(),s=R(()=>{e(t.map(o=>o.id)),n()},[t,e,n]),r=R(o=>{e(o),n()},[e,n]),i=R(()=>{let o=t.filter(c=>c.mandatory).map(c=>c.id);e(o),n()},[t,e,n]);return{approveAll:s,approveSelected:r,declineAll:i}}import{useEffect as ie,useRef as A}from"react";var M='a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';function O(t){let e=A(null),n=A(null);return ie(()=>{if(!t||typeof document>"u")return;n.current=document.activeElement;let s=e.current;s&&s.querySelector(M)?.focus();function r(i){if(i.key!=="Tab"||!s)return;let o=s.querySelectorAll(M);if(o.length===0)return;let c=o[0],l=o[o.length-1];i.shiftKey?document.activeElement===c&&(i.preventDefault(),l.focus()):document.activeElement===l&&(i.preventDefault(),c.focus())}return document.addEventListener("keydown",r),()=>{document.removeEventListener("keydown",r),n.current?.focus()}},[t]),e}import{useState as ce,useCallback as ae}from"react";function H(){let{consent:t,services:e}=p(),[n,s]=ce(()=>{let i=e.filter(o=>o.mandatory).map(o=>o.id);return[...new Set([...t,...i])]}),r=ae((i,o)=>{s(c=>o?[...c,i]:c.filter(l=>l!==i))},[]);return{selectedIds:n,toggleService:r}}import{jsx as _,jsxs as I}from"react/jsx-runtime";function D({serviceId:t,name:e,description:n,mandatory:s,onChange:r}){let{hasConsent:i}=p(),o=c=>{r(t,c.target.checked)};return I("div",{className:"rcc-settings__item",children:[I("div",{className:"rcc-settings__item__info",children:[_("label",{htmlFor:`rcc-toggle-${t}`,className:"rcc-settings__item__name",children:e}),n&&_("p",{className:"rcc-settings__item__description",children:n})]}),I("label",{className:"rcc-toggle","aria-label":`Toggle ${e}`,children:[_("input",{type:"checkbox",id:`rcc-toggle-${t}`,className:"rcc-toggle__input",defaultChecked:s||i(t),disabled:s,onChange:o}),_("span",{className:"rcc-toggle__track"})]})]})}import{jsx as u,jsxs as L}from"react/jsx-runtime";function T({onClose:t,modal:e}){let{services:n}=p(),{approveSelected:s,declineAll:r}=x(),{selectedIds:i,toggleService:o}=H(),c=()=>{s(i),t()},l=()=>{r(),t()},C=e?.title??"Cookie Settings",m=e?.approve??"Save Selection",g=e?.decline??"Decline All",S=e?.close??"Close";return u("div",{className:"rcc-settings",role:"dialog","aria-modal":"true","aria-labelledby":"rcc-settings-title",children:L("div",{className:"rcc-settings__content",children:[L("header",{className:"rcc-settings__header",children:[u("h2",{id:"rcc-settings-title",className:"rcc-settings__title",children:C}),u("button",{type:"button",className:"rcc-settings__close",onClick:t,"aria-label":"Close settings",children:S})]}),u("main",{className:"rcc-settings__body",children:n.map(d=>u(D,{serviceId:d.id,name:d.name,description:d.description,mandatory:d.mandatory,onChange:o},d.id))}),L("footer",{className:"rcc-settings__footer",children:[u("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--secondary",onClick:l,children:g}),u("button",{type:"button",className:"rcc-settings__btn rcc-settings__btn--primary",onClick:c,children:m})]})]})})}import{jsx as f,jsxs as $}from"react/jsx-runtime";function le({children:t,settings:e,decline:n,approve:s}){let{isPopupVisible:r,isSettingsVisible:i,toggleSettings:o,theme:c}=p(),{approveAll:l,declineAll:C}=x(),m=O(r);if(pe(()=>{if(!r)return;function y(k){k.key==="Escape"&&i&&o()}return document.addEventListener("keydown",y),()=>document.removeEventListener("keydown",y)},[r,i,o]),!r)return null;if(i)return f("div",{className:"rcc-popup","data-theme":c,ref:m,children:f(T,{onClose:o,modal:e?.modal})});let g=e?.label??"Settings",S=n?.label??"Decline",d=s?.label??"Accept All";return f("div",{className:"rcc-popup","data-theme":c,ref:m,children:$("div",{className:"rcc-popup__card",role:"dialog","aria-modal":"true","aria-labelledby":"rcc-popup-heading",children:[f("div",{id:"rcc-popup-heading",className:"rcc-popup__message",children:t??"This website uses cookies to improve your experience."}),$("div",{className:"rcc-popup__actions",children:[!e?.hidden&&f("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:o,children:g}),!n?.hidden&&f("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--secondary",onClick:C,children:S}),f("button",{type:"button",className:"rcc-popup__btn rcc-popup__btn--primary",onClick:l,children:d})]})]})})}import{useState as w,useCallback as h,useEffect as fe,useRef as ee}from"react";function de(t){let e=5381;for(let n=0;n<t.length;n++)e=e*33^t.charCodeAt(n);return(e>>>0).toString(16)}function F(t){let e=JSON.stringify(t.map(n=>({id:n.id,name:n.name})));return de(e)}var E="rcc-popup",N="rcc-popup-consent";function V(t){if(typeof window>"u")return{consent:[],isValid:!1};let e=localStorage.getItem(N);if(!e)return{consent:[],isValid:!1};try{let n=JSON.parse(e);return n.hash!==t?{consent:[],isValid:!1}:{consent:n.consent,isValid:!0}}catch{return{consent:[],isValid:!1}}}function K(t){return"src"in t}function B(t){return"code"in t}function G(t,e){if(typeof document>"u")return;let n=document.createElement("script");n.id=t,n.src=e,n.async=!0,document.body.appendChild(n)}function J(t,e){if(typeof document>"u")return;let n=document.createElement("script");n.id=t,n.innerHTML=e,document.body.appendChild(n)}function me(t,e){return`${E}-${t}-${e}`}function X(t,e){if(!(typeof document>"u"))for(let n of e){let s=me(t,n.id);document.getElementById(s)||(K(n)?G(s,n.src):B(n)&&J(s,n.code))}}function P(t){for(let e of t)e.scripts?.length&&X(e.id,e.scripts)}function Y(t,e){if(!(typeof document>"u"))for(let n of e){let s=`${E}-${t}-${n.id}`,r=document.getElementById(s);r&&r.remove()}}function ue(){return typeof document>"u"?[]:document.cookie.split(";").map(t=>t.trim().split("=")[0]).filter(Boolean)}function q(t){typeof document>"u"||(document.cookie=`${t}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`)}function U(t){let e=ue();for(let{pattern:n}of t)if(typeof n=="string")q(n);else for(let s of e)n.test(s)&&q(s)}function z(t){if(!(typeof window>"u"))for(let e of t)localStorage.removeItem(e)}function Q(t){if(!(typeof window>"u"))for(let e of t)sessionStorage.removeItem(e)}function W(t){for(let e of t)e.scripts?.length&&Y(e.id,e.scripts),e.cookies?.length&&U(e.cookies),e.localStorage?.length&&z(e.localStorage),e.sessionStorage?.length&&Q(e.sessionStorage)}function Z(t,e){if(typeof window>"u")return;let n={consent:t,hash:e,timestamp:Date.now()};localStorage.setItem(N,JSON.stringify(n))}function j(t,e,n,s){let r=t.filter(o=>n.includes(o.id)&&!e.includes(o.id)),i=t.filter(o=>!n.includes(o.id)&&e.includes(o.id));W(i),P(r),Z(n,s)}function ge(t){return t.customHash?t.customHash:F(t.services)}function te(t){let e=ge(t),{services:n,onConsentChange:s}=t,[r,i]=w(()=>{let{consent:a,isValid:v}=V(e);return v?a:[]}),[o,c]=w(()=>{let{isValid:a}=V(e);return!a}),[l,C]=w(!1),m=ee(r),g=ee(s);g.current=s,fe(()=>{let a=n.filter(v=>r.includes(v.id));P(a)},[]);let S=h(a=>{let v=m.current;j(n,v,a,e),i(a),m.current=a,g.current?.(a)},[n,e]),d=h(a=>r.includes(a),[r]),y=h(()=>c(!0),[]),k=h(()=>c(!1),[]),ne=h(()=>C(a=>!a),[]);return{consent:r,services:n,theme:t.theme??"light",isPopupVisible:o,isSettingsVisible:l,setConsent:S,hasConsent:d,showPopup:y,hidePopup:k,toggleSettings:ne}}import{jsx as Se}from"react/jsx-runtime";function Ce({options:t,children:e}){let n=te(t);return Se(b.Provider,{value:n,children:e})}export{b as ConsentContext,le as ConsentPopup,Ce as ConsentProvider,T as ConsentSettings,p as useConsent};
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  import type { ConsentSettingsModalLabels } from '../ConsentContext';
3
3
  export interface ConsentPopupProps {
4
4
  children?: ReactNode;
@@ -1 +1 @@
1
- .react-toggle{touch-action:pan-x;display:inline-block;position:relative;cursor:pointer;background-color:transparent;border:0;padding:0;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;-webkit-tap-highlight-color:rgba(0,0,0,0);-webkit-tap-highlight-color:transparent}.react-toggle-screenreader-only{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.react-toggle--disabled{cursor:not-allowed;opacity:.5;-webkit-transition:opacity .25s;transition:opacity .25s}.react-toggle-track{width:50px;height:24px;padding:0;border-radius:30px;background-color:#4d4d4d;-webkit-transition:all .2s ease;-moz-transition:all .2s ease;transition:all .2s ease}.react-toggle:hover:not(.react-toggle--disabled) .react-toggle-track{background-color:#000}.react-toggle--checked .react-toggle-track{background-color:#19ab27}.react-toggle--checked:hover:not(.react-toggle--disabled) .react-toggle-track{background-color:#128d15}.react-toggle-track-check{position:absolute;width:14px;height:10px;top:0;bottom:0;margin-top:auto;margin-bottom:auto;line-height:0;left:8px;opacity:0;-webkit-transition:opacity .25s ease;-moz-transition:opacity .25s ease;transition:opacity .25s ease}.react-toggle--checked .react-toggle-track-check{opacity:1;-webkit-transition:opacity .25s ease;-moz-transition:opacity .25s ease;transition:opacity .25s ease}.react-toggle-track-x{position:absolute;width:10px;height:10px;top:0;bottom:0;margin-top:auto;margin-bottom:auto;line-height:0;right:10px;opacity:1;-webkit-transition:opacity .25s ease;-moz-transition:opacity .25s ease;transition:opacity .25s ease}.react-toggle--checked .react-toggle-track-x{opacity:0}.react-toggle-thumb{transition:all .5s cubic-bezier(.23,1,.32,1) 0ms;position:absolute;top:1px;left:1px;width:22px;height:22px;border:1px solid #4D4D4D;border-radius:50%;background-color:#fafafa;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;-webkit-transition:all .25s ease;-moz-transition:all .25s ease;transition:all .25s ease}.react-toggle--checked .react-toggle-thumb{left:27px;border-color:#19ab27}.react-toggle--focus .react-toggle-thumb{-webkit-box-shadow:0px 0px 3px 2px #0099E0;-moz-box-shadow:0px 0px 3px 2px #0099E0;box-shadow:0 0 2px 3px #0099e0}.react-toggle:active:not(.react-toggle--disabled) .react-toggle-thumb{-webkit-box-shadow:0px 0px 5px 5px #0099E0;-moz-box-shadow:0px 0px 5px 5px #0099E0;box-shadow:0 0 5px 5px #0099e0}[data-theme=light]{--rcc-text: #1a1a2e;--rcc-bg: #ffffff;--rcc-backdrop: rgb(0 0 0 / 45%);--rcc-border: #e0e0e0;--rcc-btn-primary-bg: #2563eb;--rcc-btn-primary-text: #ffffff;--rcc-btn-secondary-bg: transparent;--rcc-btn-secondary-text: #2563eb;--rcc-btn-secondary-border: #2563eb;--rcc-scrollbar-track: #f0f0f0;--rcc-scrollbar-thumb: #c0c0c0}[data-theme=dark]{--rcc-text: #e8e8e8;--rcc-bg: #1e1e2e;--rcc-backdrop: rgb(0 0 0 / 60%);--rcc-border: #3a3a4a;--rcc-btn-primary-bg: #3b82f6;--rcc-btn-primary-text: #ffffff;--rcc-btn-secondary-bg: transparent;--rcc-btn-secondary-text: #93b4f6;--rcc-btn-secondary-border: #93b4f6;--rcc-scrollbar-track: #2a2a3a;--rcc-scrollbar-thumb: #555570}.rcc-settings__close,.rcc-settings__btn--secondary,.rcc-popup__btn--secondary,.rcc-settings__btn--primary,.rcc-popup__btn--primary{display:inline-flex;align-items:center;justify-content:center;padding:.625rem 1.25rem;border-radius:.375rem;font-family:inherit;font-size:.875rem;font-weight:600;line-height:1.4;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease}.rcc-settings__btn--primary,.rcc-popup__btn--primary{border:2px solid var(--rcc-btn-primary-bg);background-color:var(--rcc-btn-primary-bg);color:var(--rcc-btn-primary-text)}.rcc-settings__btn--primary:hover,.rcc-popup__btn--primary:hover{filter:brightness(1.1)}.rcc-settings__btn--secondary,.rcc-popup__btn--secondary{border:2px solid var(--rcc-btn-secondary-border);background-color:var(--rcc-btn-secondary-bg);color:var(--rcc-btn-secondary-text)}.rcc-settings__btn--secondary:hover,.rcc-popup__btn--secondary:hover{background-color:var(--rcc-btn-secondary-border);color:var(--rcc-btn-primary-text)}.rcc-popup{display:flex;position:fixed;z-index:99999;inset:0;align-items:center;justify-content:center;padding:1rem;background-color:var(--rcc-backdrop);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;color:var(--rcc-text)}.rcc-popup__card{width:100%;max-width:33.75rem;border-radius:.75rem;background-color:var(--rcc-bg);box-shadow:0 1.25rem 3.75rem #0003,0 .25rem .75rem #0000001a;padding:1.75rem 1.5rem}@media(min-width:600px){.rcc-popup__card{padding:2rem}}.rcc-popup__message{margin-bottom:1.5rem;font-size:.9375rem;line-height:1.6}.rcc-popup__actions{display:grid;gap:.5rem;grid-template-columns:1fr}@media(min-width:600px){.rcc-popup__actions{grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr))}}.rcc-settings{display:grid;width:100%;max-width:35rem;max-height:85vh;border-radius:.75rem;background-color:var(--rcc-bg);box-shadow:0 1.25rem 3.75rem #0003,0 .25rem .75rem #0000001a;grid-template-rows:auto 1fr auto}.rcc-settings__content{display:contents}.rcc-settings__header{display:flex;align-items:center;justify-content:space-between;padding:1.25rem 1.5rem;border-bottom:1px solid var(--rcc-border)}.rcc-settings__title{margin:0;font-size:1.125rem;font-weight:700}.rcc-settings__close{padding:.375rem .75rem;border:1px solid var(--rcc-border);background:transparent;font-size:.8125rem}.rcc-settings__body{overflow-y:auto;padding:1rem 1.5rem;scrollbar-width:thin;scrollbar-color:var(--rcc-scrollbar-thumb) var(--rcc-scrollbar-track)}.rcc-settings__body::-webkit-scrollbar{width:.375rem}.rcc-settings__body::-webkit-scrollbar-track{background:var(--rcc-scrollbar-track)}.rcc-settings__body::-webkit-scrollbar-thumb{border-radius:.1875rem;background:var(--rcc-scrollbar-thumb)}.rcc-settings__item{display:flex;align-items:center;justify-content:space-between;padding:.875rem 0;border-bottom:1px solid var(--rcc-border);gap:1rem}.rcc-settings__item:last-child{border-bottom:none}.rcc-settings__item__info{flex:1}.rcc-settings__item__name{display:block;font-size:.9375rem;font-weight:600;cursor:pointer}.rcc-settings__item__description{margin:.25rem 0 0;font-size:.8125rem;line-height:1.5;opacity:.75}.rcc-settings__footer{display:flex;justify-content:flex-end;padding:1rem 1.5rem;border-top:1px solid var(--rcc-border);gap:.5rem}
1
+ [data-theme=light]{--rcc-text: #1a1a2e;--rcc-bg: #ffffff;--rcc-backdrop: rgb(0 0 0 / 45%);--rcc-border: #e0e0e0;--rcc-btn-primary-bg: #2563eb;--rcc-btn-primary-text: #ffffff;--rcc-btn-secondary-bg: transparent;--rcc-btn-secondary-text: #2563eb;--rcc-btn-secondary-border: #2563eb;--rcc-scrollbar-track: #f0f0f0;--rcc-scrollbar-thumb: #c0c0c0}[data-theme=dark]{--rcc-text: #e8e8e8;--rcc-bg: #1e1e2e;--rcc-backdrop: rgb(0 0 0 / 60%);--rcc-border: #3a3a4a;--rcc-btn-primary-bg: #3b82f6;--rcc-btn-primary-text: #ffffff;--rcc-btn-secondary-bg: transparent;--rcc-btn-secondary-text: #93b4f6;--rcc-btn-secondary-border: #93b4f6;--rcc-scrollbar-track: #2a2a3a;--rcc-scrollbar-thumb: #555570}.rcc-settings__close,.rcc-settings__btn--secondary,.rcc-popup__btn--secondary,.rcc-settings__btn--primary,.rcc-popup__btn--primary{display:inline-flex;align-items:center;justify-content:center;padding:.625rem 1.25rem;border-radius:.375rem;font-family:inherit;font-size:.875rem;font-weight:600;line-height:1.4;cursor:pointer;transition:background-color .2s ease,border-color .2s ease,color .2s ease}.rcc-settings__btn--primary,.rcc-popup__btn--primary{border:2px solid var(--rcc-btn-primary-bg);background-color:var(--rcc-btn-primary-bg);color:var(--rcc-btn-primary-text)}.rcc-settings__btn--primary:hover,.rcc-popup__btn--primary:hover{filter:brightness(1.1)}.rcc-settings__btn--secondary,.rcc-popup__btn--secondary{border:2px solid var(--rcc-btn-secondary-border);background-color:var(--rcc-btn-secondary-bg);color:var(--rcc-btn-secondary-text)}.rcc-settings__btn--secondary:hover,.rcc-popup__btn--secondary:hover{background-color:var(--rcc-btn-secondary-border);color:var(--rcc-btn-primary-text)}.rcc-toggle{position:relative;display:inline-flex;flex-shrink:0;cursor:pointer}.rcc-toggle__input{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;border:0;clip:rect(0 0 0 0);white-space:nowrap}.rcc-toggle__track{display:inline-block;width:2.875rem;height:1.5rem;border-radius:.75rem;background-color:var(--rcc-border);transition:background-color .2s ease}.rcc-toggle__track:after{content:"";display:block;width:1.125rem;height:1.125rem;margin:.1875rem;border-radius:50%;background-color:#fff;box-shadow:0 1px 3px #0003;transition:transform .2s ease}.rcc-toggle__input:checked+.rcc-toggle__track{background-color:var(--rcc-btn-primary-bg)}.rcc-toggle__input:checked+.rcc-toggle__track:after{transform:translate(1.375rem)}.rcc-toggle__input:disabled+.rcc-toggle__track{opacity:.5;cursor:not-allowed}.rcc-toggle__input:focus-visible+.rcc-toggle__track{outline:2px solid var(--rcc-btn-primary-bg);outline-offset:2px}.rcc-popup{display:flex;position:fixed;z-index:99999;inset:0;align-items:center;justify-content:center;padding:1rem;background-color:var(--rcc-backdrop);font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,sans-serif;color:var(--rcc-text)}.rcc-popup__card{width:100%;max-width:33.75rem;border-radius:.75rem;background-color:var(--rcc-bg);box-shadow:0 1.25rem 3.75rem #0003,0 .25rem .75rem #0000001a;padding:1.75rem 1.5rem}@media(min-width:600px){.rcc-popup__card{padding:2rem}}.rcc-popup__message{margin-bottom:1.5rem;font-size:.9375rem;line-height:1.6}.rcc-popup__actions{display:grid;gap:.5rem;grid-template-columns:1fr}@media(min-width:600px){.rcc-popup__actions{grid-template-columns:repeat(auto-fit,minmax(7.5rem,1fr))}}.rcc-settings{display:grid;width:100%;max-width:35rem;max-height:85vh;border-radius:.75rem;background-color:var(--rcc-bg);box-shadow:0 1.25rem 3.75rem #0003,0 .25rem .75rem #0000001a;grid-template-rows:auto 1fr auto}.rcc-settings__content{display:contents}.rcc-settings__header{display:flex;align-items:center;justify-content:space-between;padding:1.25rem 1.5rem;border-bottom:1px solid var(--rcc-border)}.rcc-settings__title{margin:0;font-size:1.125rem;font-weight:700}.rcc-settings__close{padding:.375rem .75rem;border:1px solid var(--rcc-border);background:transparent;font-size:.8125rem}.rcc-settings__body{overflow-y:auto;padding:1rem 1.5rem;scrollbar-width:thin;scrollbar-color:var(--rcc-scrollbar-thumb) var(--rcc-scrollbar-track)}.rcc-settings__body::-webkit-scrollbar{width:.375rem}.rcc-settings__body::-webkit-scrollbar-track{background:var(--rcc-scrollbar-track)}.rcc-settings__body::-webkit-scrollbar-thumb{border-radius:.1875rem;background:var(--rcc-scrollbar-thumb)}.rcc-settings__item{display:flex;align-items:center;justify-content:space-between;padding:.875rem 0;border-bottom:1px solid var(--rcc-border);gap:1rem}.rcc-settings__item:last-child{border-bottom:none}.rcc-settings__item__info{flex:1}.rcc-settings__item__name{display:block;font-size:.9375rem;font-weight:600;cursor:pointer}.rcc-settings__item__description{margin:.25rem 0 0;font-size:.8125rem;line-height:1.5;opacity:.75}.rcc-settings__footer{display:flex;justify-content:flex-end;padding:1rem 1.5rem;border-top:1px solid var(--rcc-border);gap:.5rem}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "react-cookie-consent-popup",
3
- "version": "1.0.5",
4
- "description": "A React cookie consent popup component with centered modal dialog, service management, script loading, and light/dark theme support",
3
+ "version": "1.5.0",
4
+ "description": "A zero-dependency React cookie consent popup component with centered modal dialog, service management, script loading, and light/dark theme support",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/Mas-HJ/react-cookie-consent-popup.git"
@@ -19,7 +19,7 @@
19
19
  "import": "./dist/index.mjs",
20
20
  "require": "./dist/index.js"
21
21
  },
22
- "./dist/*": "./dist/*"
22
+ "./styles": "./dist/styles/style.css"
23
23
  },
24
24
  "sideEffects": false,
25
25
  "files": [
@@ -28,11 +28,13 @@
28
28
  ],
29
29
  "scripts": {
30
30
  "build": "tsx esbuild.config.ts",
31
- "postbuild": "tsc --emitDeclarationOnly",
31
+ "postbuild": "tsc --emitDeclarationOnly && node -e \"const fs=require('fs');const rm=p=>{try{fs.rmSync(p,{recursive:true})}catch{}};rm('dist/core');rm('dist/config.d.ts');rm('dist/useConsentState.d.ts');rm('dist/useConsentActions.d.ts');rm('dist/useFocusTrap.d.ts');rm('dist/settings/ServiceItem.d.ts');rm('dist/settings/useSelectedServices.d.ts')\"",
32
32
  "dev": "tsx esbuild.config.ts --watch",
33
33
  "test": "jest",
34
34
  "test:dev": "jest --watch",
35
- "coverage": "jest --coverage"
35
+ "coverage": "jest --coverage",
36
+ "preversion": "yarn test",
37
+ "prepublishOnly": "yarn test && yarn build"
36
38
  },
37
39
  "keywords": [
38
40
  "react",
@@ -44,17 +46,14 @@
44
46
  "privacy",
45
47
  "cookies",
46
48
  "cookie-consent",
47
- "cookie-banner"
49
+ "cookie-banner",
50
+ "zero-dependency"
48
51
  ],
49
52
  "license": "MIT",
50
53
  "peerDependencies": {
51
54
  "react": ">=16.8.0",
52
55
  "react-dom": ">=16.8.0"
53
56
  },
54
- "dependencies": {
55
- "object-hash": "^3.0.0",
56
- "react-toggle": "^4.1.3"
57
- },
58
57
  "devDependencies": {
59
58
  "@babel/preset-env": "^7.23.9",
60
59
  "@babel/preset-react": "^7.23.3",
@@ -62,10 +61,8 @@
62
61
  "@testing-library/jest-dom": "^6.4.2",
63
62
  "@testing-library/react": "^14.2.1",
64
63
  "@types/jest": "^29.5.12",
65
- "@types/object-hash": "^3.0.6",
66
64
  "@types/react": "^18.2.55",
67
65
  "@types/react-dom": "^18.2.19",
68
- "@types/react-toggle": "^4.0.5",
69
66
  "@typescript-eslint/eslint-plugin": "^7.0.1",
70
67
  "@typescript-eslint/parser": "^7.0.1",
71
68
  "esbuild": "^0.25.0",
package/dist/config.d.ts DELETED
@@ -1,4 +0,0 @@
1
- /** Prefix used for DOM element IDs created by this library */
2
- export declare const ELEMENT_ID_PREFIX = "rcc-popup";
3
- /** Key used for persisting consent data in localStorage */
4
- export declare const STORAGE_KEY = "rcc-popup-consent";
@@ -1,2 +0,0 @@
1
- import type { ConsentService } from '../ConsentContext';
2
- export declare function activateServices(services: ConsentService[]): void;
@@ -1,2 +0,0 @@
1
- import type { CookiePattern } from '../../ConsentContext';
2
- export declare function clearCookies(patterns: CookiePattern[]): void;
@@ -1,2 +0,0 @@
1
- import type { ConsentService } from '../ConsentContext';
2
- export declare function deactivateServices(services: ConsentService[]): void;
@@ -1 +0,0 @@
1
- export declare function clearLocalStorageItems(keys: string[]): void;
@@ -1,6 +0,0 @@
1
- export interface PersistedConsent {
2
- consent: string[];
3
- hash: string;
4
- timestamp: number;
5
- }
6
- export declare function persistConsent(consent: string[], hash: string): void;
@@ -1,6 +0,0 @@
1
- interface RetrievedConsent {
2
- consent: string[];
3
- isValid: boolean;
4
- }
5
- export declare function retrieveConsent(currentHash: string): RetrievedConsent;
6
- export {};
@@ -1,2 +0,0 @@
1
- import type { ConsentService } from '../ConsentContext';
2
- export declare function reconcileServices(allServices: ConsentService[], previousConsent: string[], nextConsent: string[], hash: string): void;
@@ -1 +0,0 @@
1
- export declare function loadExternalScript(elementId: string, src: string): void;
@@ -1 +0,0 @@
1
- export declare function loadInlineScript(elementId: string, code: string): void;
@@ -1,2 +0,0 @@
1
- import type { ConsentScript } from '../../ConsentContext';
2
- export declare function loadScripts(serviceId: string, scripts: ConsentScript[]): void;
@@ -1,3 +0,0 @@
1
- import type { ConsentScript, ExternalScript, InlineScript } from '../../ConsentContext';
2
- export declare function isExternalScript(script: ConsentScript): script is ExternalScript;
3
- export declare function isInlineScript(script: ConsentScript): script is InlineScript;
@@ -1,2 +0,0 @@
1
- import type { ConsentScript } from '../../ConsentContext';
2
- export declare function unloadScripts(serviceId: string, scripts: ConsentScript[]): void;
@@ -1 +0,0 @@
1
- export declare function clearSessionStorageItems(keys: string[]): void;
@@ -1,9 +0,0 @@
1
- interface ServiceItemProps {
2
- serviceId: string;
3
- name: string;
4
- description?: string;
5
- mandatory?: boolean;
6
- onChange: (serviceId: string, enabled: boolean) => void;
7
- }
8
- export declare function ServiceItem({ serviceId, name, description, mandatory, onChange }: ServiceItemProps): import("react/jsx-runtime").JSX.Element;
9
- export {};
@@ -1,4 +0,0 @@
1
- export declare function useSelectedServices(): {
2
- selectedIds: string[];
3
- toggleService: (serviceId: string, enabled: boolean) => void;
4
- };
@@ -1,5 +0,0 @@
1
- export declare function useConsentActions(): {
2
- approveAll: () => void;
3
- approveSelected: (serviceIds: string[]) => void;
4
- declineAll: () => void;
5
- };
@@ -1,13 +0,0 @@
1
- import type { ConsentOptions } from './ConsentContext';
2
- export declare function useConsentState(options: ConsentOptions): {
3
- consent: string[];
4
- services: import("./ConsentContext").ConsentService[];
5
- theme: import("./ConsentContext").Theme;
6
- isPopupVisible: boolean;
7
- isSettingsVisible: boolean;
8
- setConsent: (nextConsent: string[]) => void;
9
- hasConsent: (serviceId: string) => boolean;
10
- showPopup: () => void;
11
- hidePopup: () => void;
12
- toggleSettings: () => void;
13
- };