pixel-react 1.1.0 → 1.1.2

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 (75) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/lib/components/Charts/DashboardDonutChart/DashboardDonutChart.d.ts +5 -0
  3. package/lib/components/Charts/DashboardDonutChart/DashboardDonutChart.stories.d.ts +7 -0
  4. package/lib/components/Charts/DashboardDonutChart/index.d.ts +1 -0
  5. package/lib/components/Charts/DashboardDonutChart/types.d.ts +21 -0
  6. package/lib/components/Charts/PieChart/PieChart.d.ts +5 -0
  7. package/lib/components/Charts/PieChart/PieChart.stories.d.ts +7 -0
  8. package/lib/components/Charts/PieChart/index.d.ts +1 -0
  9. package/lib/components/Charts/PieChart/types.d.ts +27 -0
  10. package/lib/components/FF_Captcha/Recaptcha.d.ts +5 -0
  11. package/lib/components/FF_Captcha/captcha.stories.d.ts +8 -0
  12. package/lib/components/FF_Captcha/types.d.ts +20 -0
  13. package/lib/components/MultiSelect/MultiSelect.d.ts +1 -1
  14. package/lib/components/MultiSelect/MultiSelectTypes.d.ts +1 -1
  15. package/lib/components/NLPInput/NlpInput.d.ts +4 -0
  16. package/lib/components/NLPInput/NlpInput.stories.d.ts +7 -0
  17. package/lib/components/NLPInput/components/NlpDropDown/NlpDropDownType.d.ts +19 -0
  18. package/lib/components/NLPInput/components/NlpDropDown/NlpDropdown.d.ts +4 -0
  19. package/lib/components/NLPInput/index.d.ts +1 -0
  20. package/lib/components/NLPInput/type.d.ts +70 -0
  21. package/lib/components/Table/Table.d.ts +1 -1
  22. package/lib/index.d.ts +96 -19
  23. package/lib/index.esm.js +2951 -249
  24. package/lib/index.esm.js.map +1 -1
  25. package/lib/index.js +2953 -247
  26. package/lib/index.js.map +1 -1
  27. package/lib/tsconfig.tsbuildinfo +1 -1
  28. package/lib/utils/getEncryptedData/getEncryptedData.d.ts +2 -0
  29. package/lib/utils/getEncryptedData/getEncryptedData.stories.d.ts +6 -0
  30. package/package.json +6 -2
  31. package/src/StyleGuide/ColorPalette/ColorPalette.tsx +2 -4
  32. package/src/StyleGuide/ColorPalette/colorPaletteList.ts +95 -20
  33. package/src/assets/Themes/BaseTheme.scss +2 -3
  34. package/src/assets/Themes/DarkTheme.scss +9 -8
  35. package/src/assets/icons/wswb_delete_icon.svg +4 -0
  36. package/src/assets/icons/wswb_plus_icon.svg +5 -0
  37. package/src/components/Charts/DashboardDonutChart/DashboardDonutChart.scss +145 -0
  38. package/src/components/Charts/DashboardDonutChart/DashboardDonutChart.stories.tsx +52 -0
  39. package/src/components/Charts/DashboardDonutChart/DashboardDonutChart.tsx +335 -0
  40. package/src/components/Charts/DashboardDonutChart/index.ts +1 -0
  41. package/src/components/Charts/DashboardDonutChart/types.ts +33 -0
  42. package/src/components/Charts/PieChart/PieChart.scss +39 -0
  43. package/src/components/Charts/PieChart/PieChart.stories.tsx +46 -0
  44. package/src/components/Charts/PieChart/PieChart.tsx +193 -0
  45. package/src/components/Charts/PieChart/index.ts +1 -0
  46. package/src/components/Charts/PieChart/types.ts +28 -0
  47. package/src/components/FF_Captcha/Recaptcha.scss +11 -0
  48. package/src/components/FF_Captcha/Recaptcha.tsx +41 -0
  49. package/src/components/FF_Captcha/captcha.stories.tsx +40 -0
  50. package/src/components/FF_Captcha/index.ts +0 -0
  51. package/src/components/FF_Captcha/types.ts +22 -0
  52. package/src/components/Icon/iconList.ts +6 -0
  53. package/src/components/Modal/modal.scss +1 -1
  54. package/src/components/MultiSelect/MultiSelect.stories.tsx +2 -3
  55. package/src/components/MultiSelect/MultiSelect.tsx +35 -23
  56. package/src/components/MultiSelect/MultiSelectTypes.ts +1 -1
  57. package/src/components/NLPInput/NLPInput.scss +246 -0
  58. package/src/components/NLPInput/NlpInput.stories.tsx +136 -0
  59. package/src/components/NLPInput/NlpInput.tsx +374 -0
  60. package/src/components/NLPInput/components/NlpDropDown/NlpDropDownType.ts +60 -0
  61. package/src/components/NLPInput/components/NlpDropDown/NlpDropdown.scss +83 -0
  62. package/src/components/NLPInput/components/NlpDropDown/NlpDropdown.tsx +180 -0
  63. package/src/components/NLPInput/index.ts +1 -0
  64. package/src/components/NLPInput/type.tsx +124 -0
  65. package/src/components/Table/Table.scss +5 -0
  66. package/src/components/Table/Table.stories.tsx +12 -0
  67. package/src/components/Table/Table.tsx +25 -14
  68. package/src/components/TextArea/Textarea.scss +1 -1
  69. package/src/index.ts +8 -1
  70. package/src/utils/getEncryptedData/getEncryptedData.stories.tsx +55 -0
  71. package/src/utils/getEncryptedData/getEncryptedData.ts +10 -0
  72. package/lib/components/AddButton/AddButton.d.ts +0 -5
  73. package/lib/components/AddButton/AddButton.stories.d.ts +0 -6
  74. package/lib/components/AddButton/index.d.ts +0 -1
  75. package/lib/components/AddButton/types.d.ts +0 -4
