// PricingChart.jsx import React, { useState, useEffect, useRef } from 'react'; import { useScrollToTop } from '../hooks/useScrollToTop'; import '../styles/PricingChart.css'; const ChartLegend = ({ onLegendClick, highlightedBarTypes, showPricing = true }) => { if (!showPricing) return null; return (
onLegendClick('input')} style={{ cursor: 'pointer', opacity: highlightedBarTypes.input ? 1 : 0.5 }} >
Input Price
onLegendClick('output')} style={{ cursor: 'pointer', opacity: highlightedBarTypes.output ? 1 : 0.5 }} >
Output Price
); }; const ChartBar = ({ price, type, maxPrice, highlighted, score }) => { const getBarHeight = () => { if (score !== undefined) { return (score / maxPrice) * 200; } return (price / maxPrice) * 200; }; return (
{score !== undefined ? score : price}
); }; const ProviderColumn = ({ provider, maxPrice, highlightedBarTypes, showPricing = true }) => (
{showPricing ? ( <> ) : ( )}
{`${provider.name} {provider.name}
); const YAxis = ({ maxPrice }) => { const numberOfTicks = 5; const tickValues = []; for (let i = 0; i <= numberOfTicks; i++) { const value = ((maxPrice / numberOfTicks) * i).toFixed(2); tickValues.push(value); } return (
{tickValues.reverse().map((value, index) => (
{value}
))}
); }; const GridLines = () => (
{[...Array(5)].map((_, index) => (
))}
); const PricingChart = ({ data, showPricing = true }) => { useScrollToTop(); const [highlightedBarTypes, setHighlightedBarTypes] = useState({ input: true, output: true, }); const chartAreaRef = useRef(null); const [hasScroll, setHasScroll] = useState(false); const [showScrollHint, setShowScrollHint] = useState(false); useEffect(() => { const checkScroll = () => { if (chartAreaRef.current) { const { scrollWidth, clientWidth, scrollLeft } = chartAreaRef.current; setHasScroll(scrollWidth > clientWidth); // 只在滚动到最左侧时显示提示 setShowScrollHint(scrollWidth > clientWidth && scrollLeft === 0); } }; const handleScroll = () => { if (chartAreaRef.current) { const { scrollLeft } = chartAreaRef.current; // 当用户开始滚动时隐藏提示 if (scrollLeft > 0) { setShowScrollHint(false); } } }; checkScroll(); window.addEventListener('resize', checkScroll); if (chartAreaRef.current) { chartAreaRef.current.addEventListener('scroll', handleScroll); } return () => { window.removeEventListener('resize', checkScroll); if (chartAreaRef.current) { chartAreaRef.current.removeEventListener('scroll', handleScroll); } }; }, [data]); const handleScrollHintClick = () => { if (chartAreaRef.current) { const { scrollWidth, clientWidth } = chartAreaRef.current; chartAreaRef.current.scrollTo({ left: scrollWidth - clientWidth, behavior: 'smooth' }); setShowScrollHint(false); } }; const handleLegendClick = (barType) => { setHighlightedBarTypes((prevState) => ({ ...prevState, [barType]: !prevState[barType], })); }; const getMaxPrice = () => { if (!showPricing) { return Math.max(...data.providers.map(provider => provider.score)); } const prices = data.providers.flatMap((provider) => [ provider.inputPrice, provider.outputPrice, ]); return Math.max(...prices); }; const maxPrice = getMaxPrice(); return (

{data.title}

{data.subtitle}

{data.providers.map((provider) => ( ))}
{showScrollHint && (
)}
); }; export default PricingChart;