@alfalab/core-components-chart 3.1.12 → 3.2.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 (108) hide show
  1. package/Component.d.ts +2 -1
  2. package/Component.js +1 -1
  3. package/components/Dot/index.css +8 -8
  4. package/components/Dot/index.js +1 -1
  5. package/components/Legends/index.css +8 -8
  6. package/components/Legends/index.js +1 -1
  7. package/components/Tick/index.css +4 -4
  8. package/components/Tick/index.d.ts +2 -1
  9. package/components/Tick/index.js +1 -1
  10. package/components/TooltipContent/index.css +7 -7
  11. package/components/TooltipContent/index.d.ts +2 -1
  12. package/components/TooltipContent/index.js +1 -1
  13. package/cssm/Component.d.ts +2 -1
  14. package/cssm/components/Tick/index.d.ts +2 -1
  15. package/cssm/components/TooltipContent/index.d.ts +2 -1
  16. package/cssm/icons/Circle.d.ts +2 -1
  17. package/cssm/icons/FilledCircle.d.ts +2 -1
  18. package/cssm/icons/Point.d.ts +2 -1
  19. package/cssm/icons/StrokeCircle.d.ts +2 -1
  20. package/esm/Component.d.ts +2 -1
  21. package/esm/Component.js +1 -1
  22. package/esm/components/Dot/index.css +8 -8
  23. package/esm/components/Dot/index.js +1 -1
  24. package/esm/components/Legends/index.css +8 -8
  25. package/esm/components/Legends/index.js +1 -1
  26. package/esm/components/Tick/index.css +4 -4
  27. package/esm/components/Tick/index.d.ts +2 -1
  28. package/esm/components/Tick/index.js +1 -1
  29. package/esm/components/TooltipContent/index.css +7 -7
  30. package/esm/components/TooltipContent/index.d.ts +2 -1
  31. package/esm/components/TooltipContent/index.js +1 -1
  32. package/esm/icons/Circle.d.ts +2 -1
  33. package/esm/icons/FilledCircle.d.ts +2 -1
  34. package/esm/icons/Point.d.ts +2 -1
  35. package/esm/icons/StrokeCircle.d.ts +2 -1
  36. package/esm/index.css +5 -5
  37. package/icons/Circle.d.ts +2 -1
  38. package/icons/FilledCircle.d.ts +2 -1
  39. package/icons/Point.d.ts +2 -1
  40. package/icons/StrokeCircle.d.ts +2 -1
  41. package/index.css +5 -5
  42. package/modern/Component.d.ts +2 -1
  43. package/modern/Component.js +1 -1
  44. package/modern/components/Dot/index.css +8 -8
  45. package/modern/components/Dot/index.js +1 -1
  46. package/modern/components/Legends/index.css +8 -8
  47. package/modern/components/Legends/index.js +1 -1
  48. package/modern/components/Tick/index.css +4 -4
  49. package/modern/components/Tick/index.d.ts +2 -1
  50. package/modern/components/Tick/index.js +1 -1
  51. package/modern/components/TooltipContent/index.css +7 -7
  52. package/modern/components/TooltipContent/index.d.ts +2 -1
  53. package/modern/components/TooltipContent/index.js +1 -1
  54. package/modern/icons/Circle.d.ts +2 -1
  55. package/modern/icons/FilledCircle.d.ts +2 -1
  56. package/modern/icons/Point.d.ts +2 -1
  57. package/modern/icons/StrokeCircle.d.ts +2 -1
  58. package/modern/index.css +5 -5
  59. package/package.json +2 -2
  60. package/src/Component.tsx +404 -0
  61. package/src/components/CustomizedLabel.tsx +29 -0
  62. package/src/components/Dot/index.module.css +22 -0
  63. package/src/components/Dot/index.tsx +79 -0
  64. package/src/components/Legends/index.module.css +36 -0
  65. package/src/components/Legends/index.tsx +85 -0
  66. package/src/components/LinearGradient.tsx +16 -0
  67. package/src/components/RectBar.tsx +50 -0
  68. package/src/components/Tick/index.module.css +16 -0
  69. package/src/components/Tick/index.tsx +32 -0
  70. package/src/components/TooltipContent/index.module.css +51 -0
  71. package/src/components/TooltipContent/index.tsx +83 -0
  72. package/src/hoc/Customized.jsx +7 -0
  73. package/src/hooks/usePathBar/index.tsx +56 -0
  74. package/src/hooks/usePathBar/utils/getRadius.ts +5 -0
  75. package/src/hooks/useSettings/index.tsx +66 -0
  76. package/src/hooks/useSettings/utils/setComposedChartsMargin.ts +29 -0
  77. package/src/hooks/useSettings/utils/setDatas.ts +53 -0
  78. package/src/hooks/useSettings/utils/setGradientCharts.ts +43 -0
  79. package/src/hooks/useSettings/utils/setLegendMargin.ts +16 -0
  80. package/src/hooks/useSettings/utils/sortByIndex.ts +10 -0
  81. package/src/icons/Circle.tsx +12 -0
  82. package/src/icons/CircleLine.tsx +13 -0
  83. package/src/icons/FilledCircle.tsx +25 -0
  84. package/src/icons/Point.tsx +13 -0
  85. package/src/icons/StrokeCircle.tsx +12 -0
  86. package/src/index.module.css +21 -0
  87. package/src/index.ts +2 -0
  88. package/src/types/brush.types.ts +50 -0
  89. package/src/types/cartesianGrid.types.ts +26 -0
  90. package/src/types/chart.types.ts +81 -0
  91. package/src/types/composedChart.types.ts +36 -0
  92. package/src/types/index.ts +14 -0
  93. package/src/types/labelList.types.ts +5 -0
  94. package/src/types/legend.types.ts +35 -0
  95. package/src/types/options.types.ts +69 -0
  96. package/src/types/payload.types.ts +33 -0
  97. package/src/types/responsiveContainer.types.ts +9 -0
  98. package/src/types/seria.types.ts +86 -0
  99. package/src/types/tooltip.types.ts +85 -0
  100. package/src/types/utils/axis.types.ts +88 -0
  101. package/src/types/utils/coordinates.types.ts +11 -0
  102. package/src/types/utils/data.types.ts +19 -0
  103. package/src/types/utils/dot.types.ts +88 -0
  104. package/src/types/utils/gradient.types.ts +33 -0
  105. package/src/types/utils/index.ts +6 -0
  106. package/src/types/utils/tick.types.ts +38 -0
  107. package/src/types/xAxis.types.ts +18 -0
  108. package/src/types/yAxis.types.ts +8 -0
