@redacto.io/consent-sdk-react 3.4.0 → 3.6.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 (155) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +176 -0
  3. package/dist/index.d.mts +51 -4
  4. package/dist/index.d.ts +51 -4
  5. package/dist/index.js +1912 -345
  6. package/dist/index.mjs +1911 -345
  7. package/dist/privacy-center.d.mts +566 -0
  8. package/dist/privacy-center.d.ts +566 -0
  9. package/dist/privacy-center.js +17174 -0
  10. package/dist/privacy-center.mjs +17136 -0
  11. package/package.json +47 -3
  12. package/src/RedactoNoticeConsent/RedactoNoticeConsent.test.tsx +201 -118
  13. package/src/RedactoNoticeConsent/RedactoNoticeConsent.tsx +1194 -93
  14. package/src/RedactoNoticeConsent/api/index.ts +337 -246
  15. package/src/RedactoNoticeConsent/api/types.ts +140 -41
  16. package/src/RedactoNoticeConsent/components/DigiLockerCallback.tsx +602 -0
  17. package/src/RedactoNoticeConsent/components/index.ts +7 -0
  18. package/src/RedactoNoticeConsent/injectStyles.ts +11 -0
  19. package/src/RedactoNoticeConsent/language-codes.ts +61 -0
  20. package/src/RedactoNoticeConsent/services/storage.ts +122 -0
  21. package/src/RedactoNoticeConsent/types.ts +2 -0
  22. package/src/RedactoNoticeConsentInline/api/index.ts +26 -64
  23. package/src/RedactoNoticeConsentInline/api/types.ts +0 -25
  24. package/src/RedactoPrivacyCenter/PrivacyCenterActivityPage.tsx +19 -0
  25. package/src/RedactoPrivacyCenter/PrivacyCenterConsentManagerPage.tsx +17 -0
  26. package/src/RedactoPrivacyCenter/PrivacyCenterFormPage.tsx +79 -0
  27. package/src/RedactoPrivacyCenter/PrivacyCenterHomePage.tsx +27 -0
  28. package/src/RedactoPrivacyCenter/PrivacyCenterLayout.tsx +142 -0
  29. package/src/RedactoPrivacyCenter/RedactoPrivacyCenter.tsx +51 -0
  30. package/src/RedactoPrivacyCenter/api/actions.ts +444 -0
  31. package/src/RedactoPrivacyCenter/api/client.ts +413 -0
  32. package/src/RedactoPrivacyCenter/api/config.ts +33 -0
  33. package/src/RedactoPrivacyCenter/api/fetcher.ts +181 -0
  34. package/src/RedactoPrivacyCenter/api/upload.ts +93 -0
  35. package/src/RedactoPrivacyCenter/assets/character.svg +31 -0
  36. package/src/RedactoPrivacyCenter/assets/fileAttach.svg +9 -0
  37. package/src/RedactoPrivacyCenter/assets/greenTick.svg +4 -0
  38. package/src/RedactoPrivacyCenter/assets/mail.svg +3 -0
  39. package/src/RedactoPrivacyCenter/assets/poweredByRedacto.svg +9 -0
  40. package/src/RedactoPrivacyCenter/assets/sentImg.svg +9 -0
  41. package/src/RedactoPrivacyCenter/assets/uploadFileIcon.svg +9 -0
  42. package/src/RedactoPrivacyCenter/components/ActivitiesList.tsx +272 -0
  43. package/src/RedactoPrivacyCenter/components/CaseDetailsModal.tsx +468 -0
  44. package/src/RedactoPrivacyCenter/components/CaseHistory.tsx +433 -0
  45. package/src/RedactoPrivacyCenter/components/ConsentManager.tsx +726 -0
  46. package/src/RedactoPrivacyCenter/components/ConsentManagerColumns.tsx +239 -0
  47. package/src/RedactoPrivacyCenter/components/ConsentManagerConstants.ts +7 -0
  48. package/src/RedactoPrivacyCenter/components/ConsentManagerTableSkeleton.tsx +155 -0
  49. package/src/RedactoPrivacyCenter/components/DataRequest/CorrectionDataSection.tsx +189 -0
  50. package/src/RedactoPrivacyCenter/components/DataRequest/GrievanceDataSection.tsx +118 -0
  51. package/src/RedactoPrivacyCenter/components/DataRequest/NominationDataSection.tsx +66 -0
  52. package/src/RedactoPrivacyCenter/components/DataRequest/PurposeSelectionSection.tsx +305 -0
  53. package/src/RedactoPrivacyCenter/components/Form.tsx +1455 -0
  54. package/src/RedactoPrivacyCenter/components/GrievanceTable.tsx +176 -0
  55. package/src/RedactoPrivacyCenter/components/GrievanceTablePagination.tsx +330 -0
  56. package/src/RedactoPrivacyCenter/components/ModifyConsentModal.tsx +329 -0
  57. package/src/RedactoPrivacyCenter/components/Pagination.tsx +140 -0
  58. package/src/RedactoPrivacyCenter/components/PrivacyCenterLayout.tsx +302 -0
  59. package/src/RedactoPrivacyCenter/components/RequestSubmit.tsx +218 -0
  60. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideBarPC.tsx +475 -0
  61. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/SideMenuPC.tsx +412 -0
  62. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/constants.tsx +42 -0
  63. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/menuItemsPC.tsx +1 -0
  64. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/types.ts +40 -0
  65. package/src/RedactoPrivacyCenter/components/SideMenuBarPC/utils.ts +38 -0
  66. package/src/RedactoPrivacyCenter/components/ThemeTransitionOverlay.tsx +85 -0
  67. package/src/RedactoPrivacyCenter/components/activitiesColumns.tsx +141 -0
  68. package/src/RedactoPrivacyCenter/components/caseHistoryColumns.tsx +179 -0
  69. package/src/RedactoPrivacyCenter/components/constant.ts +20 -0
  70. package/src/RedactoPrivacyCenter/components/types.ts +68 -0
  71. package/src/RedactoPrivacyCenter/components/utils.tsx +10 -0
  72. package/src/RedactoPrivacyCenter/config/routes.ts +9 -0
  73. package/src/RedactoPrivacyCenter/config/table.ts +25 -0
  74. package/src/RedactoPrivacyCenter/context/AuthContext.tsx +123 -0
  75. package/src/RedactoPrivacyCenter/context/BaseUrlContext.tsx +44 -0
  76. package/src/RedactoPrivacyCenter/context/LocaleContext.tsx +11 -0
  77. package/src/RedactoPrivacyCenter/context/NavigationContext.tsx +64 -0
  78. package/src/RedactoPrivacyCenter/context/OrganizationContext.tsx +30 -0
  79. package/src/RedactoPrivacyCenter/context/PageChromeContext.tsx +60 -0
  80. package/src/RedactoPrivacyCenter/context/theme.tsx +127 -0
  81. package/src/RedactoPrivacyCenter/lib/api.ts +468 -0
  82. package/src/RedactoPrivacyCenter/lib/consent-manager/utils.tsx +63 -0
  83. package/src/RedactoPrivacyCenter/lib/constants.ts +116 -0
  84. package/src/RedactoPrivacyCenter/lib/i18n.ts +81 -0
  85. package/src/RedactoPrivacyCenter/lib/locales.ts +105 -0
  86. package/src/RedactoPrivacyCenter/lib/queries.ts +30 -0
  87. package/src/RedactoPrivacyCenter/lib/types.ts +346 -0
  88. package/src/RedactoPrivacyCenter/lib/useCurrentLanguage.ts +37 -0
  89. package/src/RedactoPrivacyCenter/lib/utils.ts +625 -0
  90. package/src/RedactoPrivacyCenter/locales/as/translation.json +188 -0
  91. package/src/RedactoPrivacyCenter/locales/bho/translation.json +188 -0
  92. package/src/RedactoPrivacyCenter/locales/bn/translation.json +188 -0
  93. package/src/RedactoPrivacyCenter/locales/brx/translation.json +188 -0
  94. package/src/RedactoPrivacyCenter/locales/doi/translation.json +188 -0
  95. package/src/RedactoPrivacyCenter/locales/en/translation.json +188 -0
  96. package/src/RedactoPrivacyCenter/locales/gom/translation.json +188 -0
  97. package/src/RedactoPrivacyCenter/locales/gu/translation.json +188 -0
  98. package/src/RedactoPrivacyCenter/locales/hi/translation.json +188 -0
  99. package/src/RedactoPrivacyCenter/locales/kn/translation.json +188 -0
  100. package/src/RedactoPrivacyCenter/locales/ks/translation.json +188 -0
  101. package/src/RedactoPrivacyCenter/locales/mai/translation.json +188 -0
  102. package/src/RedactoPrivacyCenter/locales/ml/translation.json +188 -0
  103. package/src/RedactoPrivacyCenter/locales/mni-Mtei/translation.json +188 -0
  104. package/src/RedactoPrivacyCenter/locales/mr/translation.json +188 -0
  105. package/src/RedactoPrivacyCenter/locales/ne/translation.json +188 -0
  106. package/src/RedactoPrivacyCenter/locales/or/translation.json +188 -0
  107. package/src/RedactoPrivacyCenter/locales/pa/translation.json +188 -0
  108. package/src/RedactoPrivacyCenter/locales/sa/translation.json +188 -0
  109. package/src/RedactoPrivacyCenter/locales/sat/translation.json +188 -0
  110. package/src/RedactoPrivacyCenter/locales/sd/translation.json +188 -0
  111. package/src/RedactoPrivacyCenter/locales/ta/translation.json +188 -0
  112. package/src/RedactoPrivacyCenter/locales/te/translation.json +188 -0
  113. package/src/RedactoPrivacyCenter/locales/ur/translation.json +188 -0
  114. package/src/RedactoPrivacyCenter/styles/base.css +117 -0
  115. package/src/RedactoPrivacyCenter/styles/injectStyles.ts +1000 -0
  116. package/src/RedactoPrivacyCenter/styles/pcStyles.ts +819 -0
  117. package/src/RedactoPrivacyCenter/styles/responsive.ts +138 -0
  118. package/src/RedactoPrivacyCenter/styles/variables.css +133 -0
  119. package/src/RedactoPrivacyCenter/table/AdvancedTable.tsx +365 -0
  120. package/src/RedactoPrivacyCenter/table/AdvancedTablePagination.tsx +479 -0
  121. package/src/RedactoPrivacyCenter/table/index.ts +2 -0
  122. package/src/RedactoPrivacyCenter/types/images.d.ts +29 -0
  123. package/src/RedactoPrivacyCenter/ui/Footer.tsx +73 -0
  124. package/src/RedactoPrivacyCenter/ui/FormBlockWrapper.tsx +54 -0
  125. package/src/RedactoPrivacyCenter/ui/PCAccordion.tsx +87 -0
  126. package/src/RedactoPrivacyCenter/ui/PCActionIcon.tsx +47 -0
  127. package/src/RedactoPrivacyCenter/ui/PCAvatar.tsx +69 -0
  128. package/src/RedactoPrivacyCenter/ui/PCBadge.tsx +48 -0
  129. package/src/RedactoPrivacyCenter/ui/PCButton.tsx +77 -0
  130. package/src/RedactoPrivacyCenter/ui/PCCheckbox.tsx +55 -0
  131. package/src/RedactoPrivacyCenter/ui/PCCollapse.tsx +97 -0
  132. package/src/RedactoPrivacyCenter/ui/PCEmpty.tsx +35 -0
  133. package/src/RedactoPrivacyCenter/ui/PCInput.tsx +128 -0
  134. package/src/RedactoPrivacyCenter/ui/PCLoader.tsx +30 -0
  135. package/src/RedactoPrivacyCenter/ui/PCModal.tsx +104 -0
  136. package/src/RedactoPrivacyCenter/ui/PCPopover.tsx +175 -0
  137. package/src/RedactoPrivacyCenter/ui/PCSelect.tsx +381 -0
  138. package/src/RedactoPrivacyCenter/ui/PCTable.tsx +137 -0
  139. package/src/RedactoPrivacyCenter/ui/PCText.tsx +39 -0
  140. package/src/RedactoPrivacyCenter/ui/PCTextarea.tsx +43 -0
  141. package/src/RedactoPrivacyCenter/ui/PCTitle.tsx +37 -0
  142. package/src/RedactoPrivacyCenter/ui/PCTooltip.tsx +119 -0
  143. package/src/RedactoPrivacyCenter/ui/SimpleBar.tsx +25 -0
  144. package/src/RedactoPrivacyCenter/ui/StatusBadge.tsx +55 -0
  145. package/src/RedactoPrivacyCenter/ui/ThemeToggle.tsx +70 -0
  146. package/src/RedactoPrivacyCenter/ui/index.ts +18 -0
  147. package/src/RedactoPrivacyCenter/utils/cn.ts +28 -0
  148. package/src/RedactoPrivacyCenter/utils/date.ts +48 -0
  149. package/src/RedactoPrivacyCenter/utils/status.ts +111 -0
  150. package/src/RedactoPrivacyCenter/utils/toast.ts +130 -0
  151. package/src/index.ts +10 -0
  152. package/src/privacy-center.ts +107 -0
  153. package/tsconfig.json +2 -0
  154. package/tsup.config.ts +16 -1
  155. package/src/utils/isLegacyApi.ts +0 -12
