UNPKG

animated-charts

Version:

Animated chart web components for all frameworks (React, Angular, Vue, HTML)

27 lines (26 loc) 4.21 kB
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