@redacto.io/consent-sdk-react 7.2.0 → 8.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.
Files changed (29) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/chunk-6A665OPR.mjs +30 -0
  3. package/dist/index.mjs +85 -108
  4. package/dist/privacy-center.js +3527 -3285
  5. package/dist/privacy-center.mjs +3530 -3300
  6. package/package.json +1 -1
  7. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +3 -3
  8. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +1 -1
  9. package/src/RedactoPrivacyCenter/api/orgBranding.ts +10 -3
  10. package/src/RedactoPrivacyCenter/components/AppShell/AppShell.tsx +204 -0
  11. package/src/RedactoPrivacyCenter/components/AppShell/MobileNavDrawer.tsx +218 -0
  12. package/src/RedactoPrivacyCenter/components/AppShell/NavItem.tsx +205 -0
  13. package/src/RedactoPrivacyCenter/components/AppShell/PageHeader.tsx +107 -0
  14. package/src/RedactoPrivacyCenter/components/AppShell/SidebarNav.tsx +100 -0
  15. package/src/RedactoPrivacyCenter/components/AppShell/TopAppBar.tsx +338 -0
  16. package/src/RedactoPrivacyCenter/components/AppShell/UserMenu.tsx +222 -0
  17. package/src/RedactoPrivacyCenter/components/AppShell/hooks/useIsTruncated.ts +28 -0
  18. package/src/RedactoPrivacyCenter/components/AppShell/hooks/useKeyboardShortcut.ts +40 -0
  19. package/src/RedactoPrivacyCenter/components/AppShell/hooks/useSidebarState.ts +62 -0
  20. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +5 -27
  21. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +0 -19
  22. package/src/RedactoPrivacyCenter/context/BrandingContext.tsx +11 -3
  23. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +42 -5
  24. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +13 -2
  25. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +0 -350
  26. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +0 -489
  27. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +0 -408
  28. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +0 -1
  29. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +0 -38
