Skip to content

Commit

Permalink
feat: extract factories
Browse files Browse the repository at this point in the history
  • Loading branch information
kristw committed Aug 5, 2020
1 parent fd8473c commit 3da1947
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 153 deletions.
41 changes: 41 additions & 0 deletions packages/vx-shape/src/factories/arcPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { arc as d3Arc } from 'd3-shape';
import { Accessor } from '../types/accessor';
import setNumberOrNumberAccessor from '../util/setNumberOrNumberAccessor';

export type ArcPathConfig<Datum> = {
/** Number or accessor function which returns a number, which defines the arc innerRadius. */
innerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc outerRadius. */
outerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc cornerRadius. */
cornerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc startAngle. */
startAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc endAngle. */
endAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc padAngle. */
padAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc padRadius. */
padRadius?: number | Accessor<Datum, number>;
};

export default function arcPath<Datum>({
innerRadius,
outerRadius,
cornerRadius,
startAngle,
endAngle,
padAngle,
padRadius,
}: ArcPathConfig<Datum> = {}) {
const path = d3Arc<Datum>();
if (innerRadius != null) setNumberOrNumberAccessor(path.innerRadius, innerRadius);
if (outerRadius != null) setNumberOrNumberAccessor(path.outerRadius, outerRadius);
if (cornerRadius != null) setNumberOrNumberAccessor(path.cornerRadius, cornerRadius);
if (startAngle != null) setNumberOrNumberAccessor(path.startAngle, startAngle);
if (endAngle != null) setNumberOrNumberAccessor(path.endAngle, endAngle);
if (padAngle != null) setNumberOrNumberAccessor(path.padAngle, padAngle);
if (padRadius != null) setNumberOrNumberAccessor(path.padRadius, padRadius);

return path;
}
45 changes: 45 additions & 0 deletions packages/vx-shape/src/factories/areaPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { area as d3Area, CurveFactory } from 'd3-shape';
import { AccessorForArrayItem } from '../types/accessor';
import setNumberOrNumberAccessor from '../util/setNumberOrNumberAccessor';

export type AreaPathConfig<Datum> = {
/** The defined accessor for the shape. The final area shape includes all points for which this function returns true. By default all points are defined. */
defined?: AccessorForArrayItem<Datum, boolean>;
/** Sets the curve factory (from @vx/curve or d3-curve) for the area generator. Defaults to curveLinear. */
curve?: CurveFactory;
/** Sets the x0 accessor function, and sets x1 to null. */
x?: number | AccessorForArrayItem<Datum, number>;
/** Specifies the x0 accessor function which defaults to d => d[0]. */
x0?: number | AccessorForArrayItem<Datum, number>;
/** Specifies the x1 accessor function which defaults to null. */
x1?: number | AccessorForArrayItem<Datum, number>;
/** Sets the y0 accessor function, and sets y1 to null. */
y?: number | AccessorForArrayItem<Datum, number>;
/** Specifies the y0 accessor function which defaults to d => 0. */
y0?: number | AccessorForArrayItem<Datum, number>;
/** Specifies the y1 accessor function which defaults to d => d[1]. */
y1?: number | AccessorForArrayItem<Datum, number>;
};

export default function areaPath<Datum>({
x,
x0,
x1,
y,
y0,
y1,
defined,
curve,
}: AreaPathConfig<Datum> = {}) {
const path = d3Area<Datum>();
if (x) setNumberOrNumberAccessor(path.x, x);
if (x0) setNumberOrNumberAccessor(path.x0, x0);
if (x1) setNumberOrNumberAccessor(path.x1, x1);
if (y) setNumberOrNumberAccessor(path.y, y);
if (y0) setNumberOrNumberAccessor(path.y0, y0);
if (y1) setNumberOrNumberAccessor(path.y1, y1);
if (defined) path.defined(defined);
if (curve) path.curve(curve);

return path;
}
24 changes: 24 additions & 0 deletions packages/vx-shape/src/factories/linePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { line as d3Line, CurveFactory, CurveFactoryLineOnly } from 'd3-shape';
import { AccessorForArrayItem } from '../types/accessor';
import setNumberOrNumberAccessor from '../util/setNumberOrNumberAccessor';

