UNPKG

react-plot

Version:

Library of React components to render SVG 2D plots.

172 lines 6.22 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { euclidean } from 'ml-distance-euclidean'; import { useEffect, useRef } from 'react'; import { closestPoint, toNumber } from '../utils.js'; const HORIZONTAL = new Set(['bottom', 'top']); function infoFromEvent(event, axisContext, stateSeries, target) { const { clientX, clientY, movementX, movementY } = event; const { left, top } = target.getBoundingClientRect(); // Calculate coordinates const xPosition = clientX - left; const yPosition = clientY - top; const coordinates = {}; const clampedCoordinates = {}; const domains = {}; const movement = {}; for (const key in axisContext) { const { scale, clampInDomain, position, domain } = axisContext[key]; if (HORIZONTAL.has(position)) { coordinates[key] = toNumber(scale.invert(xPosition)); movement[key] = toNumber(scale.invert(movementX)) - domain[0]; } else { coordinates[key] = toNumber(scale.invert(yPosition)); movement[key] = toNumber(scale.invert(movementY)) - domain[1]; } clampedCoordinates[key] = clampInDomain(coordinates[key]); domains[key] = domain; } return { event, coordinates, clampedCoordinates, movement, getClosest(method) { return closestCalculation(method, { x: xPosition, y: yPosition }, stateSeries, axisContext); }, domains, }; } function closestCalculation(method, coordinates, stateSeries, axisContext) { const series = {}; switch (method) { case 'x': { for (const { id, x, data, label } of stateSeries) { if (data) { const point = closestPoint(data, coordinates, (point, pos) => { const { scale } = axisContext[x.axisId]; const xVal = pos[x.axisId]; return Math.abs(scale(point.x) - xVal); }); series[id] = { point, label, axis: axisContext[x.axisId] }; } } break; } case 'y': { for (const { id, y, data, label } of stateSeries) { if (data) { const point = closestPoint(data, coordinates, (point, pos) => { const { scale } = axisContext[y.axisId]; const yVal = pos[y.axisId]; return Math.abs(scale(point.y) - yVal); }); series[id] = { point, label, axis: axisContext[y.axisId] }; } } break; } case 'euclidean': { for (const { id, x, y, data, label } of stateSeries) { if (data) { const point = closestPoint(data, coordinates, (point, pos) => { const { scale: xScale } = axisContext[x.axisId]; const { scale: yScale } = axisContext[y.axisId]; const xVal = pos[x.axisId]; const yVal = pos[y.axisId]; return euclidean([xScale(point.x), yScale(point.y)], [xVal, yVal]); }); series[id] = { point, label, axis: { x: axisContext[x.axisId], y: axisContext[y.axisId] }, }; } } break; } default: { throw new Error(`Unknown distance name: ${method}`); } } return series; } const nativeEventMap = { pointerenter: 'onPointerEnter', pointerdown: 'onPointerDown', pointermove: 'onPointerMove', pointerup: 'onPointerUp', pointerleave: 'onPointerLeave', click: 'onClick', dblclick: 'onDoubleClick', wheel: 'onWheel', }; const nativeEventNames = [ 'pointerenter', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'click', 'dblclick', 'wheel', ]; const globalNativeEventNames = [ 'pointermove', 'pointerup', ]; export default function Tracking({ plotId, plotEvents, stateSeries, axisContext, plotHeight, plotWidth, }) { const rectRef = useRef(null); const plotDataRef = useRef({ plotId, plotEvents, stateSeries, axisContext, plotHeight, plotWidth, }); useEffect(() => { plotDataRef.current = { plotId, plotEvents, stateSeries, axisContext, plotHeight, plotWidth, }; }, [axisContext, plotEvents, plotHeight, plotId, plotWidth, stateSeries]); useEffect(() => { const rect = rectRef.current; if (!rect) return; function eventListener(nativeEvent) { if (nativeEvent.type === 'pointerdown') { for (const pointerEvent of globalNativeEventNames) { window.addEventListener(pointerEvent, eventListener); } } else if (nativeEvent.type === 'pointerup') { for (const pointerEvent of globalNativeEventNames) { window.removeEventListener(pointerEvent, eventListener); } } const info = infoFromEvent(nativeEvent, plotDataRef.current.axisContext, plotDataRef.current.stateSeries, rect); plotEvents.handleEvent(plotId, nativeEventMap[nativeEvent.type], info); } for (const eventName of nativeEventNames) { rect.addEventListener(eventName, eventListener); } return () => { for (const eventName of nativeEventNames) { rect.removeEventListener(eventName, eventListener); } for (const eventName of globalNativeEventNames) { window.removeEventListener(eventName, eventListener); } }; }, [plotId, plotEvents]); return (_jsx("rect", { ref: rectRef, width: plotWidth, height: plotHeight, style: { fillOpacity: 0, } })); } //# sourceMappingURL=Tracking.js.map