@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.
- package/CHANGELOG.md +17 -0
- package/dist/privacy-center.d.mts +2 -4
- package/dist/privacy-center.d.ts +2 -4
- package/dist/privacy-center.js +1876 -2079
- package/dist/privacy-center.mjs +1470 -1679
- package/package.json +1 -1
- package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +6 -10
- package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +18 -17
- package/src/RedactoPrivacyCenter/api/orgBranding.ts +9 -0
- package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +16 -14
- package/src/RedactoPrivacyCenter/components/Form.tsx +3 -3
- package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +1 -1
- package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +0 -4
- package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +16 -2
- package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +44 -0
- package/src/RedactoPrivacyCenter/lib/types.ts +22 -0
- package/src/RedactoPrivacyCenter/styles/applyBrandingTheme.ts +66 -0
- package/src/RedactoPrivacyCenter/styles/injectStyles.ts +3 -111
- package/src/RedactoPrivacyCenter/styles/pcStyles.ts +3 -2
- package/src/RedactoPrivacyCenter/styles/variables.css +1 -43
- package/src/RedactoPrivacyCenter/ui/Footer.tsx +4 -10
- package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +0 -85
- package/src/RedactoPrivacyCenter/context/theme.tsx +0 -127
- package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +0 -70
|
@@ -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
|
-
}
|