export type LinePathConfig<Datum> = {
/** The defined accessor for the shape. The final line shape includes all points for which this function returns true. By default all points are defined. */
defined?: AccessorForArrayItem<Datum, boolean>;
/** Sets the curve factory (from @vx/curve or d3-curve) for the line generator. Defaults to curveLinear. */
curve?: CurveFactory | CurveFactoryLineOnly;
/** Sets the x0 accessor function, and sets x1 to null. */
x?: number | AccessorForArrayItem<Datum, number>;
/** Sets the y0 accessor function, and sets y1 to null. */
y?: number | AccessorForArrayItem<Datum, number>;
};

export default function linePath<Datum>({ x, y, defined, curve }: LinePathConfig<Datum> = {}) {
const path = d3Line<Datum>();
if (x) setNumberOrNumberAccessor(path.x, x);
if (y) setNumberOrNumberAccessor(path.y, y);
if (defined) path.defined(defined);
if (curve) path.curve(curve);

return path;
}
46 changes: 46 additions & 0 deletions packages/vx-shape/src/factories/piePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { pie as d3Pie } from 'd3-shape';
import { Accessor } from '../types/accessor';
import setNumberOrNumberAccessor from '../util/setNumberOrNumberAccessor';

// eslint-disable-next-line @typescript-eslint/no-explicit-any
type AngleAccessor<Datum> = (this: any, data: Datum[], ...args: any[]) => number;

export type PiePathConfig<Datum> = {
/** Returns the start angle of the overall Pie shape (the first value starts at startAngle), with 0 at -y (12 o’clock) and positive angles proceeding clockwise. */
startAngle?: number | AngleAccessor<Datum>;
/** Returns the end angle of the overall Pie shape (the last value ends at endAngle), with 0 at -y (12 o’clock) and positive angles proceeding clockwise. */
endAngle?: number | AngleAccessor<Datum>;
/** Padding angle of the Pie shape, which sets a fixed linear distance separating adjacent arcs. */
padAngle?: number | AngleAccessor<Datum>;
/** Invoked for each datum, returns the value for a given Pie segment/arc datum. */
value?: Accessor<Datum, number>;
/** Comparator function to sort *arcs*, overridden by sortValues if defined. If sort and sortValues are null, arcs match input data order. */
sort?: null | ((a: Datum, b: Datum) => number);
/** Comparator function to sort arc *values*, overrides sort if defined. If sort and sortValues are null, arcs match input data order. */
sortValues?: null | ((a: number, b: number) => number);
};

export default function piePath<Datum>({
startAngle,
endAngle,
padAngle,
value,
sort,
sortValues,
}: PiePathConfig<Datum> = {}) {
const path = d3Pie<Datum>();

// ts can't distinguish between these method overloads
if (sort === null) path.sort(sort);
else if (sort != null) path.sort(sort);
if (sortValues === null) path.sortValues(sortValues);
else if (sortValues != null) path.sortValues(sortValues);

if (value != null) path.value(value);

if (padAngle != null) setNumberOrNumberAccessor(path.padAngle, padAngle);
if (startAngle != null) setNumberOrNumberAccessor(path.startAngle, startAngle);
if (endAngle != null) setNumberOrNumberAccessor(path.endAngle, endAngle);

return path;
}
29 changes: 29 additions & 0 deletions packages/vx-shape/src/factories/radialLinePath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { radialLine as d3RadialLine, CurveFactory, CurveFactoryLineOnly } from 'd3-shape';
import { AccessorForArrayItem } from '../types/accessor';
import setNumberOrNumberAccessor from '../util/setNumberOrNumberAccessor';

export type RadialLinePathConfig<Datum> = {
/** The defined accessor for the shape. The final radialLine shape includes all points for which this function returns true. By default all points are defined. */
defined?: AccessorForArrayItem<Datum, boolean>;
/** Sets the curve factory (from @vx/curve or d3-curve) for the radialLine generator. Defaults to curveLinear. */
curve?: CurveFactory | CurveFactoryLineOnly;
/** Returns the angle value in radians for a given Datum, with 0 at -y (12 o’clock). */
angle?: number | AccessorForArrayItem<Datum, number>;
/** Returns the radius value in radians for a given Datum, with 0 at the center. */
radius?: number | AccessorForArrayItem<Datum, number>;
};

