@redacto.io/consent-sdk-react 6.0.0 → 7.0.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.
@@ -1,85 +0,0 @@
1
- import { useEffect, useRef } from 'react';
2
- import { IoSunnyOutline, IoMoonOutline } from 'react-icons/io5';
3
- import { useTheme } from '../context/theme';
4
-
5
- export default function ThemeTransitionOverlay() {
6
- const { isTransitioning, targetTheme } = useTheme();
7
- const overlayRef = useRef<HTMLDivElement>(null);
8
- const iconRef = useRef<HTMLDivElement>(null);
9
-
10
- useEffect(() => {
11
- if (isTransitioning && overlayRef.current && iconRef.current) {
12
- const overlay = overlayRef.current;
13
- const icon = iconRef.current;
14
-
15
- // Trigger animation
16
- overlay.style.display = 'flex';
17
- // Force reflow to ensure display change is applied
18
- overlay.offsetHeight;
19
- overlay.style.opacity = '1';
20
- icon.style.animation = 'none';
21
- // Force reflow
22
- icon.offsetHeight;
23
- icon.style.animation = '';
24
-
25
- const timeout = setTimeout(() => {
26
- if (overlay) {
27
- overlay.style.opacity = '0';
28
- setTimeout(() => {
29
- if (overlay) {
30
- overlay.style.display = 'none';
31
- }
32
- }, 300);
33
- }
34
- }, 1000);
35
-
36
- return () => clearTimeout(timeout);
37
- }
38
- }, [isTransitioning, targetTheme]);
39
-
40
- if (!targetTheme) return null;
41
-
42
- const Icon = targetTheme === 'light' ? IoSunnyOutline : IoMoonOutline;
43
- // Sun: golden yellow, Moon: soft blue/white
44
- const iconColor = targetTheme === 'light' ? '#fbbf24' : '#c7d2fe';
45
-
46
- return (
47
- <div
48
- ref={overlayRef}
49
- className="pc-theme-transition-overlay"
50
- style={{
51
- display: 'none',
52
- opacity: 0,
53
- position: 'fixed',
54
- top: 0,
55
- left: 0,
56
- right: 0,
57
- bottom: 0,
58
- pointerEvents: 'none',
59
- zIndex: 9999,
60
- alignItems: 'center',
61
- justifyContent: 'center',
62
- transition: 'opacity 0.3s ease',
63
- }}
64
- aria-hidden="true"
65
- >
66
- <div
67
- ref={iconRef}
68
- className={`pc-theme-transition-icon pc-theme-transition-${targetTheme}`}
69
- style={{
70
- position: 'absolute',
71
- bottom: '-150px',
72
- left: '50%',
73
- transform: 'translateX(-50%)',
74
- fontSize: '120px',
75
- color: iconColor,
76
- display: 'flex',
77
- alignItems: 'center',
78
- justifyContent: 'center',
79
- }}
80
- >
81
- <Icon size={120} />
82
- </div>
83
- </div>
84
- );
85
- }
@@ -1,127 +0,0 @@
1
- import {
2
- createContext,
3
- useContext,
4
- useState,
5
- useEffect,
6
- useCallback,
7
- type ReactNode,
8
- } from 'react';
9
-
10
- export type Theme = 'light' | 'dark';
11
-
12
- type ThemeContextType = {
13
- theme: Theme;
14
- toggleTheme: () => void;
15
- setTheme: (theme: Theme) => void;
16
- isTransitioning: boolean;
17
- targetTheme: Theme | null;
18
- };
19
-
20
- const THEME_STORAGE_KEY = 'privacy-center-theme';
21
-
22
- const ThemeContext = createContext<ThemeContextType | null>(null);
23
-
24
- function getInitialTheme(defaultTheme?: Theme): Theme {
25
- // If defaultTheme is provided, always use it (takes precedence over sessionStorage)
26
- // This ensures that when a parent component explicitly sets a theme, it's respected
27
- if (defaultTheme !== undefined) {
28
- return defaultTheme;
29
- }
30
-
31
- // Otherwise, check sessionStorage first
32
- if (typeof window !== 'undefined') {
33
- const stored = sessionStorage.getItem(THEME_STORAGE_KEY);
34
- if (stored === 'light' || stored === 'dark') {
35
- return stored;
36
- }
37
-
38
- // Fallback to system preference
39
- if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
40
- return 'dark';
41
- }
42
- }
43
-
44
- return 'light';
45
- }
46
-
47
- type ThemeProviderProps = {
48
- children: ReactNode;
49
- defaultTheme?: Theme;
50
- };
51
-
52
- export const ThemeProvider = ({ children, defaultTheme }: ThemeProviderProps) => {
53
- // Initialize with defaultTheme if provided, otherwise use stored/system preference
54
- // When defaultTheme is provided, it takes precedence over sessionStorage
55
- const [theme, setThemeState] = useState<Theme>(() => getInitialTheme(defaultTheme));
56
- const [isTransitioning, setIsTransitioning] = useState(false);
57
- const [targetTheme, setTargetTheme] = useState<Theme | null>(null);
58
-
59
- // Ensure defaultTheme is applied on mount if provided
60
- // This handles the case where sessionStorage had a different value
61
- useEffect(() => {
62
- if (defaultTheme !== undefined) {
63
- setThemeState(defaultTheme);
64
- }
65
- }, []); // Only run on mount
66
-
67
- const setTheme = useCallback((newTheme: Theme) => {
68
- if (newTheme !== theme) {
69
- setIsTransitioning(true);
70
- setTargetTheme(newTheme);
71
- setThemeState(newTheme);
72
- // Reset transition state after animation completes
73
- setTimeout(() => {
74
- setIsTransitioning(false);
75
- setTargetTheme(null);
76
- }, 1000);
77
- }
78
- }, [theme]);
79
-
80
- const toggleTheme = useCallback(() => {
81
- setIsTransitioning(true);
82
- const newTheme = theme === 'light' ? 'dark' : 'light';
83
- setTargetTheme(newTheme);
84
- setThemeState(newTheme);
85
- // Reset transition state after animation completes
86
- setTimeout(() => {
87
- setIsTransitioning(false);
88
- setTargetTheme(null);
89
- }, 1000);
90
- }, [theme]);
91
-
92
- // Apply theme to document and persist to sessionStorage
93
- useEffect(() => {
94
- document.documentElement.setAttribute('data-theme', theme);
95
- sessionStorage.setItem(THEME_STORAGE_KEY, theme);
96
- }, [theme]);
97
-
98
- // Listen for system theme changes
99
- useEffect(() => {
100
- const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
101
-
102
- const handleChange = (e: MediaQueryListEvent) => {
103
- // Only auto-switch if user hasn't explicitly set a preference
104
- const stored = sessionStorage.getItem(THEME_STORAGE_KEY);
105
- if (!stored) {
106
- setThemeState(e.matches ? 'dark' : 'light');
107
- }
108
- };
109
-
110
- mediaQuery.addEventListener('change', handleChange);
111
- return () => mediaQuery.removeEventListener('change', handleChange);
112
- }, []);
113
-
114
- return (
115
- <ThemeContext.Provider value={{ theme, toggleTheme, setTheme, isTransitioning, targetTheme }}>
116
- {children}
117
- </ThemeContext.Provider>
118
- );
119
- };
120
-
121
- export const useTheme = (): ThemeContextType => {
122
- const context = useContext(ThemeContext);
123
- if (!context) {
124
- throw new Error('useTheme must be used within a ThemeProvider');
125
- }
126
- return context;
127
- };
@@ -1,70 +0,0 @@
1
- import { useRef } from 'react';
2
- import { useTranslation } from 'react-i18next';
3
- import { PCActionIcon } from './index';
4
- import { IoSunnyOutline, IoMoonOutline } from 'react-icons/io5';
5
- import { useTheme } from '../context/theme';
6
- import { PCTooltip } from './PCTooltip';
7
-
8
- const getVar = (varName: string): string => `var(${varName})`;
9
-
10
- type ThemeToggleProps = {
11
- className?: string;
12
- };
13
-
14
- export default function ThemeToggle({ className }: ThemeToggleProps) {
15
- const { t } = useTranslation();
16
- const { theme, toggleTheme } = useTheme();
17
- const buttonRef = useRef<HTMLButtonElement>(null);
18
- const tooltipLabel =
19
- theme === 'light' ? t('switchToDarkMode') : t('switchToLightMode');
20
-
21
- const handleClick = () => {
22
- // Capture click position for animation
23
- let position: { x: number; y: number };
24
-
25
- if (buttonRef.current) {
26
- const rect = buttonRef.current.getBoundingClientRect();
27
- position = {
28
- x: rect.left + rect.width / 2,
29
- y: rect.top + rect.height / 2
30
- };
31
- } else {
32
- // Fallback to center
33
- position = {
34
- x: window.innerWidth / 2,
35
- y: window.innerHeight / 2
36
- };
37
- }
38
-
39
- // Store position globally for ThemeTransitionOverlay to access
40
- if (typeof window !== 'undefined') {
41
- (window as any).__pcThemeTransitionPosition = position;
42
- }
43
-
44
- toggleTheme();
45
- };
46
-
47
- return (
48
- <PCTooltip content={tooltipLabel} placement="bottom" color="invert">
49
- <div style={{ display: 'inline-flex' }}>
50
- <PCActionIcon
51
- ref={buttonRef}
52
- variant="default"
53
- className={className}
54
- onClick={handleClick}
55
- aria-label={tooltipLabel}
56
- style={{
57
- color: getVar('--pc-text-primary'),
58
- transition: 'color 0.3s ease, transform 0.2s ease',
59
- }}
60
- >
61
- {theme === 'light' ? (
62
- <IoMoonOutline size={16} />
63
- ) : (
64
- <IoSunnyOutline size={16} />
65
- )}
66
- </PCActionIcon>
67
- </div>
68
- </PCTooltip>
69
- );
70
- }