@@ -0,0 +1,137 @@
1
+ import { type ReactNode, type CSSProperties, type HTMLAttributes, createContext, useContext } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ interface TableContextValue {
5
+ variant?: 'modern' | 'minimal';
6
+ }
7
+
8
+ const TableContext = createContext<TableContextValue>({});
9
+
10
+ // Main Table component
11
+ export interface PCTableProps extends HTMLAttributes<HTMLTableElement> {
12
+ children: ReactNode;
13
+ variant?: 'modern' | 'minimal';
14
+ className?: string;
15
+ style?: CSSProperties;
16
+ }
17
+
18
+ export function PCTable({ children, variant = 'modern', className = '', style = {}, ...props }: PCTableProps) {
19
+ const tableStyle: CSSProperties = {
20
+ ...pcStyles.table,
21
+ ...style,
22
+ };
23
+
24
+ return (
25
+ <TableContext.Provider value={{ variant }}>
26
+ <table className={className} style={tableStyle} {...props}>
27
+ {children}
28
+ </table>
29
+ </TableContext.Provider>
30
+ );
31
+ }
32
+
33
+ // Header component
34
+ export interface PCTableHeaderProps extends HTMLAttributes<HTMLTableSectionElement> {
35
+ children: ReactNode;
36
+ className?: string;
37
+ style?: CSSProperties;
38
+ }
39
+
40
+ export function PCTableHeader({ children, className = '', style = {}, ...props }: PCTableHeaderProps) {
41
+ const headerStyle: CSSProperties = {
42
+ ...pcStyles.tableHeader,
43
+ ...style,
44
+ };
45
+
46
+ return (
47
+ <thead className={className} style={headerStyle} {...props}>
48
+ {children}
49
+ </thead>
50
+ );
51
+ }
52
+
53
+ // Body component
54
+ export interface PCTableBodyProps extends HTMLAttributes<HTMLTableSectionElement> {
55
+ children: ReactNode;
56
+ className?: string;
57
+ style?: CSSProperties;
58
+ }
59
+
60
+ export function PCTableBody({ children, className = '', style = {}, ...props }: PCTableBodyProps) {
61
+ const bodyStyle: CSSProperties = {
62
+ ...style,
63
+ };
64
+
65
+ return (
66
+ <tbody className={className} style={bodyStyle} {...props}>
67
+ {children}
68
+ </tbody>
69
+ );
70
+ }
71
+
72
+ // Row component
73
+ export interface PCTableRowProps extends HTMLAttributes<HTMLTableRowElement> {
74
+ children: ReactNode;
75
+ className?: string;
76
+ style?: CSSProperties;
77
+ }
78
+
79
+ export function PCTableRow({ children, className = '', style = {}, ...props }: PCTableRowProps) {
80
+ const rowStyle: CSSProperties = {
81
+ ...pcStyles.tableRow,
82
+ ...style,
83
+ };
84
+
85
+ return (
86
+ <tr className={`pc-table-row ${className}`} style={rowStyle} {...props}>
87
+ {children}
88
+ </tr>
89
+ );
90
+ }
91
+
92
+ // Header Cell component
93
+ export interface PCTableHeadProps extends HTMLAttributes<HTMLTableCellElement> {
94
+ children: ReactNode;
95
+ className?: string;
96
+ style?: CSSProperties;
97
+ }
98
+
99
+ export function PCTableHead({ children, className = '', style = {}, ...props }: PCTableHeadProps) {
100
+ const headStyle: CSSProperties = {
101
+ ...pcStyles.tableHeaderCell,
102
+ ...style,
103
+ };
104
+
105
+ return (
106
+ <th className={className} style={headStyle} {...props}>
107
+ {children}
108
+ </th>
109
+ );
110
+ }
111
+
112
+ // Cell component
113
+ export interface PCTableCellProps extends HTMLAttributes<HTMLTableCellElement> {
114
+ children: ReactNode;
115
+ className?: string;
116
+ style?: CSSProperties;
117
+ }
118
+
119
+ export function PCTableCell({ children, className = '', style = {}, ...props }: PCTableCellProps) {
120
+ const cellStyle: CSSProperties = {
121
+ ...pcStyles.tableCell,
122
+ ...style,
123
+ };
124
+
125
+ return (
126
+ <td className={className} style={cellStyle} {...props}>
127
+ {children}
128
+ </td>
129
+ );
130
+ }
131
+
132
+ // Compound component pattern
133
+ PCTable.Header = PCTableHeader;
134
+ PCTable.Body = PCTableBody;
135
+ PCTable.Row = PCTableRow;
136
+ PCTable.Head = PCTableHead;
137
+ PCTable.Cell = PCTableCell;
@@ -0,0 +1,39 @@
1
+ import { type ReactNode, type CSSProperties } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export interface PCTextProps {
5
+ children: ReactNode;
6
+ className?: string;
7
+ style?: CSSProperties;
8
+ as?: 'p' | 'span' | 'div' | 'label';
9
+ size?: 'xs' | 'sm' | 'base' | 'lg' | 'xl' | '2xl' | '3xl';
10
+ weight?: 'normal' | 'medium' | 'semibold' | 'bold';
11
+ color?: 'primary' | 'secondary' | 'tertiary' | 'inverse' | 'disabled';
12
+ align?: 'left' | 'center' | 'right';
13
+ }
14
+
15
+ export function PCText({
16
+ children,
17
+ className = '',
18
+ style = {},
19
+ as: Component = 'p',
20
+ size = 'base',
21
+ weight = 'normal',
22
+ color = 'primary',
23
+ align,
24
+ }: PCTextProps) {
25
+ const baseStyle: CSSProperties = {
26
+ ...pcStyles.text,
27
+ ...pcStyles[`text${size.charAt(0).toUpperCase() + size.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
28
+ ...pcStyles[`fontWeight${weight.charAt(0).toUpperCase() + weight.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
29
+ ...pcStyles[`text${color.charAt(0).toUpperCase() + color.slice(1)}` as keyof typeof pcStyles] as CSSProperties,
30
+ ...(align && pcStyles[`text${align.charAt(0).toUpperCase() + align.slice(1)}` as keyof typeof pcStyles] as CSSProperties),
31
+ ...style,
32
+ };
33
+
34
+ return (
35
+ <Component className={className} style={baseStyle}>
36
+ {children}
37
+ </Component>
38
+ );
39
+ }
@@ -0,0 +1,43 @@
1
+ import { type ReactNode, type CSSProperties, type TextareaHTMLAttributes } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export interface PCTextareaProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, 'style' | 'size'> {
5
+ className?: string;
6
+ style?: CSSProperties;
7
+ label?: ReactNode;
8
+ error?: boolean;
9
+ size?: 'sm' | 'md' | 'lg';
10
+ }
11
+
12
+ export function PCTextarea({
13
+ className = '',
14
+ style = {},
15
+ label,
16
+ error = false,
17
+ size = 'md',
18
+ ...props
19
+ }: PCTextareaProps) {
20
+ const textareaStyle: CSSProperties = {
21
+ ...pcStyles.textarea,
22
+ ...(size === 'sm' && pcStyles.inputSm),
23
+ ...(size === 'lg' && pcStyles.inputLg),
24
+ ...(error && pcStyles.inputError),
25
+ ...(props.disabled && pcStyles.inputDisabled),
26
+ ...style,
27
+ };
28
+
29
+ const wrapperStyle: CSSProperties = {
30
+ ...pcStyles.inputWrapper,
31
+ };
32
+
33
+ return (
34
+ <div className={className} style={wrapperStyle}>
35
+ {label && <label style={pcStyles.inputLabel}>{label}</label>}
36
+ <textarea
37
+ className="pc-textarea"
38
+ style={textareaStyle}
39
+ {...props}
40
+ />
41
+ </div>
42
+ );
43
+ }
@@ -0,0 +1,37 @@
1
+ import { type ReactNode, type CSSProperties } from 'react';
2
+ import { pcStyles } from '../styles/pcStyles';
3
+
4
+ export interface PCTitleProps {
5
+ children: ReactNode;
6
+ className?: string;
7
+ style?: CSSProperties;
8
+ as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6';
9
+ }
10
+
11
+ export function PCTitle({
12
+ children,
13
+ className = '',
14
+ style = {},
15
+ as: Component = 'h3',
16
+ }: PCTitleProps) {
17
+ const sizeMap: Record<string, keyof typeof pcStyles> = {
18
+ h1: 'titleH1',
19
+ h2: 'titleH2',
20
+ h3: 'titleH3',
21
+ h4: 'titleH4',
22
+ h5: 'titleH5',
23
+ h6: 'titleH6',
24
+ };
25
+
26
+ const baseStyle: CSSProperties = {
27
+ ...pcStyles.title,
28
+ ...(pcStyles[sizeMap[Component] as keyof typeof pcStyles] as CSSProperties),
29
+ ...style,
30
+ };
31
+
32
+ return (
33
+ <Component className={className} style={baseStyle}>
34
+ {children}
35
+ </Component>
36
+ );
37
+ }
@@ -0,0 +1,119 @@
1
+ import { type ReactNode, type CSSProperties, useState, useRef, useEffect } from 'react';
2
+ import { createPortal } from 'react-dom';
3
+ import { pcStyles } from '../styles/pcStyles';
4
+
5
+ export interface PCTooltipProps {
6
+ content: ReactNode;
7
+ children: ReactNode;
8
+ placement?: 'top' | 'bottom' | 'left' | 'right';
9
+ className?: string;
10
+ style?: CSSProperties;
11
+ color?: 'default' | 'invert';
12
+ }
13
+
14
+ export function PCTooltip({
15
+ content,
16
+ children,
17
+ placement = 'top',
18
+ className = '',
19
+ style = {},
20
+ color = 'default',
21
+ }: PCTooltipProps) {
22
+ const [isVisible, setIsVisible] = useState(false);
23
+ const [position, setPosition] = useState({ top: 0, left: 0 });
24
+ const triggerRef = useRef<HTMLDivElement>(null);
25
+ const tooltipRef = useRef<HTMLDivElement>(null);
26
+
27
+ const updatePosition = () => {
28
+ if (!triggerRef.current || !tooltipRef.current) return;
29
+
30
+ const triggerRect = triggerRef.current.getBoundingClientRect();
31
+ const tooltipRect = tooltipRef.current.getBoundingClientRect();
32
+ const gap = 2;
33
+ const viewportGutter = 12;
34
+
35
+ let top = 0;
36
+ let left = 0;
37
+
38
+ switch (placement) {
39
+ case 'top':
40
+ top = triggerRect.top - tooltipRect.height - gap;
41
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
42
+ break;
43
+ case 'bottom':
44
+ top = triggerRect.bottom + gap;
45
+ left = triggerRect.left + (triggerRect.width - tooltipRect.width) / 2;
46
+ break;
47
+ case 'left':
48
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
49
+ left = triggerRect.left - tooltipRect.width - gap;
50
+ break;
51
+ case 'right':
52
+ top = triggerRect.top + (triggerRect.height - tooltipRect.height) / 2;
53
+ left = triggerRect.right + gap;
54
+ break;
55
+ }
56
+
57
+ const maxLeft = window.innerWidth - tooltipRect.width - viewportGutter;
58
+ const maxTop = window.innerHeight - tooltipRect.height - viewportGutter;
59
+
60
+ left = Math.min(Math.max(left, viewportGutter), Math.max(viewportGutter, maxLeft));
61
+ top = Math.min(Math.max(top, viewportGutter), Math.max(viewportGutter, maxTop));
62
+
63
+ setPosition({ top, left });
64
+ };
65
+
66
+ useEffect(() => {
67
+ if (isVisible) {
68
+ updatePosition();
69
+ window.addEventListener('scroll', updatePosition, true);
70
+ window.addEventListener('resize', updatePosition);
71
+ return () => {
72
+ window.removeEventListener('scroll', updatePosition, true);
73
+ window.removeEventListener('resize', updatePosition);
74
+ };
75
+ }
76
+ }, [isVisible, placement]);
77
+
78
+ const tooltipStyle: CSSProperties = {
79
+ ...pcStyles.tooltip,
80
+ ...(color === 'invert' && {
81
+ backgroundColor: '#ffffff',
82
+ color: '#1f2937',
83
+ border: '1px solid #e5e7eb',
84
+ boxShadow: '0 8px 18px rgba(15, 23, 42, 0.10)',
85
+ }),
86
+ position: 'fixed',
87
+ top: `${position.top}px`,
88
+ left: `${position.left}px`,
89
+ display: isVisible ? 'block' : 'none',
90
+ padding: '0.375rem 0.625rem',
91
+ fontSize: '0.8125rem',
92
+ lineHeight: 1.2,
93
+ borderRadius: '0.75rem',
94
+ ...style,
95
+ };
96
+
97
+ return (
98
+ <>
99
+ <div
100
+ ref={triggerRef}
101
+ onMouseEnter={() => setIsVisible(true)}
102
+ onMouseLeave={() => setIsVisible(false)}
103
+ style={{ display: 'inline-block', width: '100%' }}
104
+ >
105
+ {children}
106
+ </div>
107
+ {isVisible && typeof document !== 'undefined' && createPortal(
108
+ <div
109
+ ref={tooltipRef}
110
+ className={`pc-tooltip ${className}`}
111
+ style={tooltipStyle}
112
+ >
113
+ {content}
114
+ </div>,
115
+ document.body
116
+ )}
117
+ </>
118
+ );
119
+ }
@@ -0,0 +1,25 @@
1
+ import { pcStyles } from '../styles/pcStyles';
2
+
3
+ type SimpleBarProps = {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ style?: React.CSSProperties;
7
+ };
8
+
9
+ export default function SimpleBar({
10
+ children,
11
+ className,
12
+ style,
13
+ }: SimpleBarProps) {
14
+ return (
15
+ <div
16
+ className={`pc-scrollbar ${className || ''}`}
17
+ style={{
18
+ ...pcStyles.overflowYAuto,
19
+ ...style,
20
+ }}
21
+ >
22
+ {children}
23
+ </div>
24
+ );
25
+ }
@@ -0,0 +1,55 @@
1
+ import { PCBadge } from './index';
2
+
3
+ type StatusBadgeProps = {
4
+ status?: 'success' | 'warning' | 'error' | 'info' | 'pending' | string;
5
+ children?: React.ReactNode;
6
+ className?: string;
7
+ variant?: 'filled' | 'outlined';
8
+ };
9
+
10
+ const predefinedStatusMap: Record<string, 'success' | 'warning' | 'error' | 'info' | 'pending'> = {
11
+ success: 'success',
12
+ warning: 'warning',
13
+ error: 'error',
14
+ info: 'info',
15
+ pending: 'pending',
16
+ active: 'success',
17
+ completed: 'success',
18
+ processing: 'warning',
19
+ in_progress: 'warning',
20
+ rejected: 'error',
21
+ declined: 'error',
22
+ expired: 'warning',
23
+ suspended: 'error',
24
+ revoked: 'error',
25
+ withdrawn: 'error',
26
+ };
27
+
28
+ export default function StatusBadge({
29
+ status = 'info',
30
+ children,
31
+ className,
32
+ variant = 'filled',
33
+ }: StatusBadgeProps) {
34
+ // Check if it's a predefined status
35
+ const normalizedStatus = status.toLowerCase();
36
+ const mappedStatus = predefinedStatusMap[normalizedStatus] || status;
37
+
38
+ let badgeVariant: 'success' | 'error' | 'warning' | 'info' | 'secondary' = 'secondary';
39
+
40
+ if (mappedStatus === 'success') {
41
+ badgeVariant = 'success';
42
+ } else if (mappedStatus === 'error') {
43
+ badgeVariant = 'error';
44
+ } else if (mappedStatus === 'warning') {
45
+ badgeVariant = 'warning';
46
+ } else if (mappedStatus === 'info') {
47
+ badgeVariant = 'info';
48
+ }
49
+
50
+ return (
51
+ <PCBadge variant={badgeVariant} className={className}>
52
+ {children || status}
53
+ </PCBadge>
54
+ );
55
+ }
@@ -0,0 +1,70 @@
1
+ import { useRef } from 'react';
2
+ import { PCActionIcon } from './index';
3
+ import { IoSunnyOutline, IoMoonOutline } from 'react-icons/io5';
4
+ import { useTheme } from '../context/theme';
5
+ import { PCTooltip } from './PCTooltip';
6
+
7
+ const getVar = (varName: string): string => `var(${varName})`;
8
+
9
+ type ThemeToggleProps = {
10
+ className?: string;
11
+ };
12
+
13
+ export default function ThemeToggle({ className }: ThemeToggleProps) {
14
+ const { theme, toggleTheme } = useTheme();
15
+ const buttonRef = useRef<HTMLButtonElement>(null);
16
+ const tooltipLabel =
17
+ theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode';
18
+
19
+ const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
20
+ // Capture click position for animation
21
+ let position: { x: number; y: number };
22
+
23
+ if (buttonRef.current) {
24
+ const rect = buttonRef.current.getBoundingClientRect();
25
+ position = {
26
+ x: rect.left + rect.width / 2,
27
+ y: rect.top + rect.height / 2
28
+ };
29
+ } else {
30
+ // Fallback to center
31
+ position = {
32
+ x: window.innerWidth / 2,
33
+ y: window.innerHeight / 2
34
+ };
35
+ }
36
+
37
+ // Store position globally for ThemeTransitionOverlay to access
38
+ if (typeof window !== 'undefined') {
39
+ (window as any).__pcThemeTransitionPosition = position;
40
+ }
41
+
42
+ toggleTheme();
43
+ };
44
+
45
+ return (
46
+ <PCTooltip content={tooltipLabel} placement="bottom" color="invert">
47
+ <div style={{ display: 'inline-flex' }}>
48
+ <PCActionIcon
49
+ ref={buttonRef}
50
+ variant="default"
51
+ className={className}
52
+ onClick={handleClick}
53
+ aria-label={tooltipLabel}
54
+ style={{
55
+ width: '32px',
56
+ height: '32px',
57
+ color: getVar('--pc-text-primary'),
58
+ transition: 'color 0.3s ease, transform 0.2s ease',
59
+ }}
60
+ >
61
+ {theme === 'light' ? (
62
+ <IoMoonOutline size={18} />
63
+ ) : (
64
+ <IoSunnyOutline size={18} />
65
+ )}
66
+ </PCActionIcon>
67
+ </div>
68
+ </PCTooltip>
69
+ );
70
+ }
@@ -0,0 +1,18 @@
1
+ export { PCText, type PCTextProps } from './PCText';
2
+ export { PCTitle, type PCTitleProps } from './PCTitle';
3
+ export { PCButton, type PCButtonProps } from './PCButton';
4
+ export { PCActionIcon, type PCActionIconProps } from './PCActionIcon';
5
+ export { PCInput, type PCInputProps } from './PCInput';
6
+ export { PCTextarea, type PCTextareaProps } from './PCTextarea';
7
+ export { PCCheckbox, type PCCheckboxProps } from './PCCheckbox';
8
+ export { PCSelect, type PCSelectProps, type SelectOption } from './PCSelect';
9
+ export { PCModal, type PCModalProps } from './PCModal';
10
+ export { PCTable, PCTableHeader, PCTableBody, PCTableRow, PCTableHead, PCTableCell, type PCTableProps, type PCTableHeaderProps, type PCTableBodyProps, type PCTableRowProps, type PCTableHeadProps, type PCTableCellProps } from './PCTable';
11
+ export { PCLoader, type PCLoaderProps } from './PCLoader';
12
+ export { PCBadge, type PCBadgeProps } from './PCBadge';
13
+ export { PCTooltip, type PCTooltipProps } from './PCTooltip';
14
+ export { PCPopover, type PCPopoverProps } from './PCPopover';
15
+ export { PCAccordion, PCAccordionHeader, PCAccordionBody, type PCAccordionProps, type PCAccordionHeaderProps, type PCAccordionBodyProps } from './PCAccordion';
16
+ export { PCCollapse, PCCollapseHeader, PCCollapseBody, type PCCollapseProps, type PCCollapseHeaderProps, type PCCollapseBodyProps } from './PCCollapse';
17
+ export { PCAvatar, type PCAvatarProps } from './PCAvatar';
18
+ export { PCEmpty, type PCEmptyProps } from './PCEmpty';
@@ -0,0 +1,28 @@
1
+ // Simple className utility - replaces clsx and tailwind-merge
2
+ // Accepts strings, objects, arrays, and undefined/null values
3
+ type ClassValue = string | number | boolean | undefined | null | Record<string, boolean> | ClassValue[];
4
+
5
+ export function cn(...inputs: ClassValue[]): string {
6
+ const classes: string[] = [];
7
+
8
+ for (const input of inputs) {
9
+ if (!input) continue;
10
+
11
+ if (typeof input === 'string') {
12
+ classes.push(input);
13
+ } else if (typeof input === 'number') {
14
+ classes.push(String(input));
15
+ } else if (Array.isArray(input)) {
16
+ const inner = cn(...input);
17
+ if (inner) classes.push(inner);
18
+ } else if (typeof input === 'object') {
19
+ for (const key in input) {
20
+ if (input[key]) {
21
+ classes.push(key);
22
+ }
23
+ }
24
+ }
25
+ }
26
+
27
+ return classes.filter(Boolean).join(' ');
28
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Unified date formatting utilities for privacy center app
3
+ */
4
+
5
+ export type FormatDateParams = {
6
+ date: string | Date;
7
+ };
8
+
9
+ /**
10
+ * Format date for display in consent records (with time)
11
+ * Format: "January 15, 2025 at 2:30 PM"
12
+ */
13
+ export const formatDate = ({ date }: FormatDateParams): string => {
14
+ return new Date(date).toLocaleDateString('en-US', {
15
+ year: 'numeric',
16
+ month: 'long',
17
+ day: 'numeric',
18
+ hour: '2-digit',
19
+ minute: '2-digit',
20
+ });
21
+ };
22
+
23
+ /**
24
+ * Format date for display in case history (short format)
25
+ * Format: "15 Jan 2025"
26
+ */
27
+ export const formatDateShort = (dateString: string): string => {
28
+ if (!dateString) return '-';
29
+ return new Date(dateString).toLocaleDateString('en-GB', {
30
+ day: 'numeric',
31
+ month: 'short',
32
+ year: 'numeric'
33
+ });
34
+ };
35
+
36
+ /**
37
+ * Format date time for consent manager (with day/month/year)
38
+ * Format: "15 Jan 2025"
39
+ */
40
+ export const formatDateTime = (timestamp: string): string => {
41
+ const date = new Date(timestamp);
42
+ const options: Intl.DateTimeFormatOptions = {
43
+ day: '2-digit',
44
+ month: 'short',
45
+ year: 'numeric',
46
+ };
47
+ return date.toLocaleString('en-US', options);
48
+ };