修复 控制台警告信息 图标大小在初始化 会警告 长宽-1
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
ResponsiveContainer,
|
||||
LineChart,
|
||||
Line,
|
||||
XAxis,
|
||||
@@ -15,8 +14,10 @@ import {
|
||||
Bar,
|
||||
} from 'recharts';
|
||||
|
||||
|
||||
// =========================
|
||||
// 默认颜色池
|
||||
// =========================
|
||||
|
||||
const DEFAULT_COLORS = [
|
||||
'#1677ff',
|
||||
'#52c41a',
|
||||
@@ -29,11 +30,114 @@ const DEFAULT_COLORS = [
|
||||
];
|
||||
|
||||
|
||||
// =========================
|
||||
// 通用自适应容器
|
||||
// =========================
|
||||
//
|
||||
// 不再使用 ResponsiveContainer。
|
||||
// 通过 ResizeObserver 获取真实尺寸,
|
||||
// 只有 width / height > 0 时才渲染 Recharts。
|
||||
// 这样可以彻底避免:
|
||||
//
|
||||
// width(-1) and height(-1)
|
||||
//
|
||||
// =========================
|
||||
|
||||
const ChartResizeContainer = ({
|
||||
children,
|
||||
width = '100%',
|
||||
height = 360,
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const [size, setSize] = useState({
|
||||
width: 0,
|
||||
height: 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const element = containerRef.current;
|
||||
|
||||
if (!element) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updateSize = () => {
|
||||
const rect = element.getBoundingClientRect();
|
||||
|
||||
const nextWidth = Math.floor(rect.width);
|
||||
const nextHeight = Math.floor(rect.height);
|
||||
|
||||
// 非法尺寸直接忽略
|
||||
if (nextWidth <= 0 || nextHeight <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setSize((prev) => {
|
||||
if (
|
||||
prev.width === nextWidth &&
|
||||
prev.height === nextHeight
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
|
||||
return {
|
||||
width: nextWidth,
|
||||
height: nextHeight,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
// 首次主动测量
|
||||
updateSize();
|
||||
|
||||
// 后续监听尺寸变化
|
||||
const observer = new ResizeObserver(() => {
|
||||
updateSize();
|
||||
});
|
||||
|
||||
observer.observe(element);
|
||||
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={className}
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
boxSizing: 'border-box',
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{size.width > 0 && size.height > 0
|
||||
? React.cloneElement(children, {
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
})
|
||||
: null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// =========================
|
||||
// Universal Line Chart
|
||||
// =========================
|
||||
|
||||
export const UniversalLineChart = ({
|
||||
data,
|
||||
data = [],
|
||||
xKey,
|
||||
series,
|
||||
series = [],
|
||||
width = '100%',
|
||||
height = 360,
|
||||
showGrid = true,
|
||||
@@ -43,6 +147,7 @@ export const UniversalLineChart = ({
|
||||
smooth = true,
|
||||
dot = false,
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
@@ -50,48 +155,80 @@ export const UniversalLineChart = ({
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
boxSizing: 'border-box',
|
||||
background: backgroundColor,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
||||
<LineChart data={data} minWidth={0} minHeight={0} width="100%" height="100%" >
|
||||
<ChartResizeContainer
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<LineChart data={data}>
|
||||
{showGrid && (
|
||||
<CartesianGrid strokeDasharray="3 3" opacity={0.25} />
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
opacity={0.25}
|
||||
/>
|
||||
)}
|
||||
|
||||
<XAxis dataKey={xKey} />
|
||||
|
||||
<YAxis />
|
||||
|
||||
{showTooltip && <Tooltip />}
|
||||
|
||||
{showLegend && <Legend />}
|
||||
|
||||
{series.map((item, index) => (
|
||||
<Line
|
||||
key={item.key}
|
||||
type={smooth ? 'monotone' : 'linear'}
|
||||
type={
|
||||
smooth
|
||||
? 'monotone'
|
||||
: 'linear'
|
||||
}
|
||||
dataKey={item.key}
|
||||
name={item.name}
|
||||
stroke={item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
||||
strokeWidth={item.strokeWidth || 2}
|
||||
strokeDasharray={item.dashed ? '6 6' : undefined}
|
||||
stroke={
|
||||
item.color ||
|
||||
DEFAULT_COLORS[
|
||||
index %
|
||||
DEFAULT_COLORS.length
|
||||
]
|
||||
}
|
||||
strokeWidth={
|
||||
item.strokeWidth || 2
|
||||
}
|
||||
strokeDasharray={
|
||||
item.dashed
|
||||
? '6 6'
|
||||
: undefined
|
||||
}
|
||||
dot={dot}
|
||||
activeDot={{ r: 6 }}
|
||||
/>
|
||||
))}
|
||||
</LineChart>
|
||||
</ResponsiveContainer>
|
||||
</ChartResizeContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// =========================
|
||||
// Universal Bar Chart
|
||||
// =========================
|
||||
|
||||
export const UniversalBarChart = ({
|
||||
data,
|
||||
data = [],
|
||||
xKey,
|
||||
series,
|
||||
series = [],
|
||||
width = '100%',
|
||||
height = 360,
|
||||
showGrid = true,
|
||||
@@ -102,6 +239,7 @@ export const UniversalBarChart = ({
|
||||
radius = 6,
|
||||
stack = false,
|
||||
className,
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
@@ -109,21 +247,34 @@ export const UniversalBarChart = ({
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
boxSizing: 'border-box',
|
||||
background: backgroundColor,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
||||
<BarChart data={data} minWidth={0} minHeight={0} width="100%" height="100%" >
|
||||
<ChartResizeContainer
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<BarChart data={data}>
|
||||
{showGrid && (
|
||||
<CartesianGrid strokeDasharray="3 3" opacity={0.25} />
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
opacity={0.25}
|
||||
/>
|
||||
)}
|
||||
|
||||
<XAxis dataKey={xKey} />
|
||||
|
||||
<YAxis />
|
||||
|
||||
{showTooltip && <Tooltip />}
|
||||
|
||||
{showLegend && <Legend />}
|
||||
|
||||
{series.map((item, index) => (
|
||||
@@ -131,22 +282,35 @@ export const UniversalBarChart = ({
|
||||
key={item.key}
|
||||
dataKey={item.key}
|
||||
name={item.name}
|
||||
fill={item.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
||||
fill={
|
||||
item.color ||
|
||||
DEFAULT_COLORS[
|
||||
index %
|
||||
DEFAULT_COLORS.length
|
||||
]
|
||||
}
|
||||
barSize={barSize}
|
||||
radius={radius}
|
||||
stackId={stack ? 'stack' : undefined}
|
||||
stackId={
|
||||
stack
|
||||
? 'stack'
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</ChartResizeContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// =========================
|
||||
// Universal Pie Chart
|
||||
// =========================
|
||||
|
||||
export const UniversalPieChart = ({
|
||||
data,
|
||||
data = [],
|
||||
width = '100%',
|
||||
height = 360,
|
||||
showLegend = true,
|
||||
@@ -157,7 +321,7 @@ export const UniversalPieChart = ({
|
||||
paddingAngle = 2,
|
||||
showLabel = true,
|
||||
className,
|
||||
style
|
||||
style,
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
@@ -165,15 +329,23 @@ export const UniversalPieChart = ({
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
minWidth: 0,
|
||||
minHeight: 0,
|
||||
boxSizing: 'border-box',
|
||||
background: backgroundColor,
|
||||
borderRadius: 12,
|
||||
padding: 12,
|
||||
...style
|
||||
overflow: 'hidden',
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
<ResponsiveContainer width="100%" height="100%" minWidth={0} minHeight={0}>
|
||||
<PieChart minWidth={0} minHeight={0} width="100%" height="100%" >
|
||||
<ChartResizeContainer
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<PieChart>
|
||||
{showTooltip && <Tooltip />}
|
||||
|
||||
{showLegend && <Legend />}
|
||||
|
||||
<Pie
|
||||
@@ -190,37 +362,86 @@ export const UniversalPieChart = ({
|
||||
{data.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={entry.color || DEFAULT_COLORS[index % DEFAULT_COLORS.length]}
|
||||
fill={
|
||||
entry.color ||
|
||||
DEFAULT_COLORS[
|
||||
index %
|
||||
DEFAULT_COLORS.length
|
||||
]
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Pie>
|
||||
</PieChart>
|
||||
</ResponsiveContainer>
|
||||
</ChartResizeContainer>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// =========================
|
||||
// 使用示例
|
||||
// =========================
|
||||
|
||||
export const DemoCharts = () => {
|
||||
const lineData = [
|
||||
{ month: '1月', uv: 400, pv: 240, amt: 240 },
|
||||
{ month: '2月', uv: 300, pv: 456, amt: 300 },
|
||||
{ month: '3月', uv: 500, pv: 139, amt: 200 },
|
||||
{ month: '4月', uv: 278, pv: 390, amt: 278 },
|
||||
{ month: '5月', uv: 189, pv: 480, amt: 189 },
|
||||
{
|
||||
month: '1月',
|
||||
uv: 400,
|
||||
pv: 240,
|
||||
amt: 240,
|
||||
},
|
||||
{
|
||||
month: '2月',
|
||||
uv: 300,
|
||||
pv: 456,
|
||||
amt: 300,
|
||||
},
|
||||
{
|
||||
month: '3月',
|
||||
uv: 500,
|
||||
pv: 139,
|
||||
amt: 200,
|
||||
},
|
||||
{
|
||||
month: '4月',
|
||||
uv: 278,
|
||||
pv: 390,
|
||||
amt: 278,
|
||||
},
|
||||
{
|
||||
month: '5月',
|
||||
uv: 189,
|
||||
pv: 480,
|
||||
amt: 189,
|
||||
},
|
||||
];
|
||||
|
||||
const pieData = [
|
||||
{ name: 'Vue', value: 40 },
|
||||
{ name: 'React', value: 35 },
|
||||
{ name: 'Angular', value: 25 },
|
||||
{
|
||||
name: 'Vue',
|
||||
value: 40,
|
||||
},
|
||||
{
|
||||
name: 'React',
|
||||
value: 35,
|
||||
},
|
||||
{
|
||||
name: 'Angular',
|
||||
value: 25,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-6 p-6">
|
||||
<div
|
||||
className="
|
||||
grid
|
||||
grid-cols-1
|
||||
gap-6
|
||||
p-6
|
||||
min-w-0
|
||||
"
|
||||
>
|
||||
<UniversalLineChart
|
||||
data={lineData}
|
||||
xKey="month"
|
||||
|
||||
Reference in New Issue
Block a user