diff --git a/README.md b/README.md index 06231a348..94cb2f03e 100644 --- a/README.md +++ b/README.md @@ -114,14 +114,8 @@ const yScale = scaleLinear({ // Compose together the scale and accessor functions to get point functions const compose = (scale, accessor) => data => scale(accessor(data)); -const xPoint = compose( - xScale, - x, -); -const yPoint = compose( - yScale, - y, -); +const xPoint = compose(xScale, x); +const yPoint = compose(yScale, y); // Finally we'll embed it all in an SVG function BarGraph(props) { @@ -273,7 +267,9 @@ Lots coming soon, check out the [roadmap](./ROADMAP.md). ## Development -[Yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) are used to manage dependencies and build config across packages in the umbrella `vx` monorepo, and [lerna](https://github.com/lerna/lerna/) is used to manage versioning. +[Yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) are used to manage dependencies and +build config across packages in the umbrella `vx` monorepo, and +[lerna](https://github.com/lerna/lerna/) is used to manage versioning. ``` vx/ @@ -308,14 +304,14 @@ yarn yarn build ``` -Upon modification of a signle `package` you can run `yarn build-one --workspaces=@vx/package` from -the `vx` monorepo root to re-build the package with your changes. You can use the local -[`next.js`](https://nextjs.org) dev server within `packages/vx-demo` to view and iterate on your -changes in the gallery. From the `packages/vx-demo` folder run `yarn dev` to start the next server -which (if correctly sym-linked) will also watch for changes you make to other packages (upon +Upon modification of a signle `package` you can run `yarn build-one --workspaces=@vx/package` from +the `vx` monorepo root to re-build the package with your changes. You can use the local +[`next.js`](https://nextjs.org) dev server within `packages/vx-demo` to view and iterate on your +changes in the gallery. From the `packages/vx-demo` folder run `yarn dev` to start the next server +which (if correctly sym-linked) will also watch for changes you make to other packages (upon re-building them). -`vx` uses [`@airbnb/nimbus`](https://github.com/airbnb/nimbus) to generate build configuration for +`vx` uses [`@airbnb/nimbus`](https://github.com/airbnb/nimbus) to generate build configuration for `eslint`, `prettier`, `jest`, `babel`, and `typescript`. :v: diff --git a/packages/vx-demo/src/components/tiles/network.js b/packages/vx-demo/src/components/tiles/network.js index 749f8e71a..db6eee0bd 100644 --- a/packages/vx-demo/src/components/tiles/network.js +++ b/packages/vx-demo/src/components/tiles/network.js @@ -1,7 +1,11 @@ import React from 'react'; import { Graph } from '@vx/network'; -const nodes = [{ x: 50, y: 20 }, { x: 200, y: 300 }, { x: 300, y: 40 }]; +const nodes = [ + { x: 50, y: 20 }, + { x: 200, y: 300 }, + { x: 300, y: 40 }, +]; const links = [ { source: nodes[0], target: nodes[1] }, { source: nodes[1], target: nodes[2] }, diff --git a/packages/vx-drag/package.json b/packages/vx-drag/package.json index b4c7d3a83..e26438857 100644 --- a/packages/vx-drag/package.json +++ b/packages/vx-drag/package.json @@ -5,6 +5,7 @@ "sideEffects": false, "main": "lib/index.js", "module": "esm/index.js", + "types": "lib/index.d.ts", "files": [ "lib", "esm" @@ -33,9 +34,10 @@ "access": "public" }, "peerDependencies": { - "react": "^15.0.0-0 || ^16.0.0-0" + "react": "^16.3.0-0" }, "dependencies": { + "@types/react": "*", "@vx/event": "0.0.192", "prop-types": "^15.5.10" } diff --git a/packages/vx-drag/src/Drag.jsx b/packages/vx-drag/src/Drag.jsx deleted file mode 100644 index 1763f3f17..000000000 --- a/packages/vx-drag/src/Drag.jsx +++ /dev/null @@ -1,104 +0,0 @@ -import { localPoint } from '@vx/event'; -import PropTypes from 'prop-types'; -import React from 'react'; - -export default class Drag extends React.Component { - constructor(props) { - super(props); - this.state = { - x: undefined, - y: undefined, - dx: 0, - dy: 0, - isDragging: false, - }; - this.handleDragEnd = this.handleDragEnd.bind(this); - this.handleDragMove = this.handleDragMove.bind(this); - this.handleDragStart = this.handleDragStart.bind(this); - } - - handleDragStart(event) { - const { onDragStart, resetOnStart } = this.props; - const { dx, dy } = this.state; - const point = localPoint(event); - const nextState = { - ...this.state, - isDragging: true, - dx: resetOnStart ? 0 : dx, - dy: resetOnStart ? 0 : dy, - x: resetOnStart ? point.x : -dx + point.x, - y: resetOnStart ? point.y : -dy + point.y, - }; - if (onDragStart) onDragStart({ ...nextState, event }); - this.setState(() => nextState); - } - - handleDragMove(event) { - const { onDragMove } = this.props; - const { x, y, isDragging } = this.state; - if (!isDragging) return; - const point = localPoint(event); - const nextState = { - ...this.state, - isDragging: true, - dx: -(x - point.x), - dy: -(y - point.y), - }; - if (onDragMove) onDragMove({ ...nextState, event }); - this.setState(() => nextState); - } - - handleDragEnd(event) { - const { onDragEnd } = this.props; - const nextState = { - ...this.state, - isDragging: false, - }; - if (onDragEnd) onDragEnd({ ...nextState, event }); - this.setState(() => nextState); - } - - render() { - const { x, y, dx, dy, isDragging } = this.state; - const { children, width, height, captureDragArea } = this.props; - return ( - - {isDragging && captureDragArea && ( - - )} - {children({ - x, - y, - dx, - dy, - isDragging, - dragEnd: this.handleDragEnd, - dragMove: this.handleDragMove, - dragStart: this.handleDragStart, - })} - - ); - } -} - -Drag.propTypes = { - children: PropTypes.func.isRequired, - width: PropTypes.number.isRequired, - height: PropTypes.number.isRequired, - captureDragArea: PropTypes.bool, - resetOnStart: PropTypes.bool, - onDragEnd: PropTypes.func, - onDragMove: PropTypes.func, - onDragStart: PropTypes.func, -}; - -Drag.defaultProps = { - captureDragArea: true, - resetOnStart: false, -}; diff --git a/packages/vx-drag/src/Drag.tsx b/packages/vx-drag/src/Drag.tsx new file mode 100644 index 000000000..c9cda5125 --- /dev/null +++ b/packages/vx-drag/src/Drag.tsx @@ -0,0 +1,139 @@ +import { localPoint } from '@vx/event'; +import React from 'react'; + +type MouseOrTouchEvent = React.MouseEvent | React.TouchEvent; + +export type DragProps = { + /** Children render function which is passed the state of dragging and callbacks for drag start/end/move. */ + children: (args: ChildrenArgs) => React.ReactNode; + /** Width of the drag container. */ + width: number; + /** Height of the drag container. */ + height: number; + /** Whether to render an invisible rect below children to capture the drag area as defined by width and height. */ + captureDragArea?: boolean; + /** Whether to reset drag state upon the start of a new drag. */ + resetOnStart?: boolean; + /** Optional callback invoked upon drag end. */ + onDragEnd?: (args: HandlerArgs) => void; + /** Optional callback invoked upon drag movement. */ + onDragMove?: (args: HandlerArgs) => void; + /** Optional callback invoked upon drag start. */ + onDragStart?: (args: HandlerArgs) => void; +}; + +type DragState = { + x: number | undefined; + y: number | undefined; + dx: number; + dy: number; + isDragging: boolean; +}; + +type HandlerArgs = DragState & { event: MouseOrTouchEvent }; + +type ChildrenArgs = DragState & { + dragEnd: (event: MouseOrTouchEvent) => void; + dragMove: (event: MouseOrTouchEvent) => void; + dragStart: (event: MouseOrTouchEvent) => void; +}; + +export default class Drag extends React.Component { + static defaultProps = { + captureDragArea: true, + resetOnStart: false, + }; + + state = { + x: undefined, + y: undefined, + dx: 0, + dy: 0, + isDragging: false, + }; + + handleDragStart = (event: MouseOrTouchEvent) => { + const { onDragStart, resetOnStart } = this.props; + event.persist(); + + this.setState( + ({ dx, dy }) => { + const point = localPoint(event) || { x: 0, y: 0 }; + return { + isDragging: true, + dx: resetOnStart ? 0 : dx, + dy: resetOnStart ? 0 : dy, + x: resetOnStart ? point.x : -dx + point.x, + y: resetOnStart ? point.y : -dy + point.y, + }; + }, + onDragStart && + (() => { + onDragStart({ ...this.state, event }); + }), + ); + }; + + handleDragMove = (event: MouseOrTouchEvent) => { + const { onDragMove } = this.props; + event.persist(); + + this.setState( + ({ x, y, isDragging }) => { + const point = localPoint(event) || { x: 0, y: 0 }; + return isDragging + ? { + isDragging: true, + dx: -((x || 0) - point.x), + dy: -((y || 0) - point.y), + } + : null; + }, + onDragMove && + (() => { + if (this.state.isDragging) onDragMove({ ...this.state, event }); + }), + ); + }; + + handleDragEnd = (event: MouseOrTouchEvent) => { + const { onDragEnd } = this.props; + event.persist(); + + this.setState( + { isDragging: false }, + onDragEnd && + (() => { + onDragEnd({ ...this.state, event }); + }), + ); + }; + + render() { + const { x, y, dx, dy, isDragging } = this.state; + const { children, width, height, captureDragArea } = this.props; + return ( + <> + {isDragging && captureDragArea && ( + + )} + {children({ + x, + y, + dx, + dy, + isDragging, + dragEnd: this.handleDragEnd, + dragMove: this.handleDragMove, + dragStart: this.handleDragStart, + })} + + ); + } +} diff --git a/packages/vx-drag/src/index.js b/packages/vx-drag/src/index.ts similarity index 100% rename from packages/vx-drag/src/index.js rename to packages/vx-drag/src/index.ts diff --git a/packages/vx-drag/src/util/raise.js b/packages/vx-drag/src/util/raise.js deleted file mode 100644 index 2621f80af..000000000 --- a/packages/vx-drag/src/util/raise.js +++ /dev/null @@ -1,7 +0,0 @@ -export default function raise(items, raiseIndex) { - const array = items.slice(); - const lastIndex = array.length - 1; - const raiseItem = array.splice(raiseIndex, 1)[0]; - array.splice(lastIndex, 0, raiseItem); - return array; -} diff --git a/packages/vx-drag/src/util/raise.ts b/packages/vx-drag/src/util/raise.ts new file mode 100644 index 000000000..6306193c9 --- /dev/null +++ b/packages/vx-drag/src/util/raise.ts @@ -0,0 +1,8 @@ +/** Given at an array of items, moves the item at the specified index to the end of the array. */ +export default function raise(items: T[], raiseIndex: number) { + const array = [...items]; + const lastIndex = array.length - 1; + const [raiseItem] = array.splice(raiseIndex, 1); + array.splice(lastIndex, 0, raiseItem); + return array; +} diff --git a/packages/vx-drag/test/Drag.test.js b/packages/vx-drag/test/Drag.test.ts similarity index 100% rename from packages/vx-drag/test/Drag.test.js rename to packages/vx-drag/test/Drag.test.ts diff --git a/packages/vx-geo/src/projections/Projection.tsx b/packages/vx-geo/src/projections/Projection.tsx index 316f1a445..0d33faf6d 100644 --- a/packages/vx-geo/src/projections/Projection.tsx +++ b/packages/vx-geo/src/projections/Projection.tsx @@ -11,14 +11,14 @@ import { geoPath, GeoPath, GeoProjection, - GeoPermissibleObjects, + GeoPermissibleObjects as GeoPermissibleObjectType, } from 'd3-geo'; // eslint-disable-next-line import/no-unresolved import { LineString, Polygon, MultiLineString } from 'geojson'; import Graticule, { GraticuleProps } from '../graticule/Graticule'; -export type GeoPermissibleObjects = GeoPermissibleObjects; +export type GeoPermissibleObjects = GeoPermissibleObjectType; // TODO: Implement all projections of d3-geo type ProjectionPreset = diff --git a/packages/vx-pattern/src/patterns/Circles.tsx b/packages/vx-pattern/src/patterns/Circles.tsx index 780670274..26a9f954c 100644 --- a/packages/vx-pattern/src/patterns/Circles.tsx +++ b/packages/vx-pattern/src/patterns/Circles.tsx @@ -42,7 +42,12 @@ export default function PatternCircles({ }: PatternCirclesProps) { let corners: [number, number][] | undefined; if (complement) { - corners = [[0, 0], [0, height], [width, 0], [width, height]]; + corners = [ + [0, 0], + [0, height], + [width, 0], + [width, height], + ]; } return ( diff --git a/packages/vx-responsive/package.json b/packages/vx-responsive/package.json index 3380961b5..4fbdbce2e 100644 --- a/packages/vx-responsive/package.json +++ b/packages/vx-responsive/package.json @@ -31,6 +31,7 @@ }, "homepage": "https://github.com/hshoff/vx#readme", "dependencies": { + "@types/lodash": "^4.14.146", "@types/react": "*", "lodash": "^4.17.10", "prop-types": "^15.6.1", diff --git a/packages/vx-responsive/test/withParentSize.test.tsx b/packages/vx-responsive/test/withParentSize.test.tsx index a8e3f5e7e..725123382 100644 --- a/packages/vx-responsive/test/withParentSize.test.tsx +++ b/packages/vx-responsive/test/withParentSize.test.tsx @@ -13,6 +13,9 @@ describe('withParentSize', () => { left: 0, bottom: 0, right: 0, + x: 0, + y: 0, + toJSON: () => '', })); }); diff --git a/packages/vx-shape/src/types.ts b/packages/vx-shape/src/types.ts index 09a6cf010..4a42b4c79 100644 --- a/packages/vx-shape/src/types.ts +++ b/packages/vx-shape/src/types.ts @@ -52,7 +52,7 @@ export interface BarGroupBar { export interface BarStack { index: number; key: StackKey; - bars: ({ + bars: { /** Processed bar Datum with bar bounds and original datum. */ bar: SeriesPoint; /** group key */ @@ -69,7 +69,7 @@ export interface BarStack { y: number; /** color of bar. */ color: string; - })[]; + }[]; } export type AccessorProps = { diff --git a/packages/vx-shape/test/LinePath.test.tsx b/packages/vx-shape/test/LinePath.test.tsx index 859b1cb2a..a5b945982 100644 --- a/packages/vx-shape/test/LinePath.test.tsx +++ b/packages/vx-shape/test/LinePath.test.tsx @@ -10,7 +10,10 @@ interface Datum { } const linePathProps = { - data: [{ x: 0, y: 0 }, { x: 1, y: 1 }], + data: [ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ], x: (d: Datum) => d.x, y: (d: Datum) => d.y, }; @@ -47,6 +50,7 @@ describe('', () => { }); test('it should expose its ref via an innerRef prop', () => { + // eslint-disable-next-line jest/no-test-return-statement return new Promise(done => { const refCallback = (ref: SVGPathElement) => { expect(ref.tagName).toMatch('path'); diff --git a/packages/vx-shape/test/LineRadial.test.tsx b/packages/vx-shape/test/LineRadial.test.tsx index ad84b3d3d..4066aa400 100644 --- a/packages/vx-shape/test/LineRadial.test.tsx +++ b/packages/vx-shape/test/LineRadial.test.tsx @@ -10,7 +10,10 @@ interface Datum { } const mockProps = { - data: [{ x: 0, y: 0 }, { x: 1, y: 1 }], + data: [ + { x: 0, y: 0 }, + { x: 1, y: 1 }, + ], angle: (d: Datum) => d.x, radius: (d: Datum) => d.y, }; @@ -43,10 +46,11 @@ describe('', () => { LineRadialChildren({ children: fn, ...mockProps }); const args = fn.mock.calls[0][0]; const keys = Object.keys(args); - expect(keys.includes('path')).toEqual(true); + expect(keys).toContain('path'); }); test('it should expose its ref via an innerRef prop', () => { + // eslint-disable-next-line jest/no-test-return-statement return new Promise(done => { const refCallback = (ref: SVGPathElement) => { expect(ref.tagName).toMatch('path'); diff --git a/packages/vx-text/package.json b/packages/vx-text/package.json index 995c81d80..273769365 100644 --- a/packages/vx-text/package.json +++ b/packages/vx-text/package.json @@ -32,6 +32,7 @@ "homepage": "https://github.com/hshoff/vx#readme", "dependencies": { "@types/classnames": "^2.2.9", + "@types/lodash": "^4.14.146", "@types/react": "*", "classnames": "^2.2.5", "lodash": "^4.17.15", diff --git a/packages/vx-voronoi/src/voronoi.js b/packages/vx-voronoi/src/voronoi.js index e2b13fad2..6adb113f9 100644 --- a/packages/vx-voronoi/src/voronoi.js +++ b/packages/vx-voronoi/src/voronoi.js @@ -8,7 +8,10 @@ export default ({ width = 0, height = 0, x, y }) => { if (x) voronoi.x(x); if (y) voronoi.y(y); - voronoi.extent([[-1, -1], [width + 1, height + 1]]); + voronoi.extent([ + [-1, -1], + [width + 1, height + 1], + ]); return voronoi; };