Skip to content

Commit

Permalink
[LineChart] Added ability to specify x and y keys as functions (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
michaldudak committed Oct 19, 2021
1 parent a2acb6b commit 2ec03e8
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 35 deletions.
22 changes: 11 additions & 11 deletions packages/charts/src/Line/Line.tsx
Expand Up @@ -4,8 +4,8 @@ import * as d3 from 'd3';
import ChartContext from '../ChartContext';
import Scatter from '../Scatter/Scatter';

function points(data, xKey) {
return data.map((d) => ({ [xKey]: d.data[xKey], y: d[1] }));
function points(data, xValueSelector) {
return data.map((d) => ({ x: xValueSelector(d.data), y: d[1] }));
}

const Line = (props) => {
Expand All @@ -19,9 +19,9 @@ const Line = (props) => {
markerShape: markerShapeContext,
smoothed: smoothedContext,
stacked,
xKey,
xValueSelector,
xScale,
yKey,
yValueSelector,
yScale,
} = useContext(ChartContext) as any;

Expand Down Expand Up @@ -55,27 +55,27 @@ const Line = (props) => {
linePath = d3
.line()
// @ts-ignore TODO: Fix me
.x((d) => xScale(d.data[xKey]))
.x((d) => xScale(xValueSelector(d.data)))
.y((d) => -yScale(d[1]));

areaPath = d3
.area()
// @ts-ignore TODO: Fix me
.x((d) => xScale(d.data[xKey]))
.x((d) => xScale(xValueSelector(d.data)))
.y0((d) => -yScale(d[0]))
.y1((d) => -yScale(d[1]));

pointData = points(chartData, xKey);
pointData = points(chartData, xValueSelector);
} else {
linePath = d3
.line()
.x((d) => xScale(d[xKey]))
.y((d) => -yScale(d[yKey]));
.x((d) => xScale(xValueSelector(d)))
.y((d) => -yScale(yValueSelector(d)));

areaPath = d3
.area()
.x((d) => xScale(d[xKey]))
.y1((d) => -yScale(d[yKey]))
.x((d) => xScale(xValueSelector(d)))
.y1((d) => -yScale(yValueSelector(d)))
.y0(-yScale(yScale.domain()[0]));
}

Expand Down
62 changes: 38 additions & 24 deletions packages/charts/src/LineChart/LineChart.tsx
Expand Up @@ -9,11 +9,6 @@ import useThrottle from '../hooks/useThrottle';
import useTicks from '../hooks/useTicks';
import { getExtent, getMaxDataSetLength, stringRatioToNumber } from '../utils';

interface ChartData<X, Y> {
x: X;
y: Y;
}

interface Margin {
bottom?: number;
left?: number;
Expand All @@ -32,7 +27,9 @@ type MarkerShape =
| 'wye'
| 'none';

export interface LineChartProps<X = unknown, Y = unknown> {
type Scale = 'linear' | 'time' | 'log' | 'point' | 'pow' | 'sqrt' | 'utc';

export interface LineChartProps<Record = unknown, X = unknown, Y = unknown> {
/**
* The keys to use when stacking the data.
*/
Expand All @@ -44,7 +41,7 @@ export interface LineChartProps<X = unknown, Y = unknown> {
/**
* The data to use for the chart.
*/
data: ChartData<X, Y>[] | ChartData<X, Y>[][];
data: Record[] | Record[][];
/**
* The fill color to use for the area.
*/
Expand Down Expand Up @@ -113,38 +110,37 @@ export interface LineChartProps<X = unknown, Y = unknown> {
/**
* Override the calculated domain of the x axis.
*/
xDomain?: X[];
xDomain?: [X] | [X, X];
/**
* The key to use for the x axis.
* The key to use for the x axis or the function to access .
*/

xKey?: string;
xValueSelector?: keyof Record | ((r: Record) => X);
/**
* The scale type to use for the x axis.
*/
xScaleType?: 'linear' | 'time' | 'log' | 'point' | 'pow' | 'sqrt' | 'utc';
xScaleType?: Scale;
/**
* Override the calculated domain of the y axis.
* By default, the domain starts at zero. Set the value to null to calculate the true domain.
*/
yDomain?: Y[];
yDomain?: [Y] | [Y, Y];
/**
* The key to use for the y axis.
*/
yKey?: string;
yValueSelector?: keyof Record | ((r: Record) => Y);
/**
* The scale type to use for the y axis.
*/
yScaleType?: 'linear' | 'time' | 'log' | 'point' | 'pow' | 'sqrt' | 'utc';
yScaleType?: Scale;
}

type LineChartComponent = <X, Y>(
props: LineChartProps<X, Y> & React.RefAttributes<SVGSVGElement>,
) => JSX.Element;

const LineChart = React.forwardRef(function LineChart<X = unknown, Y = unknown>(
props: LineChartProps<X, Y>,
ref: React.Ref<SVGSVGElement>,
const LineChart = React.forwardRef(function LineChart<Record = unknown, X = unknown, Y = unknown>(
props: LineChartProps<Record, X, Y>,
ref: React.ForwardedRef<SVGSVGElement>,
) {
const {
keys,
Expand All @@ -166,10 +162,10 @@ const LineChart = React.forwardRef(function LineChart<X = unknown, Y = unknown>(
smoothed = false,
stacked = false,
xDomain: xDomainProp,
xKey = 'x',
xValueSelector,
xScaleType = 'linear',
yDomain: yDomainProp = [0],
yKey = 'y',
yValueSelector,
yScaleType = 'linear',
...other
} = props;
Expand Down Expand Up @@ -211,8 +207,26 @@ const LineChart = React.forwardRef(function LineChart<X = unknown, Y = unknown>(
marginBottom,
} = dimensions;

const xDomain = getExtent(data, (d) => d[xKey], xDomainProp);
const yDomain = getExtent(data, (d) => d[yKey], yDomainProp);
let xGetter: (record: Record) => X;
if (typeof xValueSelector === 'string') {
xGetter = (record: Record) => record[xValueSelector as keyof Record] as unknown as X;
} else if (typeof xValueSelector === 'function') {
xGetter = xValueSelector;
} else {
xGetter = (record: Record) => (record as any).x as X;
}

let yGetter: (record: Record) => Y;
if (typeof yValueSelector === 'string') {
yGetter = (record: Record) => record[yValueSelector as keyof Record] as unknown as Y;
} else if (typeof yValueSelector === 'function') {
yGetter = yValueSelector;
} else {
yGetter = (record: Record) => (record as any).y as Y;
}

const xDomain = getExtent(data, (d: Record) => xGetter(d), xDomainProp);
const yDomain = getExtent(data, (d: Record) => yGetter(d), yDomainProp);
const xRange = [0, boundedWidth];
const yRange = [0, boundedHeight];
const maxXTicks = getMaxDataSetLength(data) - 1;
Expand Down Expand Up @@ -268,11 +282,11 @@ const LineChart = React.forwardRef(function LineChart<X = unknown, Y = unknown>(
stacked,
mousePosition,
smoothed,
xKey,
xValueSelector: xGetter,
xScale,
xScaleType,
xTicks,
yKey,
yValueSelector: yGetter,
yScale,
yScaleType,
yTicks,
Expand Down

0 comments on commit 2ec03e8

Please sign in to comment.