Skip to content

Instantly share code, notes, and snippets.

@valtism
Created October 26, 2022 00:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save valtism/7af5334243ee6a812f6d890f5a52092a to your computer and use it in GitHub Desktop.
Save valtism/7af5334243ee6a812f6d890f5a52092a to your computer and use it in GitHub Desktop.
import React, { useMemo } from "react";
import { GetLineSegmentsConfig } from "../util/getSplitLineSegments";
import { line } from "../util/D3ShapeFactories";
import { LinePathConfig } from "../types";
export type SplitLinePathRenderer = (renderProps: {
index: number;
segment: { x: number; y: number }[];
styles?: Omit<React.SVGProps<SVGPathElement>, "x" | "y" | "children">;
}) => React.ReactNode;
export type SplitLinePathProps<Datum> = {
/** Array of data segments, where each segment will be a separate path in the rendered line. */
segments: Datum[][];
/** Styles to apply to each segment. If fewer styles are specified than the number of segments, they will be re-used. */
styles: Omit<React.SVGProps<SVGPathElement>, "x" | "y" | "children">[];
/** Override render function which is passed the configured path generator as input. */
children?: SplitLinePathRenderer;
/** className applied to path element. */
className?: string;
} & LinePathConfig<Datum> &
Pick<GetLineSegmentsConfig, "segmentation" | "sampleRate">;
export default function SplitLinePath<Datum>({
children,
className,
curve,
defined,
segmentation,
sampleRate,
segments,
x,
y,
styles,
}: SplitLinePathProps<Datum>) {
// Convert data in all segments to points.
const pointsInSegments = useMemo(() => {
const xFn = typeof x === "number" || typeof x === "undefined" ? () => x : x;
const yFn = typeof y === "number" || typeof y === "undefined" ? () => y : y;
return segments.map((s) =>
s.map((value, i) => ({ x: xFn(value, i, s), y: yFn(value, i, s) }))
);
}, [x, y, segments]);
const pathString = useMemo(() => {
const path = line<Datum>({ x, y, defined, curve });
return path(segments.flat()) || "";
}, [x, y, defined, curve, segments]);
const segs = pathString.split(/(\d+.\d+,\d+.\d+)/g);
// Need to handle 1 segment case
const indexes = pointsInSegments.map((_, i) => {
const firstPoint = pointsInSegments[i]?.[0];
const pointString = Object.values(firstPoint).join();
// +1 to grab first point of next segment
const segmentIndex = segs.indexOf(pointString);
return segmentIndex;
});
indexes.push(segs.length - 2);
// This may not work with some curves
const paths = [];
for (let i = 0; i < indexes.length - 1; i++) {
paths[i] = "M" + segs.slice(indexes[i], indexes[i + 1] + 1).join("");
}
return (
<g>
{paths.map((path, index) => (
<path
className={"visx-linepath " + className}
d={path}
fill="none"
strokeWidth={2}
// without this a datum surrounded by nulls will not be visible
// https://github.com/d3/d3-shape#line_defined
strokeLinecap="round"
{...(styles[index] || styles[index % styles.length])}
/>
))}
</g>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment