From af9eefd11ea382369e9a4a2d3ed4c6ae3ed1acac Mon Sep 17 00:00:00 2001 From: Atanas Dimitrov Date: Thu, 2 May 2024 14:46:51 +0300 Subject: [PATCH] Enhance useBiteConsent with theme support This commit adds theme support to the useBiteConsent hook, allowing users to specify a theme object containing color configurations. It updates the implementation to use the theme colors for the consent banner. --- README.md | 31 +++++++++++++++++++-- example/src/App.jsx | 14 ++++++++-- src/BiteConsent.tsx | 20 +++++++++----- src/ThemeContext.tsx | 63 ++++++++++++++++++++++++++++++++++-------- src/useBiteConsent.tsx | 6 ++-- 5 files changed, 108 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index c6e66d7..703139b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Bite Consent -Bite Consent is a lightweight React library for managing cookie consent banners with customizable descriptions and actions. It provides an easy way to implement cookie consent functionality in your React applications while ensuring compliance with privacy regulations. +Bite Consent is a lightweight React library for managing cookie consent views with customizable descriptions and actions. It provides an easy way to implement cookie consent functionality in your React applications while ensuring compliance with privacy regulations. ![Screenshot 2024-04-27 at 18 43 55](https://github.com/Seishin/bite-consent/assets/324076/a4df7470-86b4-49a8-89be-73549c410695) @@ -35,7 +35,10 @@ import { useBiteConsent } from 'bite-consent' function App() { const { consentCookie, show, revoke } = useBiteConsent({ - privacyPolicyUrl: 'https://example.com/privacy' + privacyPolicyUrl: 'https://example.com/privacy', + theme: { + mode: 'auto' + } }) return ( @@ -61,7 +64,7 @@ export default App | `visibility` | `auto` \| `visible` \| `hidden` | `auto` | Specifies the visibility of the consent banner. `auto` displays the view based on the availability of the consent cookie, whereas `visible` displays and `hidden` hides it. | | `position` | `Position` \| `CustomPosition` | `bottom-left` | Sets the position of the consent view on the page. See below for details. | | `cookieConfig` | `CookieConfig` | - | Configuration options for consent cookies. See below for details. | -| `themeMode` | `ThemeMode` | `auto` | Sets the theme mode. When `auto` is passed, the widget will automatically observe the theme mode. | +| `theme` | `Theme` | - | Theme object containing color configurations for the consent view. See below for details. | | `onAccept` | `function` | `() => void` | A function to be called when the user accepts the cookie policy. | ### Position @@ -94,6 +97,28 @@ export default App | `secure` | `boolean` | Indicates if the cookie should only be sent over secure connections. | | `sameSite` | `Strict` \| `Lax` \| `None` | The SameSite attribute of the consent cookie. | +### Theme + +The `theme` object allows customization of the colors used in the consent banner. + +| Property | Type | Description | +| -------- | ----------- | ------------------------------------ | +| `mode` | `ThemeMode` | The theme mode. | +| `light` | `ColorSet` | Color configurations for light mode. | +| `dark` | `ColorSet` | Color configurations for dark mode. | + +ColorSet Properties: + +- `background`: Background color of the consent banner. +- `text`: Text color of the consent banner. +- `shadow`: Box shadow of the consent banner. +- `primaryActionBackground`: Background color of primary action buttons. +- `primaryActionHoverBackground`: Background color of primary action buttons on hover. +- `primaryActionText`: Text color of primary action buttons. +- `secondaryActionBackground`: Background color of secondary action buttons. +- `secondaryActionText`: Text color of secondary action buttons. +- `secondaryActionHoverBackground`: Background color of secondary action buttons on hover. + ## License Bite Consent is released under the MIT License. diff --git a/example/src/App.jsx b/example/src/App.jsx index 71a21d3..42eb766 100644 --- a/example/src/App.jsx +++ b/example/src/App.jsx @@ -3,7 +3,10 @@ import React, { useEffect } from 'react' function App() { const { consentCookie, show, revoke } = useBiteConsent({ - privacyPolicyUrl: 'https://example.com/privacy' + privacyPolicyUrl: 'https://example.com/privacy', + theme: { + mode: 'auto' + } }) useEffect(() => { @@ -13,7 +16,14 @@ function App() { return (

Cookie Consent

{consentCookie ?? 'undefined'}

diff --git a/src/BiteConsent.tsx b/src/BiteConsent.tsx index 771f5f5..5166514 100644 --- a/src/BiteConsent.tsx +++ b/src/BiteConsent.tsx @@ -3,6 +3,7 @@ import React, { useEffect } from 'react' import CookieConfig from './CookieConfig' import { Cookies } from './Illustrations' import Position, { CustomPosition, isCustomPosition } from './Position' +import { useTheme } from './ThemeContext' const CONSENT_COOKIE_NAME = 'cookie_consent' @@ -17,6 +18,9 @@ interface Props { const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = 'bottom-left', cookieConfig, onAccept }: Props) => { const [visible, setVisible] = React.useState() + const theme = useTheme() + + const colorSet = theme.mode === 'light' ? theme.light : theme.dark useEffect(() => { if (typeof window === 'undefined' || typeof document === 'undefined') return @@ -93,8 +97,9 @@ const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = ' position: 'fixed', ...getPosition(), zIndex: 9999, - backgroundColor: '#ffffff', - boxShadow: '0 0 10px rgba(0, 0, 0, 0.1)', + backgroundColor: colorSet.background, + color: colorSet.text, + boxShadow: colorSet.shadow, padding: '1rem', width: '18rem', maxHeight: '18rem', @@ -132,12 +137,13 @@ const BiteConsent = ({ privacyPolicyUrl, text, visibility = 'auto', position = ' { - const [mode, setMode] = useState(presetMode ?? 'auto') +const ThemeProvider = ({ theme: providedTheme, children }: { theme?: Theme; children: ReactNode }) => { + const [theme, setTheme] = useState({ + mode: providedTheme?.mode ?? 'auto', + light: { ...defaultTheme.light, ...providedTheme?.light }, + dark: { ...defaultTheme.dark, ...providedTheme?.dark } + }) useEffect(() => { - if (typeof window === 'undefined' || typeof document === 'undefined' || mode !== 'auto') return + if (typeof window === 'undefined' || typeof document === 'undefined' || theme.mode !== 'auto') return const themeQuery = window.matchMedia('(prefers-color-scheme: dark)') themeQuery.addEventListener('change', (e) => { - setMode(e.matches ? 'dark' : 'light') + setTheme({ ...theme, mode: e.matches ? 'dark' : 'light' }) }) return () => { themeQuery.removeEventListener('change', (e) => { - setMode(e.matches ? 'dark' : 'light') + setTheme({ ...theme, mode: e.matches ? 'dark' : 'light' }) }) } - }, [setMode]) + }, [setTheme]) - return {children} + return {children} } const useTheme = () => useContext(ThemeContext) export default ThemeProvider -export { ThemeMode as Theme, useTheme } +export { Theme, useTheme } diff --git a/src/useBiteConsent.tsx b/src/useBiteConsent.tsx index f2c8ab2..15904be 100644 --- a/src/useBiteConsent.tsx +++ b/src/useBiteConsent.tsx @@ -19,12 +19,12 @@ type BiteConsentOptions = { visibility?: 'auto' | 'visible' | 'hidden' position?: Position | CustomPosition cookieConfig?: CookieConfig - themeMode?: Theme + theme?: Theme onAccept?: () => void } const useBiteConsent = (options: BiteConsentOptions) => { - const { privacyPolicyUrl, text, visibility, position, cookieConfig, themeMode, onAccept } = options + const { privacyPolicyUrl, text, visibility, position, cookieConfig, theme, onAccept } = options const consentCookie = document.cookie.split(';').find((cookie) => cookie.trim().startsWith(cookieConfig?.name ?? CONSENT_COOKIE_NAME)) @@ -46,7 +46,7 @@ const useBiteConsent = (options: BiteConsentOptions) => { const shadowRoot = root.attachShadow({ mode: 'open' }) createRoot(shadowRoot).render( - +