export default function radialLinePath<Datum>({
angle,
radius,
defined,
curve,
}: RadialLinePathConfig<Datum> = {}) {
const path = d3RadialLine<Datum>();
if (angle) setNumberOrNumberAccessor(path.angle, angle);
if (radius) setNumberOrNumberAccessor(path.radius, radius);
if (defined) path.defined(defined);
if (curve) path.curve(curve);

return path;
}
42 changes: 14 additions & 28 deletions packages/vx-shape/src/shapes/Arc.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from 'react';
import cx from 'classnames';
import { arc as d3Arc, Arc as ArcType } from 'd3-shape';
import setNumOrAccessor from '../util/setNumberOrNumberAccessor';
import { Arc as ArcType } from 'd3-shape';
import { $TSFIXME, AddSVGProps } from '../types';
import { Accessor } from '../types/accessor';
import arc, { ArcPathConfig } from '../factories/arcPath';

export type ArcProps<Datum> = {
/** className applied to path element. */
Expand All @@ -14,21 +13,7 @@ export type ArcProps<Datum> = {
children?: (args: { path: ArcType<$TSFIXME, Datum> }) => React.ReactNode;
/** React ref to the path element. */
innerRef?: React.Ref<SVGPathElement>;
/** Number or accessor function which returns a number, which defines the arc innerRadius. */
innerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc outerRadius. */
outerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc cornerRadius. */
cornerRadius?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc startAngle. */
startAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc endAngle. */
endAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc padAngle. */
padAngle?: number | Accessor<Datum, number>;
/** Number or accessor function which returns a number, which defines the arc padRadius. */
padRadius?: number | Accessor<Datum, number>;
};
} & ArcPathConfig<Datum>;