@@ -1,408 +0,0 @@
1
- import { PCTitle, PCCollapse, PCTooltip } from '../../ui';
2
- import { pcStyles } from '../../styles/pcStyles';
3
- import { PiCaretDownBold } from 'react-icons/pi';
4
- import { MdLineWeight } from 'react-icons/md';
5
-
6
- const getVar = (varName: string): string => `var(${varName})`;
7
- import StatusBadge from '../../ui/StatusBadge';
8
- import { useInternalNavigation, type InternalPage } from '../../context/NavigationContext';
9
- import { useTranslation } from 'react-i18next';
10
- import type { MenuBadgeProps, SideMenuPCProps } from './types';
11
- import { badges } from './constants';
12
-
13
- const MenuBadge = ({ type, isDisabled }: MenuBadgeProps) => {
14
- const { t } = useTranslation();
15
- const badge = badges[type];
16
- if (!badge || isDisabled) return null;
17
-
18
- const isNewBadge = type === 'new';
19
- const displayText = isNewBadge ? t('badgeNew') : badge.text;
20
-
21
- return (
22
- <span
23
- style={{
24
- marginLeft: getVar('--pc-spacing-sm'),
25
- borderRadius: '9999px',
26
- paddingLeft: '0.5rem',
27
- paddingRight: '0.5rem',
28
- paddingTop: '0.125rem',
29
- paddingBottom: '0.125rem',
30
- fontSize: pcStyles.textXs.fontSize,
31
- fontWeight: pcStyles.fontWeightMedium.fontWeight,
32
- ...(isNewBadge ? {
33
- backgroundColor: getVar('--pc-bg-hover'),
34
- color: getVar('--pc-primary'),
35
- } : {
36
- backgroundColor: getVar('--pc-bg-tertiary'),
37
- color: getVar('--pc-text-secondary'),
38
- }),
39
- }}
40
- >
41
- {displayText}
42
- </span>
43
- );
44
- };
45
-
46
- export function SideMenuPC({ menuItems, isCollapsed, onItemSelect }: SideMenuPCProps) {
47
- const { currentPage, navigateTo } = useInternalNavigation();
48
-
49
- return (
50
- <div
51
- className="pc-sidebar-menu-container"
52
- style={{
53
- display: 'flex',
54
- flexDirection: 'column',
55
- paddingTop: '0.625rem',
56
- paddingBottom: '0.625rem',
57
- paddingLeft: '0.5rem',
58
- paddingRight: '0.5rem',
59
- width: '100%',
60
- }}
61
- >
62
- {menuItems.map((item, index) => {
63
- // Check if current page matches this item's href
64
- const isActive = item?.href ? currentPage === (item.href as InternalPage) : false;
65
- const pathnameExistInDropdowns = item?.dropdownItems?.filter(
66
- (dropdownItem) => {
67
- return currentPage === (dropdownItem.href as InternalPage);
68
- }
69
- );
70
- const isDropdownOpen = Boolean(pathnameExistInDropdowns?.length);
71
-
72
- // Handler for navigation
73
- const handleNavigate = (href: string) => {
74
- if (!item.isComingSoon && href) {
75
- navigateTo(href as InternalPage);
76
- onItemSelect?.();
77
- }
78
- };
79
-
80
- const renderItemWithTooltip = (content: React.ReactNode) => {
81
- if (isCollapsed) {
82
- return (
83
- <PCTooltip content={item.name} placement="right" color="invert">
84
- <div style={{ width: 'fit-content', margin: '0 auto' }}>{content}</div>
85
- </PCTooltip>
86
- );
87
- }
88
- return content;
89
- };
90
-
91
- return (
92
- <div
93
- key={item.name + '-' + index}
94
- style={{
95
- width: '100%',
96
- display: 'block',
97
- marginBottom: isCollapsed ? '0.875rem' : '0.125rem',
98
- }}
99
- >
100
- {item?.href ? (
101
- <>
102
- {item?.dropdownItems ? (
103
- <PCCollapse
104
- defaultOpen={isDropdownOpen}
105
- >
106
- {({ open, toggle }) => (
107
- <>
108
- <PCCollapse.Header
109
- open={open}
110
- toggle={toggle}
111
- >
112
- {renderItemWithTooltip(
113
- <div
114
- onClick={!item.isComingSoon ? toggle : undefined}
115
- style={{
116
- position: 'relative',
117
- marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
118
- marginBottom: isCollapsed ? 0 : '0.1875rem',
119
- ...pcStyles.flex,
120
- ...pcStyles.itemsCenter,
121
- ...pcStyles.justifyCenter,
122
- ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
123
- borderRadius: pcStyles.borderRadiusMd,
124
- padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
125
- fontWeight: pcStyles.fontWeightMedium.fontWeight,
126
- cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
127
- color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
128
- backgroundColor: isDropdownOpen && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
129
- opacity: item.isComingSoon ? 0.5 : 1,
130
- transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
131
- minHeight: isCollapsed ? '2rem' : '2.25rem',
132
- width: isCollapsed ? 'fit-content' : '100%',
133
- willChange: 'padding, justify-content',
134
- }}
135
- >
136
- <div
137
- style={{
138
- ...pcStyles.flex,
139
- ...pcStyles.itemsCenter,
140
- ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
141
- pointerEvents: item.isComingSoon ? 'none' : 'auto',
142
- transition: 'justify-content 0.3s ease, width 0.3s ease',
143
- willChange: 'justify-content, width',
144
- }}
145
- >
146
- {item?.icon && (
147
- <span
148
- style={{
149
- marginRight: isCollapsed ? 0 : getVar('--pc-spacing-sm'),
150
- display: 'inline-flex',
151
- height: '1.25rem',
152
- width: '1.25rem',
153
- ...pcStyles.itemsCenter,
154
- ...pcStyles.justifyCenter,
155
- borderRadius: pcStyles.borderRadiusMd,
156
- color: item.isComingSoon ? getVar('--pc-text-disabled') : isDropdownOpen ? getVar('--pc-primary') : getVar('--pc-text-primary'),
157
- transition: 'margin-right 0.3s ease',
158
- willChange: 'margin-right',
159
- }}
160
- >
161
- {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
162
- </span>
163
- )}
164
- <span style={{
165
- overflow: 'hidden',
166
- textOverflow: 'ellipsis',
167
- whiteSpace: 'nowrap',
168
- marginLeft: isCollapsed ? 0 : '0.5rem',
169
- opacity: isCollapsed ? 0 : 1,
170
- width: isCollapsed ? 0 : 'auto',
171
- maxWidth: isCollapsed ? 0 : '100%',
172
- transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
173
- willChange: 'opacity, width, max-width, margin-left',
174
- }}>
175
- {item.name}
176
- {item.isNew && (
177
- <MenuBadge
178
- type="new"
179
- isDisabled={item.isComingSoon}
180
- />
181
- )}
182
- </span>
183
- </div>
184
-
185
- <div style={{
186
- opacity: (!item.isComingSoon && !isCollapsed) ? 1 : 0,
187
- width: (!item.isComingSoon && !isCollapsed) ? 'auto' : 0,
188
- overflow: 'hidden',
189
- transition: 'opacity 0.3s ease, width 0.3s ease',
190
- willChange: 'opacity, width',
191
- flexShrink: 0,
192
- }}>
193
- {!item.isComingSoon && (
194
- <PiCaretDownBold
195
- strokeWidth={3}
196
- size={14}
197
- style={{
198
- transform: open ? 'rotate(0deg)' : 'rotate(-90deg)',
199
- transition: 'transform 0.2s ease',
200
- color: getVar('--pc-text-secondary'),
201
- }}
202
- />
203
- )}
204
- </div>
205
- </div>
206
- )}
207
- </PCCollapse.Header>
208
- <PCCollapse.Body open={open}>
209
- {!isCollapsed &&
210
- item?.dropdownItems?.map((dropdownItem, index) => {
211
- const isChildActive = currentPage === (dropdownItem.href as InternalPage);
212
-
213
- return renderItemWithTooltip(
214
- <div
215
- key={dropdownItem?.name + index}
216
- onClick={() => handleNavigate(dropdownItem.href)}
217
- style={{
218
- marginLeft: '1.25rem',
219
- marginRight: '0.625rem',
220
- marginTop: index === 0 ? '0.1875rem' : '0.125rem',
221
- marginBottom: '0.125rem',
222
- ...pcStyles.flex,
223
- ...pcStyles.itemsCenter,
224
- borderRadius: pcStyles.borderRadiusMd,
225
- padding: '0.4375rem 0.75rem',
226
- fontWeight: pcStyles.fontWeightMedium.fontWeight,
227
- textTransform: 'capitalize',
228
- cursor: dropdownItem.isComingSoon ? 'not-allowed' : 'pointer',
229
- color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-secondary'),
230
- backgroundColor: isChildActive && !dropdownItem.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
231
- opacity: dropdownItem.isComingSoon ? 0.5 : 1,
232
- transition: 'color 0.2s, background-color 0.2s',
233
- minHeight: '2rem',
234
- }}
235
- >
236
- <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter }}>
237
- <div style={{ ...pcStyles.flex, minWidth: 0, flex: 1, ...pcStyles.itemsCenter, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
238
- {dropdownItem?.icon && (
239
- <span
240
- style={{
241
- marginRight: getVar('--pc-spacing-sm'),
242
- display: 'inline-flex',
243
- height: '1.125rem',
244
- width: '1.125rem',
245
- ...pcStyles.itemsCenter,
246
- ...pcStyles.justifyCenter,
247
- borderRadius: pcStyles.borderRadiusMd,
248
- color: dropdownItem.isComingSoon ? getVar('--pc-text-disabled') : isChildActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
249
- }}
250
- >
251
- {dropdownItem.icon}
252
- </span>
253
- )}
254
- <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
255
- {dropdownItem?.name}
256
- </span>
257
- {dropdownItem.isNew && (
258
- <MenuBadge
259
- type="new"
260
- isDisabled={dropdownItem.isComingSoon}
261
- />
262
- )}
263
- </div>
264
- <div style={{ marginLeft: getVar('--pc-spacing-md'), ...pcStyles.flex, ...pcStyles.itemsCenter }}>
265
- {dropdownItem?.badge?.length ? (
266
- <StatusBadge status={dropdownItem?.badge} className="pc-status-badge" />
267
- ) : (
268
- <div style={{ width: '1rem' }} />
269
- )}
270
- </div>
271
- </div>
272
- </div>
273
- );
274
- })}
275
- </PCCollapse.Body>
276
- </>
277
- )}
278
- </PCCollapse>
279
- ) : (
280
- renderItemWithTooltip(
281
- <div
282
- key={item.name + '-' + index}
283
- onClick={() => item.href && handleNavigate(item.href)}
284
- style={{
285
- position: 'relative',
286
- marginTop: isCollapsed ? 0 : index === 0 ? 0 : '0.1875rem',
287
- marginBottom: isCollapsed ? 0 : '0.1875rem',
288
- ...pcStyles.flex,
289
- ...pcStyles.itemsCenter,
290
- ...pcStyles.justifyCenter,
291
- ...(isCollapsed ? {} : { ...pcStyles.justifyBetween }),
292
- borderRadius: pcStyles.borderRadiusMd,
293
- padding: isCollapsed ? '0.375rem' : '0.5rem 0.625rem',
294
- fontWeight: pcStyles.fontWeightMedium.fontWeight,
295
- textTransform: 'capitalize',
296
- cursor: item.isComingSoon ? 'not-allowed' : 'pointer',
297
- color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
298
- backgroundColor: isActive && !item.isComingSoon ? getVar('--pc-bg-hover') : 'transparent',
299
- opacity: item.isComingSoon ? 0.5 : 1,
300
- transition: 'color 0.2s ease, background-color 0.2s ease, padding 0.3s ease, justify-content 0.3s ease',
301
- minHeight: isCollapsed ? '2rem' : '2.25rem',
302
- width: isCollapsed ? 'fit-content' : '100%',
303
- willChange: 'padding, justify-content',
304
- }}
305
- >
306
- <div style={{
307
- ...pcStyles.flex,
308
- ...pcStyles.itemsCenter,
309
- ...(isCollapsed ? { justifyContent: 'center', width: '100%' } : {}),
310
- transition: 'justify-content 0.3s ease, width 0.3s ease',
311
- willChange: 'justify-content, width',
312
- }}>
313
- {item?.icon && (
314
- <span
315
- style={{
316
- marginRight: isCollapsed ? 0 : getVar('--pc-spacing-sm'),
317
- display: 'inline-flex',
318
- height: '1.25rem',
319
- width: '1.25rem',
320
- ...pcStyles.itemsCenter,
321
- ...pcStyles.justifyCenter,
322
- borderRadius: pcStyles.borderRadiusMd,
323
- color: item.isComingSoon ? getVar('--pc-text-disabled') : isActive ? getVar('--pc-primary') : getVar('--pc-text-primary'),
324
- transition: 'margin-right 0.3s ease',
325
- willChange: 'margin-right',
326
- }}
327
- >
328
- {item.isBatch ? <MdLineWeight size={24} /> : item?.icon}
329
- </span>
330
- )}
331
- <span style={{
332
- overflow: 'hidden',
333
- textOverflow: 'ellipsis',
334
- whiteSpace: 'nowrap',
335
- marginLeft: isCollapsed ? 0 : '0.5rem',
336
- opacity: isCollapsed ? 0 : 1,
337
- width: isCollapsed ? 0 : 'auto',
338
- maxWidth: isCollapsed ? 0 : '100%',
339
- transition: 'opacity 0.3s ease, width 0.3s ease, max-width 0.3s ease, margin-left 0.3s ease',
340
- willChange: 'opacity, width, max-width, margin-left',
341
- }}>
342
- {item.name}
343
- {item.isNew && <MenuBadge type="new" />}
344
- </span>
345
- </div>
346
- <div style={{
347
- opacity: (!isCollapsed && item?.badge?.length) ? 1 : 0,
348
- width: (!isCollapsed && item?.badge?.length) ? 'auto' : 0,
349
- overflow: 'hidden',
350
- transition: 'opacity 0.3s ease, width 0.3s ease',
351
- willChange: 'opacity, width',
352
- flexShrink: 0,
353
- }}>
354
- {item?.badge?.length ? <StatusBadge status={item?.badge} className="pc-status-badge" /> : null}
355
- </div>
356
- </div>
357
- )
358
- )}
359
- </>
360
- ) : (
361
- <>
362
- {isCollapsed ? (
363
- <div
364
- style={{
365
- marginLeft: '0.75rem',
366
- marginRight: '0.75rem',
367
- marginTop: index !== 0 ? '1rem' : '0.5rem',
368
- marginBottom: '0.5rem',
369
- height: '1px',
370
- }}
371
- >
372
- <hr style={{
373
- width: '100%',
374
- backgroundColor: getVar('--pc-border-color'),
375
- border: 'none',
376
- height: '1px',
377
- margin: 0,
378
- }} />
379
- </div>
380
- ) : (
381
- <PCTitle
382
- as="h6"
383
- style={{
384
- marginTop: index !== 0 ? '1.25rem' : '0.5rem',
385
- marginBottom: '0.375rem',
386
- overflow: 'hidden',
387
- textOverflow: 'ellipsis',
388
- whiteSpace: 'nowrap',
389
- paddingLeft: '1rem',
390
- paddingRight: '1rem',
391
- fontSize: pcStyles.textXs.fontSize,
392
- fontWeight: pcStyles.fontWeightMedium.fontWeight,
393
- textTransform: 'uppercase',
394
- letterSpacing: '0.1em',
395
- color: getVar('--pc-text-secondary'),
396
- }}
397
- >
398
- {item.name}
399
- </PCTitle>
400
- )}
401
- </>
402
- )}
403
- </div>
404
- );
405
- })}
406
- </div>
407
- );
408
- }
@@ -1 +0,0 @@
1
- export { menuItemsPC } from './constants';
@@ -1,38 +0,0 @@
1
- import { LOCAL_STORAGE_KEYS_TO_REMOVE } from './constants';
2
-
3
- export const clearLocalStorage = () => {
4
- try {
5
- LOCAL_STORAGE_KEYS_TO_REMOVE.forEach((key) => {
6
- sessionStorage.removeItem(key);
7
- });
8
- } catch {
9
- // Silently ignore if sessionStorage is unavailable
10
- }
11
- };
12
-
13
- // Alias for clarity - both functions clear sessionStorage
14
- export const clearSessionStorage = clearLocalStorage;
15
-
16
- export const generateOrgSlug = (
17
- slug: string | undefined,
18
- name: string | undefined
19
- ): string | undefined => {
20
- if (slug) return slug;
21
- if (!name) return undefined;
22
-
23
- const normalizedName = name.toLowerCase();
24
- const parts = normalizedName.split(' ').filter((part) => part.length > 0);
25
- return parts.length > 0 ? parts.join('-') : undefined;
26
- };
27
-
28
- /**
29
- * Convert a slug to a readable organization name
30
- * e.g., "my-org-name" -> "My Org Name"
31
- */
32
- export const slugToOrgName = (slug: string): string => {
33
- if (!slug) return '';
34
- return slug
35
- .split('-')
36
- .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
37
- .join(' ');
38
- };