react-plot
Version:
Library of React components to render SVG 2D plots.
129 lines • 6.05 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { extent } from 'd3-array';
import { useEffect, useMemo } from 'react';
import { useLegend } from '../../contexts/legendContext.js';
import { usePlotContext, usePlotDispatchContext, } from '../../contexts/plotContext.js';
import { useIsSeriesVisible, useShift } from '../../hooks.js';
import { functionalLabel, functionalShape, functionalStyle, middlePoint, useId, validateAxis, } from '../../utils.js';
import ErrorBars from '../ErrorBars.js';
import { markersMap } from '../Markers.map.js';
export function ScatterSeries(props) {
// Update plot context with data description
const dispatch = usePlotDispatchContext();
const { colorScaler } = usePlotContext();
const [, legendDispatch] = useLegend();
const id = useId(props.id, 'series');
const { xAxis = 'x', yAxis = 'y', data, label, hidden, displayErrorBars = false, xShift: oldXShift = 0, yShift: oldYShift = 0, ...otherProps } = props;
const { markerShape = 'circle', markerStyle = {}, errorBarsStyle, errorBarsCapStyle, errorBarsCapSize, } = otherProps;
const isVisible = useIsSeriesVisible(id);
const { xShift, yShift } = useShift({
xAxis,
yAxis,
xShift: oldXShift,
yShift: oldYShift,
});
const transform = `translate(${xShift},${yShift})`;
useEffect(() => {
if (!hidden) {
legendDispatch({
type: 'ADD_LEGEND_LABEL',
payload: {
id,
label,
colorLine: 'white',
shape: {
color: markerStyle?.fill?.toString() || colorScaler(id),
figure: typeof markerShape === 'string' ? markerShape : 'circle',
hidden: false,
},
},
});
return () => legendDispatch({ type: 'REMOVE_LEGEND_LABEL', payload: { id } });
}
return undefined;
}, [
colorScaler,
hidden,
id,
label,
legendDispatch,
markerShape,
markerStyle?.fill,
]);
useEffect(() => {
const [xMin, xMax] = extent(data, (d) => d.x);
const [yMin, yMax] = extent(data, (d) => d.y);
const x = { min: xMin, max: xMax, axisId: xAxis, shift: oldXShift };
const y = { min: yMin, max: yMax, axisId: yAxis, shift: oldYShift };
dispatch({ type: 'addSeries', payload: { id, x, y, label, data } });
// Delete information on unmount
return () => dispatch({ type: 'removeSeries', payload: { id } });
}, [dispatch, id, data, xAxis, yAxis, label, oldXShift, oldYShift]);
if (hidden)
return null;
// Render stateless plot component
const inheritedProps = {
data,
xAxis,
yAxis,
};
const errorBarsProps = {
hidden: !displayErrorBars,
style: errorBarsStyle,
capStyle: errorBarsCapStyle,
capSize: errorBarsCapSize,
transform,
};
return isVisible ? (_jsxs("g", { children: [_jsx(ErrorBars, { ...inheritedProps, ...errorBarsProps }), _jsx(ScatterSeriesRender, { ...otherProps, ...inheritedProps, id: id, transform: transform })] })) : null;
}
function ScatterSeriesRender({ id, data, xAxis, yAxis, markerShape = 'circle', markerSize = 8, markerStyle = {}, pointLabel = '', pointLabelStyle = {}, displayMarkers = true, lineStyle: pointLineStyle = {}, displayLines = false, transform, }) {
// Get scales from context
const { axisContext, colorScaler } = usePlotContext();
const [xScale, yScale] = validateAxis(axisContext, xAxis, yAxis);
// calculates the path to display
const markers = useMemo(() => {
if (xScale === undefined || yScale === undefined) {
return null;
}
const color = colorScaler(id);
const defaultColor = { fill: color, stroke: color };
const markers = data.map((point, i) => {
const style = functionalStyle(defaultColor, markerStyle, point, i, data);
// Show marker
const Marker = markersMap[functionalShape(markerShape, point, i, data)];
const label = functionalLabel(pointLabel, point, i, data);
const labelStyle = functionalStyle({}, pointLabelStyle, point, i, data);
const Lines = [];
if (displayLines) {
const lineStyle = functionalStyle({}, pointLineStyle, point, i, data);
const prePoint = i > 0 ? middlePoint(point, data[i - 1]) : undefined;
const nextPoint = data[i + 1]
? middlePoint(point, data[i + 1])
: undefined;
const PreviousLine = prePoint ? (_jsx("line", { x1: 0, y1: 0, x2: xScale(prePoint.x) - xScale(point.x), y2: yScale(prePoint.y) - yScale(point.y), style: { stroke: style.fill, ...lineStyle } }, `markers-${i}-previous`)) : null;
const NextLine = nextPoint ? (_jsx("line", { x1: 0, y1: 0, x2: xScale(nextPoint.x) - xScale(point.x), y2: yScale(nextPoint.y) - yScale(point.y), style: { stroke: style.fill, ...lineStyle } }, `markers-${i}-next`)) : null;
Lines.push(PreviousLine, NextLine);
}
return (_jsxs("g", { transform: `translate(${xScale(point.x)}, ${yScale(point.y)})`, children: [Lines, displayMarkers ? (_jsx(Marker, { size: markerSize, style: { stroke: style.fill, ...style } })) : null, label ? _jsx("text", { style: labelStyle, children: label }) : null] }, `markers-${i}`));
});
return markers;
}, [
xScale,
yScale,
colorScaler,
id,
data,
markerStyle,
markerShape,
pointLabel,
pointLabelStyle,
pointLineStyle,
displayLines,
displayMarkers,
markerSize,
]);
if (!markers)
return null;
return (_jsx("g", { transform: transform, className: "markers", children: markers }));
}
//# sourceMappingURL=ScatterSeries.js.map