@@ -0,0 +1,404 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import cn from 'classnames';
4
+ import {
5
+ Area,
6
+ Bar,
7
+ Brush,
8
+ CartesianGrid,
9
+ Cell,
10
+ ComposedChart,
11
+ LabelList,
12
+ Legend,
13
+ Line,
14
+ ResponsiveContainer,
15
+ Tooltip,
16
+ XAxis,
17
+ YAxis,
18
+ } from 'recharts';
19
+
20
+ import { CustomizedLabel } from './components/CustomizedLabel';
21
+ import { Dot } from './components/Dot';
22
+ import { Legends } from './components/Legends';
23
+ import { LinearGradient } from './components/LinearGradient';
24
+ import { RectBar } from './components/RectBar';
25
+ import { Tick } from './components/Tick';
26
+ import { TooltipContent } from './components/TooltipContent';
27
+ import { CustomizedHOC } from './hoc/Customized';
28
+ import { useSettings } from './hooks/useSettings';
29
+ import {
30
+ ActiveDotProps,
31
+ CoordinatesProps,
32
+ DataDynamicBooleanProps,
33
+ DataDynamicProps,
34
+ OptionsProps,
35
+ SeriaProps,
36
+ ToggleChartProps,
37
+ } from './types';
38
+
39
+ import styles from './index.module.css';
40
+
41
+ export const Chart = (props: OptionsProps) => {
42
+ const [{ state, data, charts, filterCount }, { setCharts, setFilterCount }] =
43
+ useSettings(props);
44
+ const [activeDotsState, setActiveDotsState] = useState<ActiveDotProps>({
45
+ prev: null,
46
+ active: null,
47
+ });
48
+
49
+ const [yBrush, setYBrush] = useState<number | null>(null);
50
+ const [tooltipArrowSide, setTooltipArrowSide] = useState<boolean | null>(null);
51
+ const [heightLegend, setHeightLegend] = useState<number>(0);
52
+
53
+ const svgRef = useRef<HTMLDivElement>(null);
54
+ const tooltipRef = useRef<any>(null);
55
+
56
+ const renderGradient = useMemo(() => {
57
+ if (!state) return null;
58
+
59
+ return state.series.map((item: SeriaProps) => {
60
+ const { chart, gradient } = item;
61
+
62
+ if (chart !== 'gradient' || !gradient) return null;
63
+ const { gid, points } = gradient;
64
+
65
+ return (
66
+ <LinearGradient
67
+ key={`${state.id}-${gid}`}
68
+ id={state.id}
69
+ gid={gid}
70
+ points={points}
71
+ />
72
+ );
73
+ });
74
+ }, [state]);
75
+
76
+ const toggleChart = useCallback(
77
+ (item: ToggleChartProps): void => {
78
+ const {
79
+ chart,
80
+ properties: { dataKey },
81
+ } = item;
82
+
83
+ const withGrad = chart === 'area';
84
+ let changed = false;
85
+
86
+ if (charts[`${dataKey}`] && filterCount > 1) {
87
+ changed = true;
88
+ setFilterCount((prev) => prev - 1);
89
+ }
90
+
91
+ if (!charts[`${dataKey}`]) {
92
+ changed = true;
93
+ setFilterCount((prev) => prev + 1);
94
+ }
95
+
96
+ if (!changed) return;
97
+ setCharts((prev: DataDynamicBooleanProps) => {
98
+ const newState = { ...prev };
99
+
100
+ newState[`${dataKey}`] = !newState[`${dataKey}`];
101
+ if (withGrad) newState[`${dataKey}-gradient`] = !newState[`${dataKey}-gradient`];
102
+
103
+ return newState;
104
+ });
105
+ },
106
+ [charts, filterCount, setCharts, setFilterCount],
107
+ );
108
+
109
+ const legendRef = useCallback((node: HTMLUListElement): void => {
110
+ if (node !== null) {
111
+ setTimeout(() => {
112
+ const { height } = node.getBoundingClientRect();
113
+
114
+ setHeightLegend(height);
115
+ }, 0);
116
+ }
117
+ }, []);
118
+
119
+ const renderLegend = useMemo((): React.ReactElement | null => {
120
+ if (!state?.legend) return null;
121
+
122
+ const translate =
123
+ state?.xAxis?.tickMargin && state?.legend?.verticalAlign !== 'top'
124
+ ? state.xAxis.tickMargin + (state?.brush?.brushMargin || 0)
125
+ : 0;
126
+
127
+ return (
128
+ <Legend
129
+ {...(state.legend || null)}
130
+ content={
131
+ <Legends
132
+ legend={state.legend}
133
+ series={state.series}
134
+ id={state.id}
135
+ toggleChart={toggleChart}
136
+ ref={legendRef}
137
+ charts={charts}
138
+ />
139
+ }
140
+ wrapperStyle={{
141
+ transform: `translateY(${translate}px)`,
142
+ }}
143
+ />
144
+ );
145
+ }, [state, charts, toggleChart, legendRef]);
146
+
147
+ const renderCartesianGrid = useMemo((): React.ReactElement | null => {
148
+ if (!state?.cartesianGrid) return null;
149
+
150
+ return <CartesianGrid {...state.cartesianGrid} />;
151
+ }, [state]);
152
+
153
+ const renderXAxis = useMemo((): React.ReactElement | null => {
154
+ if (!state?.xAxis) return null;
155
+
156
+ let tick;
157
+
158
+ if (state?.xAxis?.tickType === 'point') {
159
+ tick = CustomizedHOC(Tick, { xAxis: state.xAxis });
160
+ } else if (typeof state.xAxis.tick === 'boolean') {
161
+ tick = state.xAxis.tick;
162
+ } else {
163
+ tick = true;
164
+ }
165
+
166
+ return <XAxis {...state.xAxis} tick={tick} />;
167
+ }, [state]);
168
+
169
+ const renderYAxis = useMemo((): React.ReactElement | null => {
170
+ if (!state?.yAxis) return null;
171
+
172
+ let tick;
173
+
174
+ if (state?.yAxis?.tick) {
175
+ tick = CustomizedHOC(state.yAxis.tick, { state });
176
+ } else if (typeof state.yAxis.tick === 'boolean') {
177
+ tick = state.yAxis.tick;
178
+ } else {
179
+ tick = true;
180
+ }
181
+
182
+ return <YAxis {...state.yAxis} tick={tick} />;
183
+ }, [state]);
184
+
185
+ const renderBrush = useMemo((): React.ReactElement | null => {
186
+ if (!state?.brush) return null;
187
+
188
+ return <Brush y={typeof yBrush === 'number' ? yBrush : 0} {...state.brush} />;
189
+ }, [state, yBrush]);
190
+
191
+ const renderTooltip = useMemo((): React.ReactElement | null => {
192
+ if (!state?.tooltip) return null;
193
+
194
+ return (
195
+ <Tooltip
196
+ ref={tooltipRef}
197
+ {...state.tooltip}
198
+ content={CustomizedHOC(TooltipContent, { series: state.series, tooltipArrowSide })}
199
+ />
200
+ );
201
+ }, [state, tooltipArrowSide]);
202
+
203
+ const renderChartsItems = useMemo(() => {
204
+ if (!state || !charts) return null;
205
+
206
+ return state.series.map((item: SeriaProps) => {
207
+ const { chart, properties, radius, labelList } = item;
208
+ const show = charts[`${properties.dataKey}`];
209
+
210
+ switch (chart) {
211
+ case 'bar':
212
+ return show && !item?.hide ? (
213
+ <Bar
214
+ key={`${state.id}-${properties.dataKey}`}
215
+ {...properties}
216
+ shape={<RectBar radius={radius} />}
217
+ >
218
+ {labelList && (
219
+ <LabelList
220
+ dataKey={properties.dataKey.toString()}
221
+ {...labelList}
222
+ content={<CustomizedLabel radius={radius} />}
223
+ />
224
+ )}
225
+ {data.map((_: DataDynamicProps, index: number) => {
226
+ const key = `${state.id}-${properties.dataKey}-${index}`;
227
+
228
+ return (
229
+ <Cell
230
+ key={key}
231
+ className={cn(
232
+ styles.bar,
233
+ typeof activeDotsState.active === 'number' &&
234
+ activeDotsState.active !== index
235
+ ? styles.unfocused
236
+ : '',
237
+ )}
238
+ />
239
+ );
240
+ })}
241
+ </Bar>
242
+ ) : null;
243
+
244
+ case 'area':
245
+ case 'line':
246
+ return show && !item?.hide ? (
247
+ <Line
248
+ key={`${state.id}-${properties.dataKey}`}
249
+ {...properties}
250
+ dot={
251
+ properties.dot && properties.dotSettings
252
+ ? CustomizedHOC(Dot, {
253
+ activeDot: activeDotsState.active,
254
+ dotSettings: properties.dotSettings,
255
+ inherit: properties?.inheritStroke
256
+ ? properties.inheritStroke
257
+ : false,
258
+ })
259
+ : false
260
+ }
261
+ activeDot={false}
262
+ />
263
+ ) : null;
264
+
265
+ case 'gradient':
266
+ return show && !item?.hide ? (
267
+ <Area
268
+ {...item.properties}
269
+ key={`${state.id}-${item.properties.dataKey}`}
270
+ dataKey={`${item.properties.dataKey}`}
271
+ stroke='transparent'
272
+ fill={
273
+ item.gradient.gid
274
+ ? `url(#${state.id}-${item.gradient.gid})`
275
+ : item.properties.fill
276
+ }
277
+ dot={false}
278
+ activeDot={false}
279
+ />
280
+ ) : null;
281
+
282
+ default:
283
+ return null;
284
+ }
285
+ });
286
+ }, [charts, state, activeDotsState, data]);
287
+
288
+ // Позиционирование brush
289
+ useEffect(() => {
290
+ if (!state || !state.brush) return;
291
+ if (!heightLegend || heightLegend === 0) return;
292
+
293
+ const align = state?.legend?.verticalAlign;
294
+ const legendHeight = align === 'top' ? 0 : heightLegend;
295
+ const marginTick = state?.xAxis?.tickMargin ? state?.xAxis?.tickMargin : 0;
296
+ const brushY =
297
+ (svgRef.current?.clientHeight ? svgRef.current.clientHeight : 0) -
298
+ legendHeight -
299
+ state.brush.height -
300
+ (state?.composeChart?.margin?.bottom ? state.composeChart.margin.bottom : 0) +
301
+ marginTick +
302
+ (state.brush?.brushMargin ? state.brush.brushMargin : 0);
303
+
304
+ setYBrush(brushY);
305
+ }, [heightLegend, state]);
306
+
307
+ const leaveEvent = (isTooltipActive: boolean): void => {
308
+ if (isTooltipActive) return;
309
+
310
+ if (typeof activeDotsState.prev !== 'number' || typeof activeDotsState.active !== 'number')
311
+ return;
312
+
313
+ setActiveDotsState({
314
+ prev: null,
315
+ active: null,
316
+ });
317
+ };
318
+
319
+ const arrowTooltipEvent = (activeCoordinate: CoordinatesProps): void => {
320
+ if (!state?.tooltip?.arrow) return;
321
+
322
+ if (state?.tooltip?.arrow && activeCoordinate?.x) {
323
+ const side =
324
+ (svgRef?.current?.clientWidth || 0) -
325
+ (state?.composeChart?.margin?.right || 0) -
326
+ activeCoordinate.x -
327
+ (tooltipRef.current?.state?.boxWidth || 0) >
328
+ 20;
329
+
330
+ setTooltipArrowSide(side);
331
+ }
332
+ };
333
+
334
+ const hoverEvent = (isTooltipActive: boolean, activeTooltipIndex: number | undefined): void => {
335
+ if (!isTooltipActive) return;
336
+
337
+ if (
338
+ typeof activeDotsState.active === 'number' &&
339
+ activeTooltipIndex === activeDotsState.active
340
+ )
341
+ return;
342
+ if (typeof activeTooltipIndex === 'number' && typeof activeDotsState.active !== 'number') {
343
+ setActiveDotsState({
344
+ prev: activeTooltipIndex,
345
+ active: activeTooltipIndex,
346
+ });
347
+ }
348
+
349
+ if (typeof activeTooltipIndex === 'number' && typeof activeDotsState.prev === 'number') {
350
+ setActiveDotsState((prev: ActiveDotProps) => ({
351
+ prev: prev.active,
352
+ active: activeTooltipIndex,
353
+ }));
354
+ }
355
+ };
356
+
357
+ const mouseMove = (e: any): void => {
358
+ if (!state?.tooltip) return;
359
+
360
+ arrowTooltipEvent(e.activeCoordinate);
361
+ hoverEvent(e.isTooltipActive, e.activeTooltipIndex);
362
+ leaveEvent(e.isTooltipActive);
363
+ };
364
+
365
+ const mouseLeave = (e: any): void => {
366
+ if (!state?.tooltip) return;
367
+
368
+ leaveEvent(e.isTooltipActive);
369
+ };
370
+
371
+ if (!data || !charts || !state) return null;
372
+
373
+ return (
374
+ <div
375
+ className={styles.coreChart}
376
+ ref={svgRef}
377
+ id={state?.id || ''}
378
+ style={{ width: '100%', height: '100%' }}
379
+ >
380
+ <ResponsiveContainer
381
+ debounce={
382
+ state?.responsiveContainer?.debounce ? state.responsiveContainer.debounce : 0
383
+ }
384
+ width='100%'
385
+ >
386
+ <ComposedChart
387
+ {...state?.composeChart}
388
+ onMouseMove={mouseMove}
389
+ onMouseLeave={mouseLeave}
390
+ data={data}
391
+ >
392
+ <defs>{renderGradient}</defs>
393
+ {state.cartesianGrid && renderCartesianGrid}
394
+ {state.xAxis && renderXAxis}
395
+ {state.yAxis && renderYAxis}
396
+ {renderChartsItems}
397
+ {state.tooltip && renderTooltip}
398
+ {state.brush && renderBrush}
399
+ {state.legend && renderLegend}
400
+ </ComposedChart>
401
+ </ResponsiveContainer>
402
+ </div>
403
+ );
404
+ };
@@ -0,0 +1,29 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from 'react';
3
+
4
+ import { usePathBar } from '../hooks/usePathBar';
5
+
6
+ export const CustomizedLabel: React.FC<any> = ({
7
+ x,
8
+ y,
9
+ value,
10
+ offset,
11
+ radius,
12
+ height,
13
+ width,
14
+ formatter,
15
+ }) => {
16
+ const [initHeight] = usePathBar({ radius, height });
17
+
18
+ return (
19
+ <text
20
+ x={x + width / 2}
21
+ y={y + height - (initHeight + offset)}
22
+ width={width}
23
+ height={initHeight}
24
+ textAnchor='middle'
25
+ >
26
+ <tspan x={x + width / 2}>{formatter ? formatter(value) : value}</tspan>
27
+ </text>
28
+ );
29
+ };
@@ -0,0 +1,22 @@
1
+ .dotUnfocused {
2
+ opacity: 0.3;
3
+ }
4
+
5
+ .dot,
6
+ .dotItem,
7
+ .dotWrap {
8
+ transition: all 0.2s ease;
9
+ }
10
+
11
+ .dot {
12
+ animation: showDot 0.5s ease;
13
+ }
14
+
15
+ @keyframes showDot {
16
+ from {
17
+ opacity: 0;
18
+ }
19
+ to {
20
+ opacity: 1;
21
+ }
22
+ }
@@ -0,0 +1,79 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import cn from 'classnames';
3
+
4
+ import { PointIcon } from '../../icons/Point';
5
+ import { DotProps, DotSettingProps } from '../../types/utils/dot.types';
6
+
7
+ import styles from './index.module.css';
8
+
9
+ export const Dot = React.forwardRef<SVGSVGElement, DotProps>(
10
+ (
11
+ { cx, cy, index, activeDot, dataKey, dotSettings, value, stroke },
12
+ ref,
13
+ ): React.ReactElement | null => {
14
+ const [windowWidth, setWindowWidth] = useState<number>(0);
15
+
16
+ const [height, setHeight] = useState<number>(0);
17
+ const [width, setWidth] = useState<number>(0);
18
+ const [option, setOption] = useState<DotSettingProps | null>(null);
19
+
20
+ useEffect(() => {
21
+ let dotSetting: DotSettingProps =
22
+ Array.isArray(dotSettings) && dotSettings.length > 0
23
+ ? dotSettings.find((item) => item.media && windowWidth < item.media)
24
+ : dotSettings;
25
+
26
+ if (Array.isArray(dotSettings) && dotSettings.length > 0 && !dotSetting) {
27
+ dotSetting = dotSettings[dotSettings.length - 1];
28
+ }
29
+
30
+ setWindowWidth(window.innerWidth);
31
+ setOption(dotSetting);
32
+ }, [dotSettings, windowWidth]);
33
+
34
+ useEffect(() => {
35
+ if (!option) return;
36
+
37
+ if (typeof activeDot === 'number' && activeDot === index) {
38
+ setHeight(option.height * option.scale);
39
+ setWidth(option.width * option.scale);
40
+ } else {
41
+ setHeight(option.height * option.initScale);
42
+ setWidth(option.width * option.initScale);
43
+ }
44
+ }, [activeDot, index, option]);
45
+
46
+ if (!value) return null;
47
+
48
+ return (
49
+ <g
50
+ ref={ref}
51
+ className={cn(styles.dot)}
52
+ transform={`translate(${cx - width / 2}, ${cy - height / 2})`}
53
+ >
54
+ <g
55
+ className={cn(styles.dotWrap)}
56
+ transform={`scale(${
57
+ activeDot === index ? option?.scale || 0 : option?.initScale || 0
58
+ })`}
59
+ >
60
+ <svg
61
+ className={cn(
62
+ styles.dotItem,
63
+ activeDot === index ? styles.dotActive : '',
64
+ typeof activeDot === 'number' && activeDot !== index
65
+ ? styles.dotUnfocused
66
+ : '',
67
+ )}
68
+ data-id={index}
69
+ data-name={dataKey}
70
+ width={option?.width || 0}
71
+ height={option?.height || 0}
72
+ >
73
+ <PointIcon fill={stroke} />
74
+ </svg>
75
+ </g>
76
+ </g>
77
+ );
78
+ },
79
+ );
@@ -0,0 +1,36 @@
1
+ @import '@alfalab/core-components-themes/src/default.css';
2
+
3
+ .legendContent {
4
+ display: flex;
5
+ align-items: center;
6
+ flex-wrap: wrap;
7
+ }
8
+
9
+ .legendWrap {
10
+ width: 100%;
11
+ margin: 0;
12
+ padding: 0;
13
+ }
14
+
15
+ .legendItem {
16
+ margin-right: var(--gap-2xl);
17
+ cursor: pointer;
18
+ display: inline-block;
19
+ }
20
+
21
+ .legendItem:last-child {
22
+ margin-right: 0;
23
+ }
24
+
25
+ .legendUnactive {
26
+ opacity: 0.3;
27
+ }
28
+
29
+ .legendIcon {
30
+ margin-right: 13px;
31
+ display: flex;
32
+ }
33
+
34
+ .legendValue {
35
+ text-transform: capitalize;
36
+ }
@@ -0,0 +1,85 @@
1
+ import React from 'react';
2
+ import cn from 'classnames';
3
+
4
+ import { Typography } from '@alfalab/core-components-typography';
5
+
6
+ import { CircleIcon } from '../../icons/Circle';
7
+ import { CircleLineIcon } from '../../icons/CircleLine';
8
+ import { FilledCircleIcon } from '../../icons/FilledCircle';
9
+ import { StrokeCircleIcon } from '../../icons/StrokeCircle';
10
+ import { LegendProps } from '../../types/legend.types';
11
+ import { SeriaProps } from '../../types/seria.types';
12
+ import { DataDynamicBooleanProps } from '../../types/utils/data.types';
13
+
14
+ import styles from './index.module.css';
15
+
16
+ interface Props {
17
+ legend: LegendProps;
18
+ series: SeriaProps[];
19
+ id: string;
20
+ charts: DataDynamicBooleanProps;
21
+ toggleChart(item: SeriaProps): void;
22
+ }
23
+
24
+ const icons = {
25
+ circleLine: CircleLineIcon,
26
+ filledCircle: FilledCircleIcon,
27
+ strokeCircle: StrokeCircleIcon,
28
+ circle: CircleIcon,
29
+ };
30
+
31
+ export const Legends = React.forwardRef<HTMLUListElement, Props>(
32
+ ({ legend, series, id, charts, toggleChart }, ref): React.ReactElement => {
33
+ const style: React.CSSProperties = {
34
+ textAlign: legend.align || 'center',
35
+ transform: `translateY(${
36
+ (legend?.marginTop ? legend.marginTop : 0) *
37
+ (legend.verticalAlign === 'top' ? -1 : 1)
38
+ }px)`,
39
+ };
40
+
41
+ return (
42
+ <ul ref={ref} className={cn(styles.legendWrap)} style={style}>
43
+ {series.map((item: SeriaProps) => {
44
+ if (item.hideLegend || item.hide) return null;
45
+
46
+ const Icon = icons[item.icon] || CircleIcon;
47
+
48
+ return (
49
+ <li
50
+ role='presentation'
51
+ key={`${id}-${item.properties.dataKey}`}
52
+ className={cn(
53
+ styles.legendItem,
54
+ charts[`${item.properties.dataKey}`] ? '' : styles.legendUnactive,
55
+ )}
56
+ onClick={() => toggleChart(item)}
57
+ >
58
+ <div className={cn(styles.legendContent)}>
59
+ {Icon ? (
60
+ <i className={cn(styles.legendIcon)}>
61
+ <Icon
62
+ fill={
63
+ item.properties?.fill ||
64
+ item.properties?.stroke ||
65
+ ''
66
+ }
67
+ height={legend.iconHeight || 16}
68
+ />
69
+ </i>
70
+ ) : null}
71
+ <Typography.Text
72
+ view='primary-medium'
73
+ tag='span'
74
+ className={cn(styles.legendValue)}
75
+ >
76
+ {item.properties.name}
77
+ </Typography.Text>
78
+ </div>
79
+ </li>
80
+ );
81
+ })}
82
+ </ul>
83
+ );
84
+ },
85
+ );
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+
3
+ import { GradientProps, LinearGradientProps } from '../types/utils/gradient.types';
4
+
5
+ export const LinearGradient = ({ id, gid, points }: LinearGradientProps): React.ReactElement => (
6
+ <linearGradient key={`${id}-${gid}`} id={`${id}-${gid}`} x1='0' y1='0' x2='0' y2='1'>
7
+ {points.map((point: GradientProps, index: number) => (
8
+ <stop
9
+ key={`${id}${gid}-${index.toString()}`}
10
+ offset={`${point.offset}%`}
11
+ stopColor={point.stopColor}
12
+ stopOpacity={point.stopOpacity}
13
+ />
14
+ ))}
15
+ </linearGradient>
16
+ );