mirror of
https://github.com/spacedriveapp/spacedrive
synced 2024-07-02 11:13:29 +00:00
fixed hover effects (transition still broken)
This commit is contained in:
parent
91860b8b13
commit
22b8c47403
|
@ -1,4 +1,4 @@
|
||||||
import React, { useState, useEffect, useRef, FunctionComponent } from 'react';
|
import React, { useState, useRef, FunctionComponent } from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
|
|
||||||
export type PieChartProps = {
|
export type PieChartProps = {
|
||||||
|
@ -11,14 +11,6 @@ export type PieChartProps = {
|
||||||
units?: string;
|
units?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const polarToCartesian = (centerX: number, centerY: number, radius: number, angleInDegrees: number) => {
|
|
||||||
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
|
||||||
return {
|
|
||||||
x: centerX + radius * Math.cos(angleInRadians),
|
|
||||||
y: centerY + radius * Math.sin(angleInRadians),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const PieChart: FunctionComponent<PieChartProps> = ({
|
const PieChart: FunctionComponent<PieChartProps> = ({
|
||||||
data,
|
data,
|
||||||
radius,
|
radius,
|
||||||
|
@ -29,26 +21,22 @@ const PieChart: FunctionComponent<PieChartProps> = ({
|
||||||
units = '',
|
units = '',
|
||||||
}) => {
|
}) => {
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
const pieChartRef = useRef<HTMLDivElement>(null);
|
const pieChartRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const handleMouseOut = (event: MouseEvent) => {
|
|
||||||
if (pieChartRef.current && !pieChartRef.current.contains(event.target as Node)) {
|
|
||||||
setHoveredIndex(null);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('mouseout', handleMouseOut);
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('mouseout', handleMouseOut);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const total = data.reduce((sum, item) => sum + item.value, 0);
|
const total = data.reduce((sum, item) => sum + item.value, 0);
|
||||||
const margin = 10;
|
const margin = 10;
|
||||||
const svgSize = 2 * (radius + strokeWidth) + margin * 2;
|
const svgSize = 2 * (radius + strokeWidth) + margin * 2;
|
||||||
const center = radius + strokeWidth + margin;
|
const center = radius + strokeWidth + margin;
|
||||||
|
|
||||||
|
const polarToCartesian = (centerX: number, centerY: number, radius: number, angleInDegrees: number) => {
|
||||||
|
const angleInRadians = ((angleInDegrees - 90) * Math.PI) / 180.0;
|
||||||
|
return {
|
||||||
|
x: centerX + radius * Math.cos(angleInRadians),
|
||||||
|
y: centerY + radius * Math.sin(angleInRadians),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const renderSlices = () => {
|
const renderSlices = () => {
|
||||||
let cumulativeValue = 0;
|
let cumulativeValue = 0;
|
||||||
|
|
||||||
|
@ -67,75 +55,75 @@ const PieChart: FunctionComponent<PieChartProps> = ({
|
||||||
const innerEnd = polarToCartesian(center, center, innerRadius!, startAngle);
|
const innerEnd = polarToCartesian(center, center, innerRadius!, startAngle);
|
||||||
const largeArcFlag = sliceAngle > 180 ? 1 : 0;
|
const largeArcFlag = sliceAngle > 180 ? 1 : 0;
|
||||||
|
|
||||||
const outerPathData = [
|
const pathData = [
|
||||||
`M ${outerStart.x} ${outerStart.y}`,
|
`M ${outerStart.x} ${outerStart.y}`,
|
||||||
`A ${radius} ${radius} 0 ${largeArcFlag} 1 ${outerEnd.x} ${outerEnd.y}`,
|
`A ${radius} ${radius} 0 ${largeArcFlag} 1 ${outerEnd.x} ${outerEnd.y}`,
|
||||||
`L ${center} ${center}`,
|
`L ${innerStart.x} ${innerStart.y}`,
|
||||||
`Z`,
|
|
||||||
].join(' ');
|
|
||||||
|
|
||||||
const innerPathData = [
|
|
||||||
`M ${innerStart.x} ${innerStart.y}`,
|
|
||||||
`A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerEnd.x} ${innerEnd.y}`,
|
`A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerEnd.x} ${innerEnd.y}`,
|
||||||
`L ${center} ${center}`,
|
|
||||||
`Z`,
|
`Z`,
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
const fullPathData = [
|
const hoverPathData = [
|
||||||
`M ${outerStart.x} ${outerStart.y}`,
|
`M ${polarToCartesian(center, center, radius + 10, startAngle).x} ${polarToCartesian(center, center, radius + 10, startAngle).y}`,
|
||||||
`A ${radius} ${radius} 0 ${largeArcFlag} 1 ${outerEnd.x} ${outerEnd.y}`,
|
`A ${radius + 10} ${radius + 10} 0 ${largeArcFlag} 1 ${polarToCartesian(center, center, radius + 10, startAngle + sliceAngle).x} ${polarToCartesian(center, center, radius + 10, startAngle + sliceAngle).y}`,
|
||||||
`L ${innerEnd.x} ${innerEnd.y}`,
|
`L ${polarToCartesian(center, center, innerRadius! - 10, startAngle + sliceAngle).x} ${polarToCartesian(center, center, innerRadius! - 10, startAngle + sliceAngle).y}`,
|
||||||
`A ${innerRadius} ${innerRadius} 0 ${largeArcFlag} 0 ${innerStart.x} ${innerStart.y}`,
|
`A ${innerRadius! - 10} ${innerRadius! - 10} 0 ${largeArcFlag} 0 ${polarToCartesian(center, center, innerRadius! - 10, startAngle).x} ${polarToCartesian(center, center, innerRadius! - 10, startAngle).y}`,
|
||||||
`Z`,
|
`Z`,
|
||||||
].join(' ');
|
].join(' ');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g
|
<g className={clsx('transition-transform duration-300 ease-out', {
|
||||||
key={index}
|
'opacity-100': isHovered,
|
||||||
onMouseOver={() => setHoveredIndex(index)}
|
'opacity-50': hoveredIndex !== null && !isHovered,
|
||||||
onMouseOut={() => setHoveredIndex(null)}
|
})}
|
||||||
className={clsx('transition-transform duration-300 ease-out', {
|
key={index}>
|
||||||
'opacity-100': isHovered,
|
<path
|
||||||
'opacity-50': hoveredIndex !== null && !isHovered,
|
d={hoverPathData}
|
||||||
})}
|
fill="transparent"
|
||||||
style={{
|
pointerEvents="bounding-box"
|
||||||
transform: isHovered ? `translate(${translation.x}px, ${translation.y}px) scale(1.05)` : 'scale(1)',
|
onMouseEnter={() => setHoveredIndex(index)}
|
||||||
opacity: hoveredIndex !== null && !isHovered ? 0.5 : 1,
|
onMouseLeave={() => setHoveredIndex(null)}
|
||||||
transition: 'transform 0.3s ease-out, opacity 0.3s ease-out',
|
style={{ cursor: 'pointer' }}
|
||||||
zIndex: isHovered ? 10 : 1,
|
/>
|
||||||
}}
|
<path
|
||||||
>
|
d={pathData}
|
||||||
{/* Full path for the whole slice, used for hover effect */}
|
fill={colors[index % colors.length]}
|
||||||
<path d={fullPathData} fill="transparent" />
|
stroke="white"
|
||||||
{/* Path for the outer portion of the slice, with color */}
|
strokeWidth={strokeWidth}
|
||||||
<path d={outerPathData} fill={colors[index % colors.length]} />
|
className={clsx('transition-transform duration-300 ease-out', {
|
||||||
|
'opacity-100': isHovered,
|
||||||
|
'opacity-50': hoveredIndex !== null && !isHovered,
|
||||||
|
})}
|
||||||
|
style={{
|
||||||
|
transform: isHovered ? `translate(${translation.x}px, ${translation.y}px) scale(1.05)` : 'scale(1)',
|
||||||
|
opacity: hoveredIndex !== null && !isHovered ? 0.5 : 1,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={pieChartRef} className="relative inline-block">
|
<div ref={pieChartRef} className="relative inline-block transition-all">
|
||||||
<div
|
<div
|
||||||
className={clsx('relative', className)}
|
className={clsx('relative', className)}
|
||||||
style={{ width: `${svgSize}px`, height: `${svgSize}px` }}
|
style={{ width: `${svgSize}px`, height: `${svgSize}px` }}
|
||||||
>
|
>
|
||||||
<svg width={svgSize} height={svgSize} viewBox={`0 0 ${svgSize} ${svgSize}`}>
|
<svg width={svgSize} height={svgSize} viewBox={`0 0 ${svgSize} ${svgSize}`}>
|
||||||
{renderSlices()}
|
{renderSlices()}
|
||||||
{hoveredIndex !== null && (
|
|
||||||
<text
|
|
||||||
x={center}
|
|
||||||
y={center}
|
|
||||||
textAnchor="middle"
|
|
||||||
dominantBaseline="central"
|
|
||||||
className="pointer-events-none text-lg font-bold transition-opacity duration-300 ease-out"
|
|
||||||
style={{ zIndex: 10 }}
|
|
||||||
>
|
|
||||||
{data[hoveredIndex]?.value}
|
|
||||||
<tspan className="ml-0.5 text-tiny text-ink-faint">{units}</tspan>
|
|
||||||
</text>
|
|
||||||
)}
|
|
||||||
</svg>
|
</svg>
|
||||||
|
{hoveredIndex !== null && (
|
||||||
|
<div
|
||||||
|
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded p-1.5 text-lg font-bold transition-transform duration-300 ease-out"
|
||||||
|
style={{ zIndex: 10 }}
|
||||||
|
>
|
||||||
|
{data[hoveredIndex].value}
|
||||||
|
<span className="ml-0.5 text-tiny text-ink-faint">
|
||||||
|
{units}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in a new issue