@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.
- package/Component.d.ts +2 -1
- package/Component.js +1 -1
- package/components/Dot/index.css +8 -8
- package/components/Dot/index.js +1 -1
- package/components/Legends/index.css +8 -8
- package/components/Legends/index.js +1 -1
- package/components/Tick/index.css +4 -4
- package/components/Tick/index.d.ts +2 -1
- package/components/Tick/index.js +1 -1
- package/components/TooltipContent/index.css +7 -7
- package/components/TooltipContent/index.d.ts +2 -1
- package/components/TooltipContent/index.js +1 -1
- package/cssm/Component.d.ts +2 -1
- package/cssm/components/Tick/index.d.ts +2 -1
- package/cssm/components/TooltipContent/index.d.ts +2 -1
- package/cssm/icons/Circle.d.ts +2 -1
- package/cssm/icons/FilledCircle.d.ts +2 -1
- package/cssm/icons/Point.d.ts +2 -1
- package/cssm/icons/StrokeCircle.d.ts +2 -1
- package/esm/Component.d.ts +2 -1
- package/esm/Component.js +1 -1
- package/esm/components/Dot/index.css +8 -8
- package/esm/components/Dot/index.js +1 -1
- package/esm/components/Legends/index.css +8 -8
- package/esm/components/Legends/index.js +1 -1
- package/esm/components/Tick/index.css +4 -4
- package/esm/components/Tick/index.d.ts +2 -1
- package/esm/components/Tick/index.js +1 -1
- package/esm/components/TooltipContent/index.css +7 -7
- package/esm/components/TooltipContent/index.d.ts +2 -1
- package/esm/components/TooltipContent/index.js +1 -1
- package/esm/icons/Circle.d.ts +2 -1
- package/esm/icons/FilledCircle.d.ts +2 -1
- package/esm/icons/Point.d.ts +2 -1
- package/esm/icons/StrokeCircle.d.ts +2 -1
- package/esm/index.css +5 -5
- package/icons/Circle.d.ts +2 -1
- package/icons/FilledCircle.d.ts +2 -1
- package/icons/Point.d.ts +2 -1
- package/icons/StrokeCircle.d.ts +2 -1
- package/index.css +5 -5
- package/modern/Component.d.ts +2 -1
- package/modern/Component.js +1 -1
- package/modern/components/Dot/index.css +8 -8
- package/modern/components/Dot/index.js +1 -1
- package/modern/components/Legends/index.css +8 -8
- package/modern/components/Legends/index.js +1 -1
- package/modern/components/Tick/index.css +4 -4
- package/modern/components/Tick/index.d.ts +2 -1
- package/modern/components/Tick/index.js +1 -1
- package/modern/components/TooltipContent/index.css +7 -7
- package/modern/components/TooltipContent/index.d.ts +2 -1
- package/modern/components/TooltipContent/index.js +1 -1
- package/modern/icons/Circle.d.ts +2 -1
- package/modern/icons/FilledCircle.d.ts +2 -1
- package/modern/icons/Point.d.ts +2 -1
- package/modern/icons/StrokeCircle.d.ts +2 -1
- package/modern/index.css +5 -5
- package/package.json +2 -2
- package/src/Component.tsx +404 -0
- package/src/components/CustomizedLabel.tsx +29 -0
- package/src/components/Dot/index.module.css +22 -0
- package/src/components/Dot/index.tsx +79 -0
- package/src/components/Legends/index.module.css +36 -0
- package/src/components/Legends/index.tsx +85 -0
- package/src/components/LinearGradient.tsx +16 -0
- package/src/components/RectBar.tsx +50 -0
- package/src/components/Tick/index.module.css +16 -0
- package/src/components/Tick/index.tsx +32 -0
- package/src/components/TooltipContent/index.module.css +51 -0
- package/src/components/TooltipContent/index.tsx +83 -0
- package/src/hoc/Customized.jsx +7 -0
- package/src/hooks/usePathBar/index.tsx +56 -0
- package/src/hooks/usePathBar/utils/getRadius.ts +5 -0
- package/src/hooks/useSettings/index.tsx +66 -0
- package/src/hooks/useSettings/utils/setComposedChartsMargin.ts +29 -0
- package/src/hooks/useSettings/utils/setDatas.ts +53 -0
- package/src/hooks/useSettings/utils/setGradientCharts.ts +43 -0
- package/src/hooks/useSettings/utils/setLegendMargin.ts +16 -0
- package/src/hooks/useSettings/utils/sortByIndex.ts +10 -0
- package/src/icons/Circle.tsx +12 -0
- package/src/icons/CircleLine.tsx +13 -0
- package/src/icons/FilledCircle.tsx +25 -0
- package/src/icons/Point.tsx +13 -0
- package/src/icons/StrokeCircle.tsx +12 -0
- package/src/index.module.css +21 -0
- package/src/index.ts +2 -0
- package/src/types/brush.types.ts +50 -0
- package/src/types/cartesianGrid.types.ts +26 -0
- package/src/types/chart.types.ts +81 -0
- package/src/types/composedChart.types.ts +36 -0
- package/src/types/index.ts +14 -0
- package/src/types/labelList.types.ts +5 -0
- package/src/types/legend.types.ts +35 -0
- package/src/types/options.types.ts +69 -0
- package/src/types/payload.types.ts +33 -0
- package/src/types/responsiveContainer.types.ts +9 -0
- package/src/types/seria.types.ts +86 -0
- package/src/types/tooltip.types.ts +85 -0
- package/src/types/utils/axis.types.ts +88 -0
- package/src/types/utils/coordinates.types.ts +11 -0
- package/src/types/utils/data.types.ts +19 -0
- package/src/types/utils/dot.types.ts +88 -0
- package/src/types/utils/gradient.types.ts +33 -0
- package/src/types/utils/index.ts +6 -0
- package/src/types/utils/tick.types.ts +38 -0
- package/src/types/xAxis.types.ts +18 -0
- 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
|
+
);
|