export default function Arc<Datum>({
className,
Expand All @@ -44,20 +29,21 @@ export default function Arc<Datum>({
innerRef,
...restProps
}: AddSVGProps<ArcProps<Datum>, SVGPathElement>) {
const arc = d3Arc<Datum>();
if (innerRadius != null) setNumOrAccessor(arc.innerRadius, innerRadius);
if (outerRadius != null) setNumOrAccessor(arc.outerRadius, outerRadius);
if (cornerRadius != null) setNumOrAccessor(arc.cornerRadius, cornerRadius);
if (startAngle != null) setNumOrAccessor(arc.startAngle, startAngle);
if (endAngle != null) setNumOrAccessor(arc.endAngle, endAngle);
if (padAngle != null) setNumOrAccessor(arc.padAngle, padAngle);
if (padRadius != null) setNumOrAccessor(arc.padRadius, padRadius);
const path = arc({
innerRadius,
outerRadius,
cornerRadius,
startAngle,
endAngle,
padAngle,
padRadius,
});

// eslint-disable-next-line react/jsx-no-useless-fragment
if (children) return <>{children({ path: arc })}</>;
if (children) return <>{children({ path })}</>;
if (!data) return null;

return (
<path ref={innerRef} className={cx('vx-arc', className)} d={arc(data) || ''} {...restProps} />
<path ref={innerRef} className={cx('vx-arc', className)} d={path(data) || ''} {...restProps} />
);
}
13 changes: 2 additions & 11 deletions packages/vx-shape/src/shapes/Area.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from 'react';
import cx from 'classnames';
import { area } from 'd3-shape';
import setNumOrAccessor from '../util/setNumberOrNumberAccessor';
import { AddSVGProps } from '../types';
import { BaseAreaProps } from '../types/area';
import areaPath from '../factories/areaPath';

export type AreaProps<Datum> = BaseAreaProps<Datum>;

Expand All @@ -22,15 +21,7 @@ export default function Area<Datum>({
innerRef,
...restProps
}: AddSVGProps<AreaProps<Datum>, SVGPathElement>) {
const path = area<Datum>();
if (x) setNumOrAccessor(path.x, x);
if (x0) setNumOrAccessor(path.x0, x0);
if (x1) setNumOrAccessor(path.x1, x1);
if (y) setNumOrAccessor(path.y, y);
if (y0) setNumOrAccessor(path.y0, y0);
if (y1) setNumOrAccessor(path.y1, y1);
if (defined) path.defined(defined);
if (curve) path.curve(curve);
const path = areaPath<Datum>({ x, x0, x1, y, y0, y1, defined, curve });
// eslint-disable-next-line react/jsx-no-useless-fragment
if (children) return <>{children({ path })}</>;
return (
Expand Down
9 changes: 2 additions & 7 deletions packages/vx-shape/src/shapes/AreaClosed.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React from 'react';
import cx from 'classnames';
import { area } from 'd3-shape';
import setNumOrAccessor from '../util/setNumberOrNumberAccessor';
import { PositionScale, AddSVGProps } from '../types';
import { BaseAreaProps } from '../types/area';
import area from '../factories/areaPath';

export type AreaClosedProps<Datum> = BaseAreaProps<Datum> & {
yScale: PositionScale;
Expand All @@ -25,10 +25,7 @@ export default function AreaClosed<Datum>({
children,
...restProps
}: AddSVGProps<AreaClosedProps<Datum>, SVGPathElement>) {
const path = area<Datum>();
if (x) setNumOrAccessor(path.x, x);
if (x0) setNumOrAccessor(path.x0, x0);
if (x1) setNumOrAccessor(path.x1, x1);
const path = area<Datum>({ x, x0, x1, defined, curve });
if (y0) {
setNumOrAccessor(path.y0, y0);
} else {
Expand All @@ -40,8 +37,6 @@ export default function AreaClosed<Datum>({
}
if (y && !y1) setNumOrAccessor(path.y1, y);
if (y1 && !y) setNumOrAccessor(path.y1, y1);
if (defined) path.defined(defined);
if (curve) path.curve(curve);
// eslint-disable-next-line react/jsx-no-useless-fragment
if (children) return <>{children({ path })}</>;
return (
Expand Down
20 changes: 4 additions & 16 deletions packages/vx-shape/src/shapes/LinePath.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,21 @@
import React from 'react';
import cx from 'classnames';
import { line, Line as LineType, CurveFactory, CurveFactoryLineOnly } from 'd3-shape';
import { Line as LineType } from 'd3-shape';
import { AddSVGProps } from '../types';
import { AccessorForArrayItem } from '../types/accessor';
import linePath, { LinePathConfig } from '../factories/linePath';

export type LinePathProps<Datum> = {
/** Array of data for which to generate a line shape. */
data?: Datum[];
/** Sets the curve factory (from @vx/curve or d3-curve) for the area generator. Defaults to curveLinear. */
curve?: CurveFactory | CurveFactoryLineOnly;
/** React RefObject passed to the path element. */
innerRef?: React.Ref<SVGPathElement>;
/** The defined accessor for the shape. The final line shape includes all points for which this function returns true. By default all points are defined. */
defined?: AccessorForArrayItem<Datum, boolean>;
/** Given a datum, returns the x value. Defaults to d[0]. */
x?: AccessorForArrayItem<Datum, number>;
/** Given a datum, returns the y value. Defaults to d[1]. */
y?: AccessorForArrayItem<Datum, number>;
/** Override render function which is passed the configured path generator as input. */
children?: (args: { path: LineType<Datum> }) => React.ReactNode;
/** Fill color of the path element. */
fill?: string;
/** className applied to path element. */
className?: string;
};
} & LinePathConfig<Datum>;

export default function LinePath<Datum>({
children,
Expand All @@ -37,11 +29,7 @@ export default function LinePath<Datum>({
defined = () => true,
...restProps
}: AddSVGProps<LinePathProps<Datum>, SVGPathElement>) {
const path = line<Datum>();
if (x) path.x(x);
if (y) path.y(y);
if (defined) path.defined(defined);
if (curve) path.curve(curve);
const path = linePath<Datum>({ x, y, defined, curve });
// eslint-disable-next-line react/jsx-no-useless-fragment
if (children) return <>{children({ path })}</>;
return (
Expand Down
Loading

0 comments on commit 3da1947

Please sign in to comment.