animated-charts
Version:
Animated chart web components for all frameworks (React, Angular, Vue, HTML)
27 lines (26 loc) • 4.21 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import "./scatter-plot.css";
export const ScatterPlot = ({ data, width = 500, height = 350, colors = ["#007bff"], animationDuration = 800, xLabel = "X Axis", yLabel = "Y Axis", ariaLabel = "Scatter Plot Chart", }) => {
// Find min/max for scaling
const minX = Math.min(...data.map((d) => d.x));
const maxX = Math.max(...data.map((d) => d.x));
const minY = Math.min(...data.map((d) => d.y));
const maxY = Math.max(...data.map((d) => d.y));
// Padding for axes
const padding = 40;
const plotWidth = width - padding * 2;
const plotHeight = height - padding * 2;
// Scale functions
const scaleX = (x) => padding + ((x - minX) / (maxX - minX || 1)) * plotWidth;
const scaleY = (y) => height - padding - ((y - minY) / (maxY - minY || 1)) * plotHeight;
// Tooltip state
const [tooltip, setTooltip] = useState(null);
// Grid lines
const gridLines = 5;
const xTicks = Array.from({ length: gridLines }, (_, i) => minX + ((maxX - minX) * i) / (gridLines - 1));
const yTicks = Array.from({ length: gridLines }, (_, i) => minY + ((maxY - minY) * i) / (gridLines - 1));
return (_jsx("div", { className: "scatter-plot-responsive", style: { width: "100%", maxWidth: width }, children: _jsxs("svg", { width: "100%", height: height, viewBox: `0 0 ${width} ${height}`, className: "scatter-plot", role: "img", "aria-label": ariaLabel, children: [xTicks.map((tick, i) => (_jsx("line", { x1: scaleX(tick), x2: scaleX(tick), y1: padding, y2: height - padding, stroke: "#e9ecef", strokeDasharray: "4 2" }, `xgrid-${i}`))), yTicks.map((tick, i) => (_jsx("line", { y1: scaleY(tick), y2: scaleY(tick), x1: padding, x2: width - padding, stroke: "#e9ecef", strokeDasharray: "4 2" }, `ygrid-${i}`))), _jsx("line", { x1: padding, x2: width - padding, y1: height - padding, y2: height - padding, stroke: "#333", strokeWidth: 2 }), _jsx("line", { x1: padding, x2: padding, y1: padding, y2: height - padding, stroke: "#333", strokeWidth: 2 }), _jsx("text", { x: width / 2, y: height - 5, textAnchor: "middle", fontSize: 14, fill: "#333", children: xLabel }), _jsx("text", { x: 15, y: height / 2, textAnchor: "middle", fontSize: 14, fill: "#333", transform: `rotate(-90 15,${height / 2})`, children: yLabel }), xTicks.map((tick, i) => (_jsx("text", { x: scaleX(tick), y: height - padding + 18, textAnchor: "middle", fontSize: 12, fill: "#666", children: Number(tick.toFixed(2)) }, `xtick-${i}`))), yTicks.map((tick, i) => (_jsx("text", { x: padding - 8, y: scaleY(tick) + 4, textAnchor: "end", fontSize: 12, fill: "#666", children: Number(tick.toFixed(2)) }, `ytick-${i}`))), _jsx(AnimatePresence, { children: data.map((point, i) => (_jsx(motion.circle, { cx: scaleX(point.x), cy: scaleY(point.y), r: point.r || 8, fill: point.color || colors[i % colors.length], initial: { r: 0, opacity: 0 }, animate: { r: point.r || 8, opacity: 1 }, exit: { r: 0, opacity: 0 }, transition: { duration: animationDuration / 1000, type: "spring", stiffness: 120 }, tabIndex: 0, "aria-label": point.label || `Point (${point.x}, ${point.y})`, onMouseEnter: () => setTooltip({ x: scaleX(point.x), y: scaleY(point.y), label: point.label, value: { x: point.x, y: point.y } }), onFocus: () => setTooltip({ x: scaleX(point.x), y: scaleY(point.y), label: point.label, value: { x: point.x, y: point.y } }), onMouseLeave: () => setTooltip(null), onBlur: () => setTooltip(null), style: { filter: "drop-shadow(0 2px 6px rgba(0,0,0,0.10))", cursor: "pointer" } }, i))) }), tooltip && (_jsx("foreignObject", { x: tooltip.x + 10, y: tooltip.y - 40, width: 120, height: 40, pointerEvents: "none", children: _jsxs("div", { className: "scatter-tooltip", style: { background: "rgba(0,0,0,0.8)", color: "#fff", borderRadius: 6, padding: "6px 10px", fontSize: 13, pointerEvents: "none" }, children: [tooltip.label && _jsx("div", { style: { fontWeight: 600 }, children: tooltip.label }), _jsxs("div", { children: ["X: ", tooltip.value.x] }), _jsxs("div", { children: ["Y: ", tooltip.value.y] })] }) }))] }) }));
};
//# sourceMappingURL=scatter-plot.js.map