@@ -0,0 +1,335 @@
1
+ import React, { useState } from 'react';
2
+ import { DashboardDonutChartProps, ChartItem, LegendType } from './types';
3
+ import Typography from '../../Typography';
4
+ import './DashBoardDonutChart.scss';
5
+ import classNames from 'classnames';
6
+
7
+ const calculateArc = (
8
+ x: number,
9
+ y: number,
10
+ radius: number,
11
+ startAngle: number,
12
+ endAngle: number
13
+ ): string => {
14
+ const startX = x + radius * Math.cos(startAngle);
15
+ const startY = y + radius * Math.sin(startAngle);
16
+ const endX = x + radius * Math.cos(endAngle);
17
+ const endY = y + radius * Math.sin(endAngle);
18
+ const largeArcFlag = endAngle - startAngle > Math.PI ? 1 : 0;
19
+
20
+ return `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY}`;
21
+ };
22
+
23
+ const colorMapping = [
24
+ 'var(--status-success-text-color)',
25
+ 'var(--status-rejected-text-color)',
26
+ 'var(--status-warning-text-color)',
27
+ 'var(--status-skipped-text-color)',
28
+ 'var(--brand-color)',
29
+ ];
30
+
31
+ const DashboardDonutChart: React.FC<DashboardDonutChartProps> = ({
32
+ radius = 60,
33
+ lineWidth = 15,
34
+ statusValues = [],
35
+ gapAngle = 0,
36
+ legendDetailsType = '',
37
+ isLegendDetails = true,
38
+ legendType = 'numberLegend',
39
+ showOnlyLabel = false,
40
+ }) => {
41
+ const [hoveredItemIndex, setHoveredItemIndex] = useState<number | null>(null);
42
+ const [tooltipPosition, setTooltipPosition] = useState({ x: 0, y: 0 });
43
+ const [showTooltip, setShowTooltip] = useState<boolean>(false);
44
+ const total = statusValues?.reduce((acc, { value }) => acc + value, 0);
45
+ const nonZeroValues = statusValues?.filter(({ value }) => value > 0);
46
+
47
+ // Calculate angles and gaps
48
+ const TOTAL_GAP_ANGLE = gapAngle * nonZeroValues.length;
49
+ let remainingAngle = 2 * Math.PI - TOTAL_GAP_ANGLE;
50
+ let currentAngle = Math.PI / 2;
51
+
52
+ const MIN_PERCENTAGE = 1;
53
+ const MIN_ANGLE = (MIN_PERCENTAGE / 100) * (2 * Math.PI);
54
+ let minAngleTotal = 0;
55
+
56
+ // Adjust for small angles
57
+ nonZeroValues.forEach(({ value }) => {
58
+ const valuePercentage = value / total;
59
+ const angle = Math.max(valuePercentage * (2 * Math.PI), MIN_ANGLE);
60
+ minAngleTotal += angle;
61
+ remainingAngle -= angle;
62
+ });
63
+
64
+ const handleMouseEnter = (itemIndex: number) => {
65
+ setHoveredItemIndex(itemIndex);
66
+ setShowTooltip(true);
67
+ };
68
+ const handleMouseLeave = () => {
69
+ setTooltipPosition({ x: 0, y: 0 });
70
+ setHoveredItemIndex(null);
71
+ setShowTooltip(false);
72
+ };
73
+ const handleMouseMove = (event: React.MouseEvent) => {
74
+ setTooltipPosition({
75
+ x: event.clientX + 10,
76
+ y: event.clientY + 10,
77
+ });
78
+ };
79
+
80
+ const SVG_PADDING = 4;
81
+ const DONUT_SVG_SIZE = (radius + lineWidth) * 2 + SVG_PADDING * 2;
82
+
83
+ const renderArc = (chartItem: ChartItem, endAngle: number, i: number) => {
84
+ const isFullCircle = nonZeroValues.length === 1;
85
+
86
+ // Full circle handling
87
+ const foregroundArcPath = isFullCircle
88
+ ? calculateArc(0, 0, radius, 0, 2 * Math.PI)
89
+ : calculateArc(0, 0, radius, currentAngle, endAngle);
90
+
91
+ currentAngle = endAngle + gapAngle;
92
+
93
+ return (
94
+ <g key={i}>
95
+ {/* Main arc */}
96
+ <path
97
+ d={foregroundArcPath}
98
+ fill="none"
99
+ stroke={
100
+ chartItem?.color
101
+ ? chartItem.color
102
+ : colorMapping[i % colorMapping.length]
103
+ }
104
+ strokeWidth={lineWidth}
105
+ onMouseEnter={() => handleMouseEnter(i)}
106
+ onMouseLeave={handleMouseLeave}
107
+ strokeOpacity={0.8}
108
+ onMouseMove={handleMouseMove}
109
+ />
110
+ </g>
111
+ );
112
+ };
113
+
114
+ const renderTooltip = () => {
115
+ return (
116
+ <div
117
+ className="ff-donut-chart-tooltip"
118
+ style={{
119
+ left: tooltipPosition.x,
120
+ top: tooltipPosition.y,
121
+ }}
122
+ >
123
+ <Typography fontSize={12}>
124
+ {hoveredItemIndex !== null &&
125
+ `${nonZeroValues[hoveredItemIndex]?.key} : `}
126
+ </Typography>
127
+ <Typography fontSize={12}>
128
+ {hoveredItemIndex !== null && nonZeroValues[hoveredItemIndex]?.value}
129
+ </Typography>
130
+ </div>
131
+ );
132
+ };
133
+
134
+ const renderLegend = (legendData: ChartItem[], legendType: LegendType) => {
135
+ switch (legendType) {
136
+ case 'numberLegend':
137
+ return (
138
+ <div className="ff-legend-container numberLegend">
139
+ {legendData.map((item, index) => (
140
+ <div className="ff-legend-item" key={index}>
141
+ <Typography
142
+ fontSize={22}
143
+ fontWeight="semi-bold"
144
+ className="ff-legend-value"
145
+ color={
146
+ item.color
147
+ ? item.color
148
+ : colorMapping[index % colorMapping.length]
149
+ }
150
+ >
151
+ {item.value}
152
+ </Typography>
153
+ <Typography fontSize={12} className="ff-legend-key">
154
+ {item.key}
155
+ </Typography>
156
+ </div>
157
+ ))}
158
+ </div>
159
+ );
160
+
161
+ case 'pillLegend':
162
+ return (
163
+ <div className="ff-legend-container pillLegend">
164
+ {legendData.map((item, index) => (
165
+ <div className="ff-legend-item" key={index}>
166
+ <span
167
+ className="ff-legend-capsule"
168
+ style={{
169
+ backgroundColor: item.color
170
+ ? item.color
171
+ : colorMapping[index % colorMapping.length],
172
+ }}
173
+ >
174
+ <Typography fontSize={10}>{item.value}</Typography>
175
+ </span>
176
+ <Typography className="ff-legend-key">{item.key}</Typography>
177
+ </div>
178
+ ))}
179
+ </div>
180
+ );
181
+
182
+ case 'memoryLegend':
183
+ return (
184
+ <div className="ff-legend-container memoryLegend">
185
+ {legendData.map((item, index) => (
186
+ <React.Fragment key={index}>
187
+ <div className="ff-legend-item">
188
+ <Typography
189
+ fontSize={22}
190
+ fontWeight="medium"
191
+ className="ff-legend-value"
192
+ color={
193
+ item.color
194
+ ? item.color
195
+ : colorMapping[index % colorMapping.length]
196
+ }
197
+ >
198
+ {item.value}
199
+ </Typography>
200
+ <Typography className="ff-legend-key">{item.key}</Typography>
201
+ </div>
202
+ </React.Fragment>
203
+ ))}
204
+ </div>
205
+ );
206
+
207
+ case 'tableLegend':
208
+ return (
209
+ <div className="ff-legend-table-wrapper">
210
+ <table className="ff-legend-table tableLegend">
211
+ <thead>
212
+ <tr>
213
+ <th className="ff-table-header">Name</th>
214
+ <th className="ff-table-header">%</th>
215
+ <th className="ff-table-header">Count</th>
216
+ </tr>
217
+ </thead>
218
+ <tbody>
219
+ {legendData.map((item, index) => (
220
+ <tr
221
+ className="ff-legend-item"
222
+ key={index}
223
+ onMouseEnter={() => setHoveredItemIndex(index)}
224
+ onMouseLeave={() => setHoveredItemIndex(null)}
225
+ >
226
+ <td className="ff-legend-name">
227
+ <span
228
+ className="ff-legend-capsule"
229
+ style={{
230
+ backgroundColor: item.color
231
+ ? item.color
232
+ : colorMapping[index % colorMapping.length],
233
+ }}
234
+ >
235
+ <Typography fontSize={10}>{item.value}</Typography>
236
+ </span>
237
+ <Typography fontSize={10}>{item.key}</Typography>
238
+ </td>
239
+ <td className="ff-legend-percentage">
240
+ {((item.value / total) * 100).toFixed(1)}
241
+ </td>
242
+ <td className="ff-legend-count">{item.value}</td>
243
+ </tr>
244
+ ))}
245
+ </tbody>
246
+ </table>
247
+ </div>
248
+ );
249
+
250
+ default:
251
+ return null;
252
+ }
253
+ };
254
+
255
+ return (
256
+ <div
257
+ className={classNames('ff-dashboard-donut-chart-section', {
258
+ 'ff-dashboard-donut-chart-section-table-legend':
259
+ legendType === 'tableLegend',
260
+ })}
261
+ >
262
+ <div className="ff-dashboard-donut-chart-svg-container">
263
+ <svg
264
+ width={DONUT_SVG_SIZE}
265
+ height={DONUT_SVG_SIZE}
266
+ viewBox={`0 0 ${DONUT_SVG_SIZE} ${DONUT_SVG_SIZE}`}
267
+ >
268
+ <g
269
+ transform={`translate(${radius + lineWidth + SVG_PADDING}, ${
270
+ radius + lineWidth + SVG_PADDING
271
+ })`}
272
+ >
273
+ {nonZeroValues?.map((status, i) => {
274
+ const valuePercentage = status.value / total;
275
+ let angle = Math.max(valuePercentage * (2 * Math.PI), MIN_ANGLE);
276
+ angle += remainingAngle * (valuePercentage / total);
277
+ const endAngle = currentAngle + angle;
278
+
279
+ return renderArc(status, endAngle, i);
280
+ })}
281
+ {showOnlyLabel ? (
282
+ <text
283
+ x="0"
284
+ y="5"
285
+ className="ff-svg-label-text"
286
+ textAnchor="middle"
287
+ fill={colorMapping[3]}
288
+ >
289
+ {legendDetailsType}
290
+ </text>
291
+ ) : (
292
+ (legendType !== 'tableLegend' || hoveredItemIndex !== null) && (
293
+ <>
294
+ <text x="0" y="5" textAnchor="middle" fill={colorMapping[3]}>
295
+ {legendType === 'tableLegend' &&
296
+ hoveredItemIndex !== null &&
297
+ nonZeroValues[hoveredItemIndex]
298
+ ? `${(
299
+ (nonZeroValues[hoveredItemIndex].value / total) *
300
+ 100
301
+ ).toFixed(1)}%`
302
+ : total}
303
+ </text>
304
+
305
+ <text
306
+ x="0"
307
+ y="26"
308
+ textAnchor="middle"
309
+ fill="var(--text-color)"
310
+ >
311
+ {legendType === 'tableLegend' &&
312
+ hoveredItemIndex !== null &&
313
+ nonZeroValues[hoveredItemIndex]
314
+ ? nonZeroValues[hoveredItemIndex].key
315
+ : legendDetailsType}
316
+ </text>
317
+ </>
318
+ )
319
+ )}
320
+ </g>
321
+ </svg>
322
+ {legendType === 'tableLegend' && (
323
+ <div>
324
+ <Typography fontWeight="semi-bold">{legendDetailsType} </Typography>
325
+ <Typography> {`Total ${legendDetailsType} - ${total}`} </Typography>
326
+ </div>
327
+ )}
328
+ {showTooltip && renderTooltip()}
329
+ </div>
330
+ {isLegendDetails && renderLegend(nonZeroValues, legendType)}
331
+ </div>
332
+ );
333
+ };
334
+
335
+ export default DashboardDonutChart;
@@ -0,0 +1 @@
1
+ export { default } from './DashboardDonutChart';
@@ -0,0 +1,33 @@
1
+ export type Status =
2
+ | 'passed'
3
+ | 'failed'
4
+ | 'warning'
5
+ | 'skipped'
6
+ | 'Passed'
7
+ | 'Failed'
8
+ | 'Skipped'
9
+ | 'Warning';
10
+
11
+ export type StatusValue = {
12
+ status: Status;
13
+ value: number;
14
+ };
15
+
16
+ export type ChartItem = {
17
+ key: string;
18
+ value: number;
19
+ color?: string;
20
+ };
21
+
22
+ export type LegendType = 'numberLegend' | 'pillLegend' | 'memoryLegend' | 'tableLegend'
23
+
24
+ export type DashboardDonutChartProps = {
25
+ radius: number;
26
+ lineWidth: number;
27
+ statusValues: ChartItem[];
28
+ legendDetailsType: string;
29
+ isLegendDetails: boolean;
30
+ gapAngle?: number;
31
+ legendType: LegendType;
32
+ showOnlyLabel : boolean;
33
+ };
@@ -0,0 +1,39 @@
1
+ .ff-pie-chart-container {
2
+ display: flex;
3
+ flex-direction: column;
4
+ align-items: center;
5
+ font-size: var(--fontSize);
6
+ .ff-pie-chart-border {
7
+ display: inline-block;
8
+ border: 2px solid var(--pie-chart-border-color);
9
+ border-radius: 50%;
10
+ }
11
+
12
+ .ff-pie-chart-tooltip {
13
+ position: absolute;
14
+ top: 50%;
15
+ left: 50%;
16
+ transform: translate(-50%, -100%);
17
+ padding: 8px;
18
+ border-radius: 4px;
19
+ background-color: var(--tooltip-bg-color);
20
+ color: var(--primary-icon-color);
21
+ border: 10px solid;
22
+ font-size: 14px;
23
+ font-weight: 400;
24
+ pointer-events: none;
25
+ opacity: 0.8;
26
+ }
27
+
28
+ .ff-pie-chart-legend {
29
+ display: grid;
30
+ grid-template-columns: repeat(3, 1fr);
31
+ gap: 16px;
32
+ .ff-pie-chart-legend-item {
33
+ display: flex;
34
+ flex-direction: column;
35
+ align-items: center;
36
+ text-align: center;
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,46 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+ import PieChart from './PieChart';
3
+
4
+ const meta: Meta<typeof PieChart> = {
5
+ title: 'Components/PieChart',
6
+ component: PieChart,
7
+ parameters: {
8
+ layout: 'centered',
9
+ },
10
+ tags: ['autodocs'],
11
+ argTypes: {
12
+ radius: { control: 'number' },
13
+ colors: { control: 'object' },
14
+ data: { control: 'object' },
15
+ chartBorder: { control: 'boolean' },
16
+ },
17
+ };
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof PieChart>;
21
+
22
+ export const PieChartDashBoard: Story = {
23
+ args: {
24
+ radius: 55,
25
+ colors: ['#71347B', '#4C90FF', '#F8A509'],
26
+ data: [
27
+ { label: 'SuperAdmin', value: 2 },
28
+ { label: 'Admin', value: 6 },
29
+ { label: 'Users', value: 2 },
30
+ ],
31
+ chartBorder: false,
32
+ },
33
+ };
34
+
35
+ export const PieChartDashBoardWithBorder: Story = {
36
+ args: {
37
+ radius: 55,
38
+ colors: ['#b6b6b6', '#08CB84', '#BE3131'],
39
+ data: [
40
+ { label: 'All User', value: 0 },
41
+ { label: 'Active', value: 8 },
42
+ { label: 'Pending', value: 2 },
43
+ ],
44
+ chartBorder: true,
45
+ },
46
+ };
@@ -0,0 +1,193 @@
1
+ import React, { useState } from 'react';
2
+ import './PieChart.scss';
3
+ import { PieChartProps, ArcParams, ArcResult, ArcAnglesResult } from './types';
4
+ import Typography from '../../Typography';
5
+
6
+ const calculateArc = ({
7
+ x,
8
+ y,
9
+ radius,
10
+ startAngle,
11
+ endAngle,
12
+ }: ArcParams): ArcResult => {
13
+ const startX = x + radius * Math.cos(startAngle);
14
+ const startY = y + radius * Math.sin(startAngle);
15
+ const endX = x + radius * Math.cos(endAngle);
16
+ const endY = y + radius * Math.sin(endAngle);
17
+ const largeArcFlag = endAngle - startAngle > Math.PI ? 1 : 0;
18
+
19
+ return `M ${startX} ${startY} A ${radius} ${radius} 0 ${largeArcFlag} 1 ${endX} ${endY} L ${x} ${y} Z`;
20
+ };
21
+
22
+ const calculateArcAngles = (
23
+ value: number,
24
+ total: number,
25
+ currentAngle: number,
26
+ radius: number
27
+ ): ArcAnglesResult => {
28
+ if (total === 0) {
29
+ return {
30
+ endAngle: currentAngle,
31
+ backgroundArcPath: '',
32
+ foregroundArcPath: '',
33
+ percentage: 0,
34
+ };
35
+ }
36
+
37
+ const percentage = value / total;
38
+ const angleIncrement = percentage * 2 * Math.PI;
39
+ const startAngle = currentAngle;
40
+ const endAngle = startAngle + angleIncrement;
41
+
42
+ const path = calculateArc({
43
+ x: 0,
44
+ y: 0,
45
+ radius,
46
+ startAngle,
47
+ endAngle,
48
+ });
49
+
50
+ return {
51
+ endAngle,
52
+ backgroundArcPath: path,
53
+ foregroundArcPath: path,
54
+ percentage,
55
+ };
56
+ };
57
+
58
+ type Tooltip = {
59
+ label: string;
60
+ value: number;
61
+ color: string;
62
+ };
63
+
64
+ const PieChart: React.FC<PieChartProps> = ({
65
+ radius = 15,
66
+ colors = [],
67
+ data = [],
68
+ chartBorder = false,
69
+ }) => {
70
+ const [tooltip, setTooltip] = useState<Tooltip | null>(null);
71
+ const [tooltipPosition, setTooltipPosition] = useState<{
72
+ x: number;
73
+ y: number;
74
+ }>({ x: 0, y: 0 });
75
+
76
+ const total = data.reduce((acc, item) => acc + item.value, 0);
77
+ let currentAngle = -Math.PI / 2;
78
+ const svgSize = 2 * (radius + 5);
79
+
80
+ const chartData =
81
+ chartBorder && data.length > 0
82
+ ? [
83
+ {
84
+ label: data[0]?.label || '',
85
+ value: data.slice(1).reduce((acc, item) => acc + item.value, 0),
86
+ },
87
+ ...data.slice(1),
88
+ ]
89
+ : data;
90
+
91
+ const handleMouseMove = (e: React.MouseEvent) => {
92
+ const { clientX: x, clientY: y } = e;
93
+ setTooltipPosition({ x, y });
94
+ };
95
+
96
+ const handleMouseEnter = (
97
+ item: { label: string; value: number },
98
+ color: string
99
+ ) => {
100
+ setTooltip({ label: item.label, value: item.value, color });
101
+ };
102
+
103
+ const handleMouseLeave = () => {
104
+ setTooltip(null);
105
+ };
106
+ const legendItems = chartData.map((item, index) => (
107
+ <div key={item.label} className="ff-pie-chart-legend-item">
108
+ <Typography
109
+ as="div"
110
+ fontSize={24}
111
+ fontWeight="semi-bold"
112
+ lineHeight="36px"
113
+ color={colors[index % colors.length] || ''}
114
+ >
115
+ {item.value}
116
+ </Typography>
117
+ <Typography
118
+ as="div"
119
+ fontSize={10}
120
+ fontWeight="regular"
121
+ lineHeight="18px"
122
+ className="ff-Pie-chart-legend-value"
123
+ >
124
+ {item.label}
125
+ </Typography>
126
+ </div>
127
+ ));
128
+
129
+ return (
130
+ <div className="ff-pie-chart-container" onMouseMove={handleMouseMove}>
131
+ <div
132
+ className={` ${chartBorder ? 'ff-pie-chart-border' : ''}`}
133
+ style={{ width: svgSize, height: svgSize }}
134
+ >
135
+ <svg
136
+ width={svgSize}
137
+ height={svgSize}
138
+ viewBox={`0 0 ${svgSize} ${svgSize}`}
139
+ >
140
+ <g transform={`translate(${radius + 5}, ${radius + 5})`}>
141
+ {chartData.map((item, index) => {
142
+ const { endAngle, backgroundArcPath } = calculateArcAngles(
143
+ item.value,
144
+ total,
145
+ currentAngle,
146
+ radius
147
+ );
148
+ currentAngle = endAngle;
149
+ const color = colors[index % colors.length] || '';
150
+
151
+ return (
152
+ <g key={item.label}>
153
+ <path
154
+ d={backgroundArcPath}
155
+ fill={color}
156
+ stroke="white"
157
+ strokeWidth={0.5}
158
+ onMouseEnter={() => handleMouseEnter(item, color)}
159
+ onMouseLeave={handleMouseLeave}
160
+ />
161
+ <text
162
+ x={(radius / 2) * Math.cos((currentAngle + endAngle) / 2)}
163
+ y={(radius / 2) * Math.sin((currentAngle + endAngle) / 2)}
164
+ fill="white"
165
+ textAnchor="middle"
166
+ dominantBaseline="central"
167
+ ></text>
168
+ </g>
169
+ );
170
+ })}
171
+ </g>
172
+ </svg>
173
+ </div>
174
+
175
+ {tooltip && (
176
+ <div
177
+ className="ff-pie-chart-tooltip"
178
+ style={{
179
+ top: tooltipPosition.y,
180
+ left: tooltipPosition.x,
181
+ border: `2px solid ${tooltip.color}`,
182
+ }}
183
+ >
184
+ {tooltip.label} : {tooltip.value}
185
+ </div>
186
+ )}
187
+
188
+ <div className="ff-pie-chart-legend">{legendItems}</div>
189
+ </div>
190
+ );
191
+ };
192
+
193
+ export default PieChart;
@@ -0,0 +1 @@
1
+ export { default } from './PieChart';
@@ -0,0 +1,28 @@
1
+ export type Status = {
2
+ status: 'Passed';
3
+ value: number;
4
+ };
5
+
6
+ export interface PieChartProps {
7
+ radius: number;
8
+ data: Array<{ label: string; value: number }>;
9
+ colors: string[];
10
+ chartBorder: boolean;
11
+ }
12
+
13
+ export type ArcParams = {
14
+ x: number;
15
+ y: number;
16
+ radius: number;
17
+ startAngle: number;
18
+ endAngle: number;
19
+ };
20
+
21
+ export type ArcResult = string;
22
+
23
+ export type ArcAnglesResult = {
24
+ endAngle: number;
25
+ backgroundArcPath: string;
26
+ foregroundArcPath: string;
27
+ percentage: number;
28
+ };
@@ -0,0 +1,11 @@
1
+ .ff-recaptcha-wrapper {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 8px;
5
+
6
+ .ff-recaptcha-error {
7
+ color: var(--input-error-text-color);
8
+ font-size: 12px;
9
+ margin-top: 4px;
10
+ }
11
+ }