` inside headings bigger than `` inside paragraphs so components headings are actually clearer as such… [#297](https://github.com/hshoff/vx/pull/297)
- [shape] Add Pie documentation. [#297](https://github.com/hshoff/vx/pull/297)
#### :white_check_mark: Tests
- [shape] Add tests for sort callbacks in the `Pie` component. [#292](https://github.com/hshoff/vx/pull/292)
#### :trophy: Contributors
- [yuchi](https://github.com/yuchi)
```
Changes:
- @vx/annotation: 0.0.162 => 0.0.164
- @vx/axis: 0.0.162 => 0.0.164
- @vx/demo: 0.0.163 => 0.0.164
- @vx/grid: 0.0.162 => 0.0.164
- @vx/marker: 0.0.162 => 0.0.164
- @vx/shape: 0.0.162 => 0.0.164
- @vx/threshold: 0.0.162 => 0.0.164
- @vx/vx: 0.0.163 => 0.0.164
```
# v0.0.163
#### :bug: Bug Fix
- [tooltip] don't pass `getRects` func prop from `withBoundingRects` to `Tooltip` [#290](https://github.com/hshoff/vx/pull/290)
#### :trophy: Contributors
- [williaster](https://github.com/williaster)
```
Changes:
- @vx/demo: 0.0.162 => 0.0.163
- @vx/tooltip: 0.0.161 => 0.0.163
- @vx/vx: 0.0.162 => 0.0.163
```
# v0.0.162
#### :rocket: Enhancements
- [threshold] add ` ` [#285](https://github.com/hshoff/vx/pull/285)
- [grid] add support for band scales [#282](https://github.com/hshoff/vx/pull/282)
- [shape] now supports function as children [#285](https://github.com/hshoff/vx/pull/285)
#### :memo: Documentation
- [demo] add /threshold demo [#285](https://github.com/hshoff/vx/pull/285)
- [demo] demo band scale grid on /barstack [#282](https://github.com/hshoff/vx/pull/282)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```
Changes:
- @vx/annotation: 0.0.161 => 0.0.162
- @vx/axis: 0.0.161 => 0.0.162
- @vx/demo: 0.0.161 => 0.0.162
- @vx/grid: 0.0.161 => 0.0.162
- @vx/marker: 0.0.161 => 0.0.162
- @vx/shape: 0.0.161 => 0.0.162
- @vx/threshold: 0.0.1 => 0.0.162
- @vx/vx: 0.0.161 => 0.0.162
```
# v0.0.161
#### :boom: Breaking Changes
- [scale] Removed color scales, recommend users to use [`d3-scale-chromatic`](https://github.com/d3/d3-scale-chromatic), following d3's lead in release [5.0.0](https://github.com/d3/d3/releases/tag/v5.0.0). [#270](https://github.com/hshoff/vx/pull/270)
- [scale] The following files/tests/documentation are no longer part of `@vx/scale`: `schemeCategory10`, `schemeCategory20`, `schemeCategory20b`, `schemeCategory20c`. [#270](https://github.com/hshoff/vx/pull/270)
#### :rocket: Enhancements
- [tooltip] adds an optional `containerProps` as a second HOC "config" argument of `withTooltip(BaseComponent [, containerProps])`. This exposes a hook to enable users to customize any props on the container element. [#272](https://github.com/hshoff/vx/pull/272)
- [tooltip] sets `width` and `height` to `inherit` by default on the container. I'm kind of on the edge with this and am open to removing it because it may be an edge case, but my reasoning is as follows: if a user is combining `withTooltip` and a responsive component like `ParentSize` like this example, they'll have to update the `withTooltip` container `div` to also have full width/height so this would be a "smart default". [#272](https://github.com/hshoff/vx/pull/272)
#### :memo: Documentation
- [scale] Added a section on color scales, which goes over how one would use `d3-scale-chromatic` with `vx/scale`. [#270](https://github.com/hshoff/vx/pull/270)
- [tooltip] Adds a readme for `@vx/tooltip` components + enhancer. [#272](https://github.com/hshoff/vx/pull/272)
- [demo] add ` ` to /axis demo tile. [#280](https://github.com/hshoff/vx/pull/280)
- [demo] update vx-demo.now.sh doc pages. [#281](https://github.com/hshoff/vx/pull/281)
#### :house: Internal
- Add configuration for [Prettier](https://prettier.io) and format the existing codebase. [#275](https://github.com/hshoff/vx/pull/275)
- Add pre-commit hook to format changed files before commits. [#275](https://github.com/hshoff/vx/pull/275)
- [docs] fix `npm run docs` script. [#281](https://github.com/hshoff/vx/pull/281)
#### :trophy: Contributors
- [sto3psl](https://github.com/sto3psl)
- [williaster](https://github.com/williaster)
- [trainorpj](https://github.com/trainorpj)
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/annotation: 0.0.160 => 0.0.161
- @vx/axis: 0.0.160 => 0.0.161
- @vx/bounds: 0.0.153 => 0.0.161
- @vx/boxplot: 0.0.153 => 0.0.161
- @vx/brush: 0.0.153 => 0.0.161
- @vx/clip-path: 0.0.153 => 0.0.161
- @vx/curve: 0.0.153 => 0.0.161
- @vx/demo: 0.0.160 => 0.0.161
- @vx/drag: 0.0.157 => 0.0.161
- @vx/event: 0.0.153 => 0.0.161
- @vx/geo: 0.0.153 => 0.0.161
- @vx/glyph: 0.0.153 => 0.0.161
- @vx/gradient: 0.0.153 => 0.0.161
- @vx/grid: 0.0.160 => 0.0.161
- @vx/group: 0.0.153 => 0.0.161
- @vx/heatmap: 0.0.153 => 0.0.161
- @vx/hierarchy: 0.0.153 => 0.0.161
- @vx/legend: 0.0.154 => 0.0.161
- @vx/marker: 0.0.160 => 0.0.161
- @vx/mock-data: 0.0.153 => 0.0.161
- @vx/network: 0.0.153 => 0.0.161
- @vx/pattern: 0.0.153 => 0.0.161
- @vx/point: 0.0.153 => 0.0.161
- @vx/responsive: 0.0.158 => 0.0.161
- @vx/scale: 0.0.153 => 0.0.161
- @vx/shape: 0.0.160 => 0.0.161
- @vx/stats: 0.0.153 => 0.0.161
- @vx/text: 0.0.159 => 0.0.161
- @vx/tooltip: 0.0.160 => 0.0.161
- @vx/voronoi: 0.0.153 => 0.0.161
- @vx/vx: 0.0.160 => 0.0.161
- @vx/zoom: 0.0.153 => 0.0.161
```
# v0.0.160
#### :boom: Breaking Changes
- [shape] `` components now use `...additionalProps()` everywhere for consistency. So function props get passed data. example: `onClick={event => // stuff}` becomes `onClick={data => event => // stuff}` and now you can stroke/fill/attr based on data `stroke={({ target }) => target.data.children ? 'yellow' : 'blue' }. [#265](https://github.com/hshoff/vx/pull/265)
#### :rocket: Enhancements
- [shape] export link path generators. fixes: [#263](https://github.com/hshoff/vx/issues/263). [#265](https://github.com/hshoff/vx/pull/265)
- [shape] add optional `path` prop so you can pass in path generator function instead of creating the generator every render. [#265](https://github.com/hshoff/vx/pull/265)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/annotation: 0.0.158 => 0.0.160
- @vx/axis: 0.0.159 => 0.0.160
- @vx/demo: 0.0.159 => 0.0.160
- @vx/grid: 0.0.158 => 0.0.160
- @vx/marker: 0.0.158 => 0.0.160
- @vx/shape: 0.0.158 => 0.0.160
- @vx/tooltip: 0.0.158 => 0.0.160
- @vx/vx: 0.0.159 => 0.0.160
```
# v0.0.159
#### :rocket: Enhancements
- [axis] By default ` ` components now use `@vx/text` to render tick labels. This enables multi line labels and scaling text to fit in a certain amount of space. [#260](https://github.com/hshoff/vx/pull/260)
Example:
```jsx
({
textAnchor: 'middle',
verticalAnchor: 'middle',
width: 100,
scaleToFit: true
})
/>
```
- [axis] ` ` components got a new prop `tickComponent` to enable rendering of custom ticks. With this prop one can completely customize ticks without having to create a new custom ` ` component. [#260](https://github.com/hshoff/vx/pull/260)
Example:
```jsx
(
{formattedValue}
)}
/>
```
`tickComponent` accepts a function and gets called with the following attribute:
```js
tickComponent({ x, y, formattedValue, ...tickLabelPropsObj })
```
#### :memo: Documentation
- [axis] update `@vx/axis` documentation. [#260](https://github.com/hshoff/vx/pull/260)
- [demo] fix bargroup example code. [#250](https://github.com/hshoff/vx/pull/250)
- [demo] fix barstack example code. [#249](https://github.com/hshoff/vx/pull/249)
- [text] fix readme.md of `@vx/text` package. [#257](https://https://github.com/hshoff/vx/pull/257)
#### :trophy: Contributors
- [bulat-f](https://github.com/bulat-f)
- [sto3psl](https://github.com/sto3psl)
- [browniefed](https://github.com/browniefed)
```bash
Changes:
- @vx/axis: 0.0.158 => 0.0.159
- @vx/demo: 0.0.158 => 0.0.159
- @vx/text: 0.0.153 => 0.0.159
- @vx/vx: 0.0.158 => 0.0.159
```
# v0.0.158
#### :rocket: Enhancements
- [responsive] add debounceTime prop to ` ` with a default of 300ms. [#241](https://github.com/hshoff/vx/pull/241)
- [tooltip] ` ` now also reconsiders window bounds [#240](https://github.com/hshoff/vx/pull/240)
#### :house: Internal
- [demo] fix streamgraph transparent fill [#242](https://github.com/hshoff/vx/pull/242)
#### :trophy: Contributors
- [AlexJuarez](https://github.com/AlexJuarez)
- [manuelrocha88](https://github.com/manuelrocha88)
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/annotation: 0.0.153 => 0.0.158
- @vx/axis: 0.0.153 => 0.0.158
- @vx/demo: 0.0.157 => 0.0.158
- @vx/grid: 0.0.153 => 0.0.158
- @vx/marker: 0.0.153 => 0.0.158
- @vx/responsive: 0.0.153 => 0.0.158
- @vx/shape: 0.0.153 => 0.0.158
- @vx/tooltip: 0.0.153 => 0.0.158
- @vx/vx: 0.0.157 => 0.0.158
```
# v0.0.157
#### :rocket: Enhancements
- [drag] remove `svg` prop. This was causing hacky problems like calling `forceUpdate` in `cDM`. `localPoint()` now finds svg from the event argument [#233](https://github.com/hshoff/vx/pull/233)
#### :memo: Documentation
- [demo] update drag demos, add `touch-action: none` on drag demos so no scrolling when dragging [#233](https://github.com/hshoff/vx/pull/233)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/demo: 0.0.156 => 0.0.157
- @vx/drag: 0.0.156 => 0.0.157
- @vx/vx: 0.0.156 => 0.0.157
```
# v0.0.156
#### :rocket: Enhancements
- [drag] add `resetOnStart` prop (default to false). When true, it will reset drag `x,y` to the start point from the mousedown/touchstart event and `dx,dy` to 0 on drag start [#231](https://github.com/hshoff/vx/pull/231)
#### :memo: Documentation
- [demo] add /drag-ii demo of a drawboard made with drag [#231](https://github.com/hshoff/vx/pull/231)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/demo: 0.0.155 => 0.0.156
- @vx/drag: 0.0.155 => 0.0.156
- @vx/vx: 0.0.155 => 0.0.156
```
# v0.0.155
#### :rocket: Enhancements
- [drag] add ` ` component + demo [#229](https://github.com/hshoff/vx/pull/229)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/demo: 0.0.154 => 0.0.155
- @vx/drag: 0.0.153 => 0.0.155
- @vx/vx: 0.0.154 => 0.0.155
```
# v0.0.154
#### :rocket: Enhancements
- [legend] make legend items clickable, add ` ` propTypes, add click test [#227](https://github.com/hshoff/vx/pull/227)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/demo: 0.0.153 => 0.0.154
- @vx/legend: 0.0.153 => 0.0.154
- @vx/vx: 0.0.153 => 0.0.154
```
# v0.0.153
#### :house: Internal
- [internal] add sideEffects: false to pkg for webpack 4 [#225](https://github.com/hshoff/vx/pull/225)
#### :trophy: Contributors
- [hshoff](https://github.com/hshoff)
```bash
Changes:
- @vx/annotation: 0.0.147 => 0.0.153
- @vx/axis: 0.0.152 => 0.0.153
- @vx/bounds: 0.0.147 => 0.0.153
- @vx/boxplot: 0.0.143 => 0.0.153
- @vx/brush: 0.0.143 => 0.0.153
- @vx/clip-path: 0.0.143 => 0.0.153
- @vx/curve: 0.0.143 => 0.0.153
- @vx/demo: 0.0.152 => 0.0.153
- @vx/drag: 0.0.143 => 0.0.153
- @vx/event: 0.0.143 => 0.0.153
- @vx/geo: 0.0.150 => 0.0.153
- @vx/glyph: 0.0.143 => 0.0.153
- @vx/gradient: 0.0.143 => 0.0.153
- @vx/grid: 0.0.147 => 0.0.153
- @vx/group: 0.0.143 => 0.0.153
- @vx/heatmap: 0.0.143 => 0.0.153
- @vx/hierarchy: 0.0.144 => 0.0.153
- @vx/legend: 0.0.143 => 0.0.153
- @vx/marker: 0.0.147 => 0.0.153
- @vx/mock-data: 0.0.147 => 0.0.153
- @vx/network: 0.0.143 => 0.0.153
- @vx/pattern: 0.0.143 => 0.0.153
- @vx/point: 0.0.143 => 0.0.153
- @vx/responsive: 0.0.152 => 0.0.153
- @vx/scale: 0.0.152 => 0.0.153
- @vx/shape: 0.0.147 => 0.0.153
- @vx/stats: 0.0.152 => 0.0.153
- @vx/text: 0.0.152 => 0.0.153
- @vx/tooltip: 0.0.148 => 0.0.153
- @vx/voronoi: 0.0.143 => 0.0.153
- @vx/vx: 0.0.152 => 0.0.153
- @vx/zoom: 0.0.143 => 0.0.153
```
# v0.0.152
#### :rocket: Enhancements
- [text] add `fontWeight` option to vx-text demo [#215](https://github.com/hshoff/vx/pull/215)
#### :memo: Documentation
- [demo] add vx-text tile and update /text demo [#214](https://github.com/hshoff/vx/pull/214)
- [responsive] add description and example of each component and enhancer [#217](https://github.com/hshoff/vx/pull/217)
#### :bug: Bug Fix
- [text] fix memoized `getStringWidth` ignoring styles [#215](https://github.com/hshoff/vx/pull/215)
- [text] remove default width and height from measurement SVG [#219](https://github.com/hshoff/vx/pull/219)
- [scale] fix scalePower api to take in exponent instead of base [#223](https://github.com/hshoff/vx/pull/223)
#### :house: Internal
- [travis] fix for travis failing for timing out when [not receiving output for 10min](https://docs.travis-ci.com/user/common-build-problems/#Build-times-out-because-no-output-was-received) [#224](https://github.com/hshoff/vx/pull/224)
- [vx][test] fix `@vx/vx` text test. It was looking for `TextOutline` export which was removed with the [new `@vx/text`](https://github.com/hshoff/vx/pull/208) [#224](https://github.com/hshoff/vx/pull/224)
- [axis] bump `prop-types` dep and use `^` [#224](https://github.com/hshoff/vx/pull/224)
#### :trophy: Contributors
- [techniq](https://github.com/techniq)
- [hshoff](https://github.com/hshoff)
- [katerineknox](https://github.com/katerineknox)
- [crcarlo](https://github.com/crcarlo)
```bash
Changes:
- @vx/axis: 0.0.151 => 0.0.152
- @vx/demo: 0.0.151 => 0.0.152
- @vx/responsive: 0.0.151 => 0.0.152
- @vx/scale: 0.0.151 => 0.0.152
- @vx/stats: 0.0.151 => 0.0.152
- @vx/text: 0.0.151 => 0.0.152
- @vx/vx: 0.0.151 => 0.0.152
```
# v0.0.151
- ignore this one, v0.0.152 includes what v0.0.151 was supposed be. i messed up the publish.
```bash
Changes:
- @vx/axis: 0.0.147 => 0.0.151
- @vx/demo: 0.0.150 => 0.0.151
- @vx/responsive: 0.0.150 => 0.0.151
- @vx/scale: 0.0.143 => 0.0.151
- @vx/stats: 0.0.148 => 0.0.151
- @vx/text: 0.0.150 => 0.0.151
- @vx/vx: 0.0.150 => 0.0.151
```
# v0.0.150
#### :boom: Breaking Changes
- [text] Removes ``, `` and `` components, which were incomplete [#208](https://github.com/hshoff/vx/pull/208)
#### :rocket: Enhancements
- [geo] Added pointRadius and fixed center [#213](https://github.com/hshoff/vx/pull/213)
- [text] Add new ``, with the following features
- Word-wrapping (when width prop is defined)
- Vertical alignment (verticalAnchor prop)
- Rotation (angle prop)
- Scale-to-fit text (scaleToFit prop)
#### :bug: Bug Fix
- [geo] Fixed center typo [#213](https://github.com/hshoff/vx/pull/213)
#### :memo: Documentation
- [responsive] Backticks import not working so, copy paste broken [#212](https://github.com/hshoff/vx/pull/212)
#### :house: Internal
- [text] Update `vx-text` author to @techniq [#210](https://github.com/hshoff/vx/pull/210)
```bash
Changes:
- @vx/demo: 0.0.149 => 0.0.150
- @vx/geo: 0.0.143 => 0.0.150
- @vx/responsive: 0.0.149 => 0.0.150
- @vx/text: 0.0.143 => 0.0.150
- @vx/vx: 0.0.149 => 0.0.150
```
# v0.0.149
#### :rocket: Enhancements
- [responsive] bump `resize-observer-polyfill` [#206](https://github.com/hshoff/vx/pull/206)
#### :bug: Bug Fix
- [demo] add overflow hidden on flex: 1 ` ` parents [#206](https://github.com/hshoff/vx/pull/206)
```bash
Changes:
- @vx/demo: 0.0.148 => 0.0.149
- @vx/responsive: 0.0.147 => 0.0.149
- @vx/vx: 0.0.148 => 0.0.149
```
# v0.0.148
#### :bug: Bug Fix
- [stats] [boxplot] fix container props calculation [#203](https://github.com/hshoff/vx/pull/203)
- [tooltip] fix tootlip with bounds offset [#204](https://github.com/hshoff/vx/pull/204)
```bash
Changes:
- @vx/demo: 0.0.147 => 0.0.148
- @vx/stats: 0.0.147 => 0.0.148
- @vx/tooltip: 0.0.147 => 0.0.148
- @vx/vx: 0.0.147 => 0.0.148
```
# v0.0.147
#### :boom: Breaking Changes
- [shape] deep links to `@vx/shape/shapes/Link{Horizontal, Vertical, Radial}.js` => `@vx/shape/shapes/link/diagonal/Link{Horizontal, Vertical, Radial}.js`. [#194](https://github.com/hshoff/vx/pull/194)
#### :rocket: Enhancements
- [tooltip] add offset props to ` `. [#193](https://github.com/hshoff/vx/pull/193)
- [shape] Add support for step, curve, and line links. [#194](https://github.com/hshoff/vx/pull/194)
- [responsive] add ` ` component. [#198](https://github.com/hshoff/vx/pull/198)
- [stats] added vx-stats for statistic related glyphs (boxplot and violinplot). [#197](https://github.com/hshoff/vx/pull/197) **note:** `@vx/boxplot` is deprecated in favor of `@vx/stats` in a future release `@vx/boxplot` will be removed
#### :house: Internal
- [demo] update gallery tiles to use ` `. [#198](https://github.com/hshoff/vx/pull/198)
- [demo] add /responsive gallery tile + page. [#198](https://github.com/hshoff/vx/pull/198)
```bash
Changes:
- @vx/annotation: 0.0.146 => 0.0.147
- @vx/axis: 0.0.146 => 0.0.147
- @vx/bounds: 0.0.143 => 0.0.147
- @vx/demo: 0.0.146 => 0.0.147
- @vx/grid: 0.0.146 => 0.0.147
- @vx/marker: 0.0.146 => 0.0.147
- @vx/mock-data: 0.0.144 => 0.0.147
- @vx/responsive: 0.0.143 => 0.0.147
- @vx/shape: 0.0.146 => 0.0.147
- @vx/stats: 0.0.143 => 0.0.147
- @vx/tooltip: 0.0.143 => 0.0.147
- @vx/vx: 0.0.146 => 0.0.147
```
# v0.0.146
#### :rocket: Enhancements
- [shape] add ` ` [#185](https://github.com/hshoff/vx/pull/185)
#### :memo: Documentation
- [demo] add ` ` [#185](https://github.com/hshoff/vx/pull/185)
- [demo] tile updates [#186](https://github.com/hshoff/vx/pull/186)
#### :house: Internal
- [shape] remove build/index.js [#186](https://github.com/hshoff/vx/pull/186)
```bash
Changes:
- @vx/annotation: 0.0.145 => 0.0.146
- @vx/axis: 0.0.145 => 0.0.146
- @vx/demo: 0.0.145 => 0.0.146
- @vx/grid: 0.0.145 => 0.0.146
- @vx/marker: 0.0.145 => 0.0.146
- @vx/shape: 0.0.145 => 0.0.146
- @vx/vx: 0.0.145 => 0.0.146
```
# v0.0.145
#### :rocket: Enhancements
- [shape] add `` and tests [#183](https://github.com/hshoff/vx/pull/183)
- [demo] add Radar chart [#180](https://github.com/hshoff/vx/pull/180)
- [axis] add additional tests [#161](https://github.com/hshoff/vx/pull/161)
#### :bug: Bug Fix
- [axis] less restrictive tickValue propTypes [#184](https://github.com/hshoff/vx/pull/184)
```bash
Changes:
- @vx/annotation: 0.0.144 => 0.0.145
- @vx/axis: 0.0.144 => 0.0.145
- @vx/demo: 0.0.144 => 0.0.145
- @vx/grid: 0.0.144 => 0.0.145
- @vx/marker: 0.0.144 => 0.0.145
- @vx/shape: 0.0.144 => 0.0.145
- @vx/vx: 0.0.144 => 0.0.145
```
# v0.0.144
#### 💥 Breaking Changes
- [shape] ` ` renamed ` `, new ` ` not dependent on d3-shape pie generator. [#179](https://github.com/hshoff/vx/pull/179)
#### 🚀 Enhancements
- [demo] add ` ` and ` ` demo tiles + pages. [#179](https://github.com/hshoff/vx/pull/179)
- [mock] add exoplanets, planets, and shakespeare mocks. [#179](https://github.com/hshoff/vx/pull/179)
#### 🐛 Bug Fix
- [hierarchy] rename ` ` classnames from `vx-pack` => `vx-partition`. [#179](https://github.com/hshoff/vx/pull/179)
- [hierarchy] export partition, treemap, and pack from index. [#179](https://github.com/hshoff/vx/pull/179)
```bash
Changes:
- @vx/annotation: 0.0.143 => 0.0.144
- @vx/axis: 0.0.143 => 0.0.144
- @vx/demo: 0.0.143 => 0.0.144
- @vx/grid: 0.0.143 => 0.0.144
- @vx/hierarchy: 0.0.143 => 0.0.144
- @vx/marker: 0.0.143 => 0.0.144
- @vx/mock-data: 0.0.143 => 0.0.144
- @vx/shape: 0.0.143 => 0.0.144
- @vx/vx: 0.0.143 => 0.0.144
```
# v0.0.143
#### :boom: Breaking Changes
- [hierarchy] ` ` & ` ` now only pass `data` as an argument to the child render function [#173](https://github.com/hshoff/vx/pull/173)
#### :rocket: Enhancement
- [hierarchy] add ` `, ` `, & ` ` [#173](https://github.com/hshoff/vx/pull/173)
#### :house: Internal
- [deps][tests] use react 16 dev dep, enzyme 3, jest 21. fix tests. [#178](https://github.com/hshoff/vx/pull/178)
```bash
Changes:
- @vx/annotation: 0.0.142 => 0.0.143
- @vx/axis: 0.0.142 => 0.0.143
- @vx/bounds: 0.0.141 => 0.0.143
- @vx/boxplot: 0.0.140 => 0.0.143
- @vx/brush: 0.0.140 => 0.0.143
- @vx/clip-path: 0.0.140 => 0.0.143
- @vx/curve: 0.0.140 => 0.0.143
- @vx/demo: 0.0.142 => 0.0.143
- @vx/drag: 0.0.140 => 0.0.143
- @vx/event: 0.0.141 => 0.0.143
- @vx/geo: 0.0.140 => 0.0.143
- @vx/glyph: 0.0.140 => 0.0.143
- @vx/gradient: 0.0.140 => 0.0.143
- @vx/grid: 0.0.142 => 0.0.143
- @vx/group: 0.0.140 => 0.0.143
- @vx/heatmap: 0.0.140 => 0.0.143
- @vx/hierarchy: 0.0.141 => 0.0.143
- @vx/legend: 0.0.141 => 0.0.143
- @vx/marker: 0.0.142 => 0.0.143
- @vx/mock-data: 0.0.136 => 0.0.143
- @vx/network: 0.0.140 => 0.0.143
- @vx/pattern: 0.0.140 => 0.0.143
- @vx/point: 0.0.136 => 0.0.143
- @vx/responsive: 0.0.140 => 0.0.143
- @vx/scale: 0.0.140 => 0.0.143
- @vx/shape: 0.0.142 => 0.0.143
- @vx/text: 0.0.140 => 0.0.143
- @vx/tooltip: 0.0.141 => 0.0.143
- @vx/voronoi: 0.0.140 => 0.0.143
- @vx/vx: 0.0.142 => 0.0.143
- @vx/zoom: 0.0.140 => 0.0.143
```
# v0.0.142
#### :rocket: Enhancement
- [shape] add innerRef prop to shapes [#168](https://github.com/hshoff/vx/pull/168)
### :memo: Documentation
- [demo] fix typo on /, fix areas tile details [#169](https://github.com/hshoff/vx/pull/169)
```bash
Changes:
- @vx/annotation: 0.0.141 => 0.0.142
- @vx/axis: 0.0.141 => 0.0.142
- @vx/demo: 0.0.141 => 0.0.142
- @vx/grid: 0.0.141 => 0.0.142
- @vx/marker: 0.0.141 => 0.0.142
- @vx/shape: 0.0.141 => 0.0.142
- @vx/vx: 0.0.141 => 0.0.142
```
# v0.0.141
#### :rocket: Enhancement
- [hierarchy] add render prop to ` ` + ` ` [#163](https://github.com/hshoff/vx/pull/163)
- [axis] render prop for axis, full control over rendering [#165](https://github.com/hshoff/vx/pull/165)
- [event] add touch event support to localPoint(), find owner svg for single arity call [#167](https://github.com/hshoff/vx/pull/167)
#### :bug: Bug Fix
- [shape] fix typo in stack order enum [#164](https://github.com/hshoff/vx/pull/164)
- [legend] fix legend threshold [#166](https://github.com/hshoff/vx/pull/166)
```bash
Changes:
- @vx/annotation: 0.0.140 => 0.0.141
- @vx/axis: 0.0.140 => 0.0.141
- @vx/bounds: 0.0.140 => 0.0.141
- @vx/demo: 0.0.140 => 0.0.141
- @vx/event: 0.0.140 => 0.0.141
- @vx/grid: 0.0.140 => 0.0.141
- @vx/hierarchy: 0.0.140 => 0.0.141
- @vx/legend: 0.0.140 => 0.0.141
- @vx/marker: 0.0.140 => 0.0.141
- @vx/shape: 0.0.140 => 0.0.141
- @vx/tooltip: 0.0.140 => 0.0.141
- @vx/vx: 0.0.140 => 0.0.141
```
# v0.0.140
### :house: Internal
- [deps] add react 16 as peer dep, use react-test-renderer [#155](https://github.com/hshoff/vx/pull/155)
```bash
Changes:
- @vx/annotation: 0.0.139 => 0.0.140
- @vx/axis: 0.0.139 => 0.0.140
- @vx/bounds: 0.0.137 => 0.0.140
- @vx/boxplot: 0.0.136 => 0.0.140
- @vx/brush: 0.0.136 => 0.0.140
- @vx/clip-path: 0.0.136 => 0.0.140
- @vx/curve: 0.0.136 => 0.0.140
- @vx/demo: 0.0.139 => 0.0.140
- @vx/drag: 0.0.136 => 0.0.140
- @vx/event: 0.0.136 => 0.0.140
- @vx/geo: 0.0.136 => 0.0.140
- @vx/glyph: 0.0.136 => 0.0.140
- @vx/gradient: 0.0.136 => 0.0.140
- @vx/grid: 0.0.139 => 0.0.140
- @vx/group: 0.0.136 => 0.0.140
- @vx/heatmap: 0.0.136 => 0.0.140
- @vx/hierarchy: 0.0.139 => 0.0.140
- @vx/legend: 0.0.139 => 0.0.140
- @vx/marker: 0.0.139 => 0.0.140
- @vx/network: 0.0.136 => 0.0.140
- @vx/pattern: 0.0.136 => 0.0.140
- @vx/responsive: 0.0.136 => 0.0.140
- @vx/scale: 0.0.136 => 0.0.140
- @vx/shape: 0.0.139 => 0.0.140
- @vx/text: 0.0.136 => 0.0.140
- @vx/tooltip: 0.0.137 => 0.0.140
- @vx/voronoi: 0.0.136 => 0.0.140
- @vx/vx: 0.0.139 => 0.0.140
- @vx/zoom: 0.0.136 => 0.0.140
```
# v0.0.139
#### :rocket: Enhancement
- [shape] add ` ` for streamgraphs and other fun + exciting things [#153](https://github.com/hshoff/vx/pull/153)
#### :bug: Bug Fix
- [legend] fix legend style prop [#151](https://github.com/hshoff/vx/pull/151)
- [hierarchy] fix name collisions [#147](https://github.com/hshoff/vx/pull/147)
### :memo: Documentation
- [hierarchy] update links and descriptions in readme [#148](https://github.com/hshoff/vx/pull/148)
```bash
Changes:
- @vx/annotation: 0.0.136 => 0.0.139
- @vx/axis: 0.0.138 => 0.0.139
- @vx/demo: 0.0.138 => 0.0.139
- @vx/grid: 0.0.136 => 0.0.139
- @vx/hierarchy: 0.0.138 => 0.0.139
- @vx/legend: 0.0.136 => 0.0.139
- @vx/marker: 0.0.136 => 0.0.139
- @vx/shape: 0.0.136 => 0.0.139
- @vx/vx: 0.0.138 => 0.0.139
```
# v0.0.138
### :boom: Breaking Changes
- [axis] improve `@vx/axis` api, update docs [#142](https://github.com/hshoff/vx/pull/142)
### :memo: Documentation
- [hierarchy] add readme for vx/hierarchy [#136](https://github.com/hshoff/vx/pull/136)
### :house: Internal
- [vx][pkg] bump lerna 2.0.0-beta.38 => 2.1.2 [#145](https://github.com/hshoff/vx/pull/145)
```bash
Changes:
- @vx/axis: 0.0.136 => 0.0.138
- @vx/demo: 0.0.137 => 0.0.138
- @vx/hierarchy: 0.0.136 => 0.0.138
- @vx/vx: 0.0.137 => 0.0.138
```
# v0.0.137
- [vx] add one stop install pkg @vx/vx [#131](https://github.com/hshoff/vx/pull/131)
- [bounds] move react-dom to peerDeps [#132](https://github.com/hshoff/vx/pull/132)
```bash
Changes:
- @vx/bounds: 0.0.136 => 0.0.137
- @vx/demo: 0.0.136 => 0.0.137
- @vx/tooltip: 0.0.136 => 0.0.137
- @vx/vx: 1.0.0 => 0.0.137
```
# v0.0.136
- [all] add package-lock=false to .npmrc fixes [#93](https://github.com/hshoff/vx/issues/93) [#129](https://github.com/hshoff/vx/pull/129)
- [demo][docs] sync vx-demo site documentation with packages [#125](https://github.com/hshoff/vx/pull/125)
- [gradient][pattern] fix typos [#121](https://github.com/hshoff/vx/pull/121)
- [demo] updated geo + network tiles [#120](https://github.com/hshoff/vx/pull/120)
- [event] add touch point [#116](https://github.com/hshoff/vx/pull/116)
- [gradient] Add minimal rendering tests [#114](https://github.com/hshoff/vx/pull/114)
```bash
Changes:
- @vx/annotation: 0.0.131 => 0.0.136
- @vx/axis: 0.0.134 => 0.0.136
- @vx/bounds: 0.0.129 => 0.0.136
- @vx/boxplot: 0.0.131 => 0.0.136
- @vx/brush: 0.0.127 => 0.0.136
- @vx/clip-path: 0.0.127 => 0.0.136
- @vx/curve: 0.0.127 => 0.0.136
- @vx/demo: 0.0.135 => 0.0.136
- @vx/drag: 0.0.127 => 0.0.136
- @vx/event: 0.0.127 => 0.0.136
- @vx/geo: 0.0.135 => 0.0.136
- @vx/glyph: 0.0.127 => 0.0.136
- @vx/gradient: 0.0.129 => 0.0.136
- @vx/grid: 0.0.131 => 0.0.136
- @vx/group: 0.0.127 => 0.0.136
- @vx/heatmap: 0.0.127 => 0.0.136
- @vx/hierarchy: 0.0.127 => 0.0.136
- @vx/legend: 0.0.127 => 0.0.136
- @vx/marker: 0.0.131 => 0.0.136
- @vx/mock-data: 0.0.135 => 0.0.136
- @vx/network: 0.0.135 => 0.0.136
- @vx/pattern: 0.0.127 => 0.0.136
- @vx/point: 0.0.127 => 0.0.136
- @vx/responsive: 0.0.127 => 0.0.136
- @vx/scale: 0.0.127 => 0.0.136
- @vx/shape: 0.0.131 => 0.0.136
- @vx/text: 0.0.127 => 0.0.136
- @vx/tooltip: 0.0.134 => 0.0.136
- @vx/voronoi: 0.0.127 => 0.0.136
- @vx/zoom: 0.0.127 => 0.0.136
```
# v0.0.135
- [geo] add graticule [#111](https://github.com/hshoff/vx/pull/111)
- [network] add @vx/network [#113](https://github.com/hshoff/vx/pull/113)
- [demo] fix invalid JSX [#118](https://github.com/hshoff/vx/pull/118)
- [network][geo][demo] polish for v0.0.135 [#119](https://github.com/hshoff/vx/pull/119)
```bash
Changes:
- @vx/demo: 0.0.134 => 0.0.135
- @vx/geo: 0.0.134 => 0.0.135
- @vx/mock-data: 0.0.127 => 0.0.135
- @vx/network: 0.0.127 => 0.0.135
```
# v0.0.134
- [axis] make ticks more customizable [#109](https://github.com/hshoff/vx/pull/109)
- [tooltip] add ` ` and PropTypes to `@vx/tooltip` exports [#108](https://github.com/hshoff/vx/pull/108)
- [demo] use @vx/geo version in deps [#106](https://github.com/hshoff/vx/pull/106)
```bash
Changes:
- @vx/axis: 0.0.133 => 0.0.134
- @vx/demo: 0.0.133 => 0.0.134
- @vx/tooltip: 0.0.133 => 0.0.134
```
# v0.0.133
- ignore this version, lerna got into a bad state.
```bash
Changes:
- @vx/axis: 0.0.131 => 0.0.133
- @vx/demo: 0.0.132 => 0.0.133
- @vx/tooltip: 0.0.127 => 0.0.133
```
# v0.0.132
- [geo] add package geo [#105](https://github.com/hshoff/vx/pull/105)
```bash
Changes:
- @vx/demo: 0.0.131 => 0.0.132
- @vx/geo: 0.0.132 => 0.0.132
```
# v0.0.131
- [shape] LinePath.defined should default to true [#101](https://github.com/hshoff/vx/pull/101)
- [boxplot] add docs [#102](https://github.com/hshoff/vx/pull/102)
- [shape] add x-value mouseover to area demo [#103](https://github.com/hshoff/vx/pull/103)
- [grid] add styles and restProps support for grid lines [#103](https://github.com/hshoff/vx/pull/103)
```bash
Changes:
- @vx/annotation: 0.0.130 => 0.0.131
- @vx/axis: 0.0.130 => 0.0.131
- @vx/boxplot: 0.0.127 => 0.0.131
- @vx/demo: 0.0.130 => 0.0.131
- @vx/grid: 0.0.130 => 0.0.131
- @vx/marker: 0.0.130 => 0.0.131
- @vx/shape: 0.0.130 => 0.0.131
```
# v0.0.130
- [shape] Add tests for Arc, AreaClosed, & Line, fix AreaClosed error [#95](https://github.com/hshoff/vx/pull/95)
- [Axis] Add tests to Axis.test.js [#94](https://github.com/hshoff/vx/pull/94)
```bash
Changes:
- @vx/annotation: 0.0.127 => 0.0.130
- @vx/axis: 0.0.127 => 0.0.130
- @vx/demo: 0.0.129 => 0.0.130
- @vx/grid: 0.0.127 => 0.0.130
- @vx/marker: 0.0.127 => 0.0.130
- @vx/shape: 0.0.127 => 0.0.130
```
# v0.0.129
- [gradient] add [#90](https://github.com/hshoff/vx/pull/90)
- [bounds] add `@vx/bounds` package with `withBoundingRects()` HOC [#91](https://github.com/hshoff/vx/pull/91)
```bash
Changes:
- @vx/bounds: 0.0.128 => 0.0.129
- @vx/demo: 0.0.128 => 0.0.129
- @vx/gradient: 0.0.128 => 0.0.129
```
# v0.0.128
- ignore this one, `lerna publish` failed midway through
```bash
Changes:
- @vx/bounds: 0.0.0 => 0.0.128
- @vx/demo: 0.0.127 => 0.0.128
- @vx/gradient: 0.0.127 => 0.0.128
```
# v0.0.127
- [boxplot] add `@vx/boxplot` [#89](https://github.com/hshoff/vx/pull/89)
- [mock data] add `genBoxPlot()` [#89](https://github.com/hshoff/vx/pull/89)
- [tooltip] fix pass through style and restProps [#89](https://github.com/hshoff/vx/pull/89)
- [shape] fix BarStack.test.js [#88](https://github.com/hshoff/vx/pull/88)
```bash
Changes:
- @vx/annotation: 0.0.126 => 0.0.127
- @vx/axis: 0.0.126 => 0.0.127
- @vx/boxplot: 1.0.0 => 0.0.127
- @vx/brush: 0.0.126 => 0.0.127
- @vx/clip-path: 0.0.126 => 0.0.127
- @vx/curve: 0.0.126 => 0.0.127
- @vx/demo: 0.0.126 => 0.0.127
- @vx/drag: 0.0.126 => 0.0.127
- @vx/event: 0.0.126 => 0.0.127
- @vx/glyph: 0.0.126 => 0.0.127
- @vx/gradient: 0.0.126 => 0.0.127
- @vx/grid: 0.0.126 => 0.0.127
- @vx/group: 0.0.126 => 0.0.127
- @vx/heatmap: 0.0.126 => 0.0.127
- @vx/hierarchy: 0.0.126 => 0.0.127
- @vx/legend: 0.0.126 => 0.0.127
- @vx/marker: 0.0.126 => 0.0.127
- @vx/mock-data: 0.0.126 => 0.0.127
- @vx/pattern: 0.0.126 => 0.0.127
- @vx/point: 0.0.126 => 0.0.127
- @vx/responsive: 0.0.126 => 0.0.127
- @vx/scale: 0.0.126 => 0.0.127
- @vx/shape: 0.0.126 => 0.0.127
- @vx/text: 0.0.126 => 0.0.127
- @vx/tooltip: 0.0.126 => 0.0.127
- @vx/voronoi: 0.0.126 => 0.0.127
- @vx/zoom: 0.0.126 => 0.0.127
```
# v0.0.126
- [tooltip] add @vx/tooltip [#87](https://github.com/hshoff/vx/pull/87)
- [glyph] put classname on the not on [#87](https://github.com/hshoff/vx/pull/87)
- [mock data] add mock/bitcoinPrice [#87](https://github.com/hshoff/vx/pull/87)
- [demo] add tooltip demo to dots and barstack, add legend to barstack [#87](https://github.com/hshoff/vx/pull/87)
- [shape] update `data` passed to each bar in [#87](https://github.com/hshoff/vx/pull/87)
# v0.0.125
- ignore this one, `lerna publish` failed midway through
# v0.0.124
- [glyph] add remaining d3 symbols [#84](https://github.com/hshoff/vx/pull/84) + [#81](https://github.com/hshoff/vx/pull/81)
- [gradient] add horizontal linear gradients, make more flexible [#82](https://github.com/hshoff/vx/pull/82)
- [axis] export orientation constants [#80](https://github.com/hshoff/vx/pull/80)
- [legend] fix proptypes check on shape prop [#82](https://github.com/hshoff/vx/pull/82)
```bash
Changes:
- @vx/axis: 0.0.120 => 0.0.124
- @vx/demo: 0.0.123 => 0.0.124
- @vx/glyph: 0.0.121 => 0.0.124
- @vx/gradient: 0.0.120 => 0.0.124
- @vx/legend: 0.0.121 => 0.0.124
```
# v0.0.123
- add `@vx/voronoi` [#78](https://github.com/hshoff/vx/pull/78)
```bash
Changes:
- @vx/demo: 0.0.122 => 0.0.123
- @vx/voronoi: 1.0.0 => 0.0.123
```
# v0.0.122
- ignore this one, I ran `lerna publish --exact` before `lerna bootstrap` and it failed to publish, but managed to increment versions and couldn't figure how to "undo" it so rolling foward to v0.0.123
```bash
Changes:
- @vx/demo: 0.0.122 => 0.0.122
- @vx/voronoi: 0.0.0 => 0.0.122
```
# v0.0.121
- add `@vx/legend` [#77](https://github.com/hshoff/vx/pull/77)
- add `scaleQuantize`, `scaleQuantile`, `scaleThreshold`
- added `GlyphCross` but it's not working yet
```bash
Changes:
- @vx/demo: 0.0.120 => 0.0.121
- @vx/glyph: 0.0.120 => 0.0.121
- @vx/legend: 1.0.0 => 0.0.121
- @vx/scale: 0.0.117 => 0.0.121
```
# v0.0.120
- moved `react` to peerDep & devDep [#75](https://github.com/hshoff/vx/pull/75)
- add missing `restProps` + `additionalProps` to shape & glyph [#76](https://github.com/hshoff/vx/pull/76)
- set AreaClosed `y0` to the range's start not `0` [#45](https://github.com/hshoff/vx/pull/74)
- add strokeDashoffset prop to LinePath [#70](https://github.com/hshoff/vx/pull/70)
- replace lodash per-method packages with scoped imports [#66](https://github.com/hshoff/vx/pull/66)
- add tests for pattern circles [#63](https://github.com/hshoff/vx/pull/63)
- add @vx/clip-path [#61](https://github.com/hshoff/vx/pull/61)
- fix axis label transform [#59](https://github.com/hshoff/vx/pull/59)
```bash
Changes:
- @vx/annotation: 0.0.119 => 0.0.120
- @vx/axis: 0.0.119 => 0.0.120
- @vx/brush: 0.0.114 => 0.0.120
- @vx/clip-path: 0.0.0 => 0.0.120
- @vx/demo: 0.0.119 => 0.0.120
- @vx/drag: 0.0.114 => 0.0.120
- @vx/glyph: 0.0.114 => 0.0.120
- @vx/gradient: 0.0.112 => 0.0.120
- @vx/grid: 0.0.119 => 0.0.120
- @vx/group: 0.0.114 => 0.0.120
- @vx/heatmap: 0.0.116 => 0.0.120
- @vx/hierarchy: 0.0.119 => 0.0.120
- @vx/marker: 0.0.119 => 0.0.120
- @vx/pattern: 0.0.112 => 0.0.120
- @vx/responsive: 0.0.115 => 0.0.120
- @vx/shape: 0.0.119 => 0.0.120
- @vx/text: 0.0.114 => 0.0.120
```
# v0.0.114
### @vx/shape
- added ` ` & ` ` [#39](https://github.com/hshoff/vx/pull/39)
### general
- added jest + enzyme tests & travis + coveralls ci
# v0.0.113
### @vx/axis
- axis labels and tickLabels are now passed in as components [#31](https://github.com/hshoff/vx/pull/31) • [example diff](https://github.com/hshoff/vx/pull/31/files#diff-427e08aaa7d707f2374af36902ff0e15)
### @vx/group
- added `transform` prop [#31](https://github.com/hshoff/vx/pull/31)
# v0.0.112
### @vx/curve, @vx/point, @vx/mock-data, @vx/annotation, @vx/group, @vx/pattern, @vx/gradient, @vx/glyph
- added tests with jest + enzyme [#30](https://github.com/hshoff/vx/pull/30)
### @vx/annotation, @vx/pattern
- added prop-types [#30](https://github.com/hshoff/vx/pull/30)
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
Airbnb has adopted a Code of Conduct that we expect project participants to adhere to. Please
[read the full Code of Conduct text](https://airbnb.io/codeofconduct/) so that you can understand
what actions will and will not be tolerated. Report violations to the maintainers of this project or
to [opensource-conduct@airbnb.com](mailto:opensource-conduct@airbnb.com).
Reports sent to [opensource-conduct@airbnb.com](mailto:opensource-conduct@airbnb.com) are received
by Airbnb's open source code of conductmoderation team, which is composed of Airbnb employees. All
communications are private and confidential.
================================================
FILE: CONTRIBUTING.md
================================================
# Contributing
Contributions welcome! Please follow the [code of conduct](./CODE_OF_CONDUCT.md).
## Overview
[Yarn workspaces](https://yarnpkg.com/lang/en/docs/workspaces/) are used to manage dependencies and
build config across packages in the umbrella `visx` monorepo, and
[lerna](https://github.com/lerna/lerna/) is used to manage versioning.
## Project structure
```
visx/
lerna.json
package.json
packages/
visx-package-1/
src/
test/
build/
package.json
...
visx-package-2/
...
...
```
## Local development
Run the following to setup your local dev environment:
```sh
# Install `yarn`, alternatives at https://yarnpkg.com/en/docs/install
curl -o- -L https://yarnpkg.com/install.sh | bash
# Clone or fork `visx`
git clone git@github.com:airbnb/visx.git # or your fork
cd visx
# install dependencies, and have `yarn` symlink within-`visx` dependencies
yarn
# build packages and generate types for local development
yarn build
```
#### Rebuild specific package(s)
Upon modification of a single `package` you can run the following to rebuild it. Note that you can
specify multiple packages to build this way, and optionally append `--watch` to continuously watch
for changes.
```sh
# build the specified package(s) as cjs + esm versions
# example `PKG=@visx/axis yarn babel:pkg`
PKG=@visx/{package[,package]} yarn babel:pkg
# generate d.ts(definition files) the specified package(s)
# and rebuild any other packages the specified package(s) depend on
# example `PKG=@visx/axis yarn type:pkg`
PKG=@visx/{package[,package]} yarn type:pkg
```
from the `visx` monorepo root to re-build the package with your changes.
#### Running demo pages
You can use the local [`next.js`](https://nextjs.org) dev server within `packages/visx-demo` to view
and iterate on your changes in the gallery. From the `packages/visx-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, see above section).
#### Config generation
`visx` uses [`@airbnb/nimbus`](https://github.com/airbnb/nimbus) to generate build configuration for
`eslint`, `prettier`, `jest`, `babel`, and `typescript`.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017-2018 Harrison Shoff
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: PULL_REQUEST_TEMPLATE.md
================================================
#### :boom: Breaking Changes
-
#### :rocket: Enhancements
-
#### :memo: Documentation
-
#### :bug: Bug Fix
-
#### :house: Internal
-
================================================
FILE: README.md
================================================
### visx
visx is a collection of reusable low-level visualization components. visx combines the power of d3
to generate your visualization with the benefits of react for updating the DOM.
Docs
•
Gallery
•
Blog
•
Discussions
•
Changelog
•
Getting started tutorial
## Usage
Let's make a simple bar graph.
First we'll install the relevant packages:
```bash
npm install --save @visx/mock-data @visx/group @visx/shape @visx/scale
```
```javascript
import React from 'react';
import { letterFrequency } from '@visx/mock-data';
import { Group } from '@visx/group';
import { Bar } from '@visx/shape';
import { scaleLinear, scaleBand } from '@visx/scale';
// We'll use some mock data from `@visx/mock-data` for this.
const data = letterFrequency;
// Define the graph dimensions and margins
const width = 500;
const height = 500;
const margin = { top: 20, bottom: 20, left: 20, right: 20 };
// Then we'll create some bounds
const xMax = width - margin.left - margin.right;
const yMax = height - margin.top - margin.bottom;
// We'll make some helpers to get at the data we want
const x = (d) => d.letter;
const y = (d) => +d.frequency * 100;
// And then scale the graph by our data
const xScale = scaleBand({
range: [0, xMax],
round: true,
domain: data.map(x),
padding: 0.4,
});
const yScale = scaleLinear({
range: [yMax, 0],
round: true,
domain: [0, Math.max(...data.map(y))],
});
// 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);
// Finally we'll embed it all in an SVG
function BarGraph(props) {
return (
);
}
// ... somewhere else, render it ...
//
```
For more examples using `visx`, check out the [gallery](https://airbnb.io/visx/gallery).
## Motivation
**Goal**
The goal is to create a library of components you can use to make both your own reusable chart
library or your slick custom one-off chart. visx is largely unopinionated and is meant to be built
upon. Keep your bundle sizes down and use only the packages you need.
**How?**
Under the hood, visx is using d3 for the calculations and math. If you're creating your own awesome
chart library on top of visx, it's easy to create a component api that hides d3 entirely. Meaning
your team could create charts as easily as using reusable react components.
**But why?**
Mixing two mental models for updating the DOM is never a good time. Copy and pasting d3 code into
`componentDidMount()` is just that. This collection of components lets you easily build your own
reusable visualization charts or library without having to learn d3. No more selections or
`enter()`/`exit()`/`update()`.
## In the wild
- [williaster/data-ui](https://github.com/williaster/data-ui)
([Demo](https://williaster.github.io/data-ui/))
- [dylanmoz/trello](https://github.com/DylanMoz/dylanmoz.github.io/blob/source/src/pages/trello/TrelloGraph.js)
([Demo](http://dylanmoz.github.io/trello/))
([How to Make Beautiful Graphs With vx and React-Motion](https://devblog.classy.org/how-to-make-beautiful-graphs-with-vx-and-react-motion-6ffe7aecf6f3))
- [gkunthara/Crypto-Chart](https://github.com/gkunthara/Crypto-Chart)
([Tutorial](https://medium.com/@georgekunthara/after-the-tutorial-the-first-react-app-4dce6645634e))
- Collapsible tree with [`react-move`](https://github.com/react-tools/react-move) by
[@techniq](https://github.com/techniq) ([Demo](https://codesandbox.io/s/n3w687vmqj))
([Radial demo](https://codesandbox.io/s/vmqwrkl395))
([More info](https://github.com/airbnb/visx/issues/162#issuecomment-335029517))
- Bitcoin 30-day price by [@hshoff](https://github.com/hshoff)
([Github](https://github.com/hshoff/viewsource#1-bitcoin-price-chart))
([YouTube](https://www.youtube.com/watch?v=oeE2tuspdHg))
- Ethereum candlestick chart by [@hshoff](https://github.com/hshoff)
([Github](https://github.com/hshoff/viewsource#2-ethereum-candlestick-chart))
- Song data visualization through spotify by [@bother7](https://github.com/bother7)
([Github](https://github.com/bother7/spotalyzer_frontend))
- Investment Calculator ([website](https://investmentcalculator.io/))
- Animation with [`react-spring`](https://github.com/drcmda/react-spring/) by
[@drcmda](https://github.com/drcmda) ([Demo](https://codesandbox.io/embed/j3x61vjz5v))
- Code Coverage Dashboard by [@ezy](https://github.com/ezy)
([Github](https://github.com/ezy/code-coverage-dashboard))
- Ethereum Portfolio Toolkit by [@JayWelsh](https://github.com/JayWelsh)
([Demo](https://cryptocape.com/)) ([Github](https://github.com/JayWelsh/CryptoCape))
- Family tree by [@vkallore](https://github.com/vkallore)
([Github](https://github.com/vkallore/d3-vx-family-tree))
- South African Coronavirus Data Visuals by [@JayWelsh](https://github.com/JayWelsh)
([Demo](https://coronamap.co.za/)) ([Github](https://github.com/JayWelsh/coronamap))
- [CNN: Tracking America's Recovery](https://www.cnn.com/business/us-economic-recovery-coronavirus)
- [Wall Street Journal: Americans Familiarize Themselves with the Word ‘Forbearance’](https://blogs.wsj.com/dailyshot/2020/04/13/the-daily-shot-americans-familiarize-themselves-with-the-word-forbearance/)
by [@rayshan](https://github.com/rayshan)
([Demo](https://finance.shan.io/recessions-bear-markets-compared))
- Dollar to food emoji caculator by [@gmlwo530](https://github.com/gmlwo530)
([Demo](https://dollar-to-food-emoji.web.app/))
([Github](https://github.com/gmlwo530/dollar-to-food-emoji))
- [zh-TW] Taiwan Real-time Air Quality Index by
[@ArvinH](https://github.com/ArvinH)([Demo](https://codesandbox.io/s/simpleradar-aqi-with-tooltip-select-data-react-spring-item3?file=/Radar.tsx))([Tutorial](https://blog.arvinh.info/tech/datavis-visx))
- tokenized BTC on ethereum stacked chart with brush by [@sakulstra](https://github.com/sakulstra)
- [Escape From Tarkov Ammo Chart](https://eft.monster/) by
[@codenomial](https://github.com/codenomial)
- [Pry](https://pry.co) Finance for Founders (dashboard by [@valtism](https://github.com/valtism))
- [Data 2 the People](https://www.data2thepeople.org/) Donation Efficacy Analysis for Downballot
Races ([Demo](https://donate.data2thepeople.org/))
([Github](https://github.com/Data-2-the-People/skyfall/blob/master/components/Scatterplot.jsx))
- [Augora](https://augora.fr) Display information of french deputies
([Demo](https://augora.fr/statistiques))([Github](https://github.com/Augora/Augora))
- WHO Coronavirus (COVID-19) Dashboard is built on top of `vx`, earlier version of `visx`.
([Demo](https://covid19.who.int/))
- [Fig Stats](https://fig-stats.com) - Figma community plugin & widget analytics
- [Physician.FYI](https://physician.fyi) - Explore physicians' disciplinary history
- [Index by Superstardle](https://index.superstardle.com),
[Salaries by Superstardle](https://salaries.superstardle.com), &
[Pack'Em by Superstardle](https://playoffs.superstardle.com) - Explore professional sports teams
and superstars in the world of underdogs, salaries, and playoff performances.
- Ridgeline chart visualizing shuffling probabilities by [@jmssnr](https://github.com/jmssnr)
([Demo](https://shuffling-probability.vercel.app/))
([Github](https://github.com/jmssnr/shuffling-probability))
- [UCSF Data Library](https://datalibrary.ucsf.edu) - Landing page for disease research tools
([Github](https://github.com/mountetna/monoetna/tree/master/vesta/ui))
Have a project that's using `visx`? Open a pull request and we'll add it to the list.
## FAQ
1. What does `visx` stand for?
> visx stands for visualization components.
1. Do you plan on supporting animation/transitions?
> A common criticism of visx is it doesn't have animation baked in, but this was a conscious
> choice. It's a powerful feature to not bake it in.
>
> Imagine your app already bundles `react-motion`, adding a hypothetical `@visx/animation` is
> bloat. Since visx is react, it already supports all react animation libs.
>
> Charting libraries are like style guides. Each org or app will eventually want full control
> over their own implementation.
>
> visx makes this easier for everyone. No need to reinvent the wheel each time.
>
> more info: https://github.com/airbnb/visx/issues/6
>
> examples:
>
> - Collapsible tree with [`react-move`](https://github.com/react-tools/react-move) by
> [@techniq](https://github.com/techniq) ([Demo](https://codesandbox.io/s/n3w687vmqj))
> ([Radial demo](https://codesandbox.io/s/vmqwrkl395))
> - Animation with `react-spring` by [@drcmda](https://github.com/drcmda)
> ([Demo](https://codesandbox.io/embed/j3x61vjz5v))
1. Do I have to use every package to make a chart?
> nope! pick and choose the packages you need.
1. Can I use this to create my own library of charts for my team?
> Please do.
1. Does visx work with [preact](https://preactjs.com/)?
> yup! need to alias `react` + `react-dom` and use `preact-compat`.
1. I like using d3.
> Me too.
## Development
Please see [CONTRIBUTING.md](./CONTRIBUTING.md)
:v:
[MIT](./LICENSE)
================================================
FILE: babel.config.js
================================================
const esm = process.env.ESM;
const envOptions = {
loose: false,
modules: esm ? false : 'commonjs',
shippedProposals: true,
targets: {
browsers: [
'chrome >= 108',
'edge >= 108',
'firefox >= 133',
'safari >= 15.6',
'ios_saf >= 15.6',
'samsung >= 27',
],
},
bugfixes: false,
};
const presets = [
['@babel/preset-env', envOptions],
['@babel/preset-react', { runtime: 'automatic', useBuiltIns: true, useSpread: true }],
'@babel/preset-typescript',
];
const plugins = [];
const ignore = [
'coverage/',
'public/',
'esm/',
'lib/',
'tmp/',
'dist/',
'*.d.ts',
'__tests__',
'__mocks__',
];
switch (process.env.NODE_ENV) {
case 'test': {
envOptions.modules = 'commonjs';
envOptions.targets = { node: 'current' };
plugins.push('babel-plugin-dynamic-import-node');
break;
}
case 'development':
case 'production':
default: {
break;
}
}
module.exports = {
ignore,
plugins,
presets,
};
================================================
FILE: config-eslint/base.js
================================================
const EXTS = ['.ts', '.tsx', '.js', '.jsx', '.json'];
const EXTS_GROUP = '{ts,tsx,js,jsx}';
const ASSET_EXT_PATTERN = /\.(ttf|eot|otf|svg|woff|woff2|mp3|png|jpg|jpeg|gif|ico)$/;
module.exports = {
parser: '@babel/eslint-parser',
parserOptions: {
requireConfigFile: false,
},
extends: ['airbnb', 'plugin:jsx-a11y/recommended'],
plugins: ['import', 'react', 'react-hooks'],
globals: {
// Metrics and analytics providers
ga: 'readonly',
// Mostly for easier compatibility between browsers, workers, etc
global: 'readonly',
// Mostly references to `process.env.NODE_ENV`
process: 'readonly',
},
env: {
browser: true,
node: false,
},
reportUnusedDisableDirectives: true,
settings: {
propWrapperFunctions: ['forbidExtraProps', 'exact', 'Object.freeze'],
'import/ignore': ['node_modules', '\\.json$', ASSET_EXT_PATTERN.source],
'import/extensions': EXTS,
'import/resolver': {
node: {
extensions: EXTS,
},
},
},
rules: {
'react-hooks/exhaustive-deps': 'error',
'react-hooks/rules-of-hooks': 'error',
},
overrides: [
{
files: [`*.test.${EXTS_GROUP}`],
plugins: [],
globals: {
// vitest globals
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
assert: 'readonly',
vitest: 'readonly',
vi: 'readonly',
beforeAll: 'readonly',
beforeEach: 'readonly',
afterAll: 'readonly',
afterEach: 'readonly',
// other globals
jsdom: 'readonly',
},
env: {
node: true,
},
rules: {
'max-classes-per-file': 'off',
'no-magic-numbers': 'off',
'sort-keys': 'off',
// REACT
'react/function-component-definition': 'off',
},
},
],
};
================================================
FILE: config-eslint/next.js
================================================
const EXTS_GROUP = '{ts,tsx,js,jsx}';
module.exports = {
parser: '@babel/eslint-parser',
plugins: ['promise', 'unicorn'],
rules: {
// Not enabled in Airbnb
'default-param-last': 'error',
'func-name-matching': [
'error',
'always',
{
considerPropertyDescriptor: true,
includeCommonJSModuleExports: false,
},
],
'jsx-quotes': ['error', 'prefer-double'],
'multiline-comment-style': 'off',
'multiline-ternary': ['error', 'never'],
'no-constant-condition': 'error',
'no-constructor-return': 'error',
'no-div-regex': 'error',
'no-dupe-else-if': 'error',
'no-implicit-coercion': 'error',
'no-import-assign': 'error',
'no-native-reassign': 'error',
'no-negated-condition': 'error',
'no-setter-return': 'error',
'no-useless-call': 'error',
'prefer-exponentiation-operator': 'error',
'prefer-regex-literals': 'error',
'require-atomic-updates': 'error',
// Replaced with new proposals
'react/jsx-props-no-spreading': 'off',
'react/state-in-constructor': 'off',
'react/static-property-placement': 'off',
// IMPORT
'import/default': 'error',
'import/namespace': 'error',
'import/no-unused-modules': 'off', // Super broken at the moment
'import/imports-first': 'error',
// PROMISE
'promise/no-callback-in-promise': 'error',
'promise/no-new-statics': 'error',
'promise/no-promise-in-callback': 'error',
'promise/no-return-in-finally': 'error',
'promise/no-return-wrap': ['error', { allowReject: true }],
'promise/param-names': 'error',
'promise/valid-params': 'error',
// REACT
'react/destructuring-assignment': 'off', // Broken with class properties
'react/forbid-prop-types': ['error', { forbid: ['any', 'array'] }],
'react/jsx-handler-names': [
'error',
{
eventHandlerPrefix: 'handle',
eventHandlerPropPrefix: 'on',
},
],
'react/jsx-key': 'error',
'react/jsx-no-literals': 'error',
'react/jsx-no-useless-fragment': 'error',
'react/jsx-no-script-url': 'error',
'react/jsx-sort-default-props': [
'error',
{
ignoreCase: true,
},
],
'react/jsx-sort-props': [
'error',
{
callbacksLast: true,
shorthandFirst: true,
noSortAlphabetically: true,
reservedFirst: true,
},
],
'react/no-did-mount-set-state': 'error',
'react/no-direct-mutation-state': 'error',
'react/no-unknown-property': [
2,
{
ignore: ['jsx', 'global'], // used by next
},
],
'react/sort-comp': 'off',
'react/sort-prop-types': [
'error',
{
ignoreCase: true,
callbacksLast: true,
requiredFirst: false,
sortShapeProp: true,
},
],
// UNICORN
'unicorn/better-regex': 'error',
'unicorn/catch-error-name': 'error',
'unicorn/consistent-function-scoping': 'error',
'unicorn/custom-error-definition': 'error',
'unicorn/error-message': 'error',
'unicorn/escape-case': 'error',
'unicorn/explicit-length-check': 'error',
'unicorn/filename-case': 'off',
'unicorn/import-index': 'error',
'unicorn/new-for-builtins': 'error',
'unicorn/no-abusive-eslint-disable': 'off',
'unicorn/no-array-instanceof': 'error',
'unicorn/no-hex-escape': 'error',
'unicorn/no-fn-reference-in-iterator': 'error',
'unicorn/no-for-loop': 'error',
'unicorn/no-new-buffer': 'error',
'unicorn/no-process-exit': 'error',
'unicorn/no-zero-fractions': 'error',
'unicorn/number-literal-case': 'error',
'unicorn/prefer-add-event-listener': 'error',
'unicorn/prefer-dataset': 'error',
'unicorn/prefer-event-key': 'error',
'unicorn/prefer-flat-map': 'error',
'unicorn/prefer-includes': 'error',
'unicorn/prefer-modern-dom-apis': 'error',
'unicorn/prefer-negative-index': 'error',
'unicorn/prefer-node-append': 'error',
'unicorn/prefer-node-remove': 'error',
'unicorn/prefer-starts-ends-with': 'error',
'unicorn/prefer-string-slice': 'error',
'unicorn/prefer-spread': 'off', // Currently broken
'unicorn/prefer-text-content': 'error',
'unicorn/prefer-trim-start-end': 'error',
'unicorn/prefer-type-error': 'error',
'unicorn/throw-new-error': 'error',
},
overrides: [
{
files: [`*.test.${EXTS_GROUP}`],
rules: {
'unicorn/no-fn-reference-in-iterator': 'off',
'unicorn/consistent-function-scoping': 'off',
},
},
],
};
================================================
FILE: config-eslint/prettier.js
================================================
module.exports = {
extends: ['prettier'],
plugins: ['prettier'],
rules: {
'prettier/prettier': 'error',
},
};
================================================
FILE: config-eslint/typescript.js
================================================
const EXTS_GROUP = '{ts,tsx,js,jsx}';
// In TS, all arguments are required for type information,
// so we need to override the base JS setting.
const noUnused = { vars: 'all', args: 'none', ignoreRestSiblings: true };
const project = 'tsconfig.eslint.json';
module.exports = {
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
},
parserOptions: {
project,
},
overrides: [
{
files: ['*.{ts,tsx}'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
rules: {
'func-call-spacing': 'off',
'no-restricted-globals': 'off',
'no-unused-vars': 'off',
// IMPORT (Conflicts with TS patterns)
'no-duplicate-imports': 'off', // Disabled in favor of import/no-duplicates
'import/export': 'off', // TypeScript allows same name for type and value exports
'import/extensions': [
'error',
'never',
{
json: 'always',
},
],
'import/named': 'off',
'import/no-cycle': 'off',
'import/no-duplicates': 'error', // Handles TS type/value namespace separation
'import/no-named-as-default': 'off',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
`test/**/*.${EXTS_GROUP}`,
`tests/**/*.${EXTS_GROUP}`,
`**/*.test.${EXTS_GROUP}`,
`**/jest.config.${EXTS_GROUP}`,
`**/webpack.config.${EXTS_GROUP}`,
`**/webpack.config.*.${EXTS_GROUP}`,
],
optionalDependencies: false,
},
],
// REACT (We dont use prop types)
'react/default-props-match-prop-types': 'off',
'react/jsx-filename-extension': [
'error',
{
extensions: ['.tsx'],
},
],
'react/no-unused-prop-types': 'off',
'react/prop-types': 'off',
'react/require-default-props': 'off',
// UNICORN
'unicorn/no-fn-reference-in-iterator': 'off',
// TYPESCRIPT
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/await-thenable': 'error',
'@typescript-eslint/camelcase': 'off', // broken
'@typescript-eslint/class-name-casing': 'off', // broken
'@typescript-eslint/consistent-type-assertions': [
'error',
{ assertionStyle: 'as', objectLiteralTypeAssertions: 'allow-as-parameter' },
],
'@typescript-eslint/consistent-type-exports': ['error', { fixMixedExportsWithInlineTypeSpecifier: true }],
'@typescript-eslint/consistent-type-imports': ['error', { prefer: 'type-imports' }],
'@typescript-eslint/explicit-function-return-type': 'off', // Allow inferrence
'@typescript-eslint/func-call-spacing': ['error', 'never'],
'@typescript-eslint/member-delimiter-style': 'error',
'@typescript-eslint/member-ordering': 'off', // Prefer react/sort-comp
'@typescript-eslint/no-array-constructor': 'error',
'@typescript-eslint/no-empty-function': 'off', // Default props are usually empty
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-explicit-any': [
'warn',
{ fixToUnknown: false, ignoreRestArgs: true },
],
'@typescript-eslint/no-extra-parens': 'error',
'@typescript-eslint/no-for-in-array': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-misused-promises': 'error',
'@typescript-eslint/no-require-imports': 'error',
'@typescript-eslint/no-unnecessary-boolean-literal-compare': 'error',
'@typescript-eslint/no-unsafe-call': 'warn',
'@typescript-eslint/no-unsafe-member-access': 'warn',
'@typescript-eslint/no-unsafe-return': 'warn',
'@typescript-eslint/no-unused-vars': ['error', noUnused],
'@typescript-eslint/no-use-before-define': 'error',
'@typescript-eslint/prefer-as-const': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
'@typescript-eslint/prefer-nullish-coalescing': 'off', // Lots of false positives
'@typescript-eslint/prefer-optional-chain': 'error',
'@typescript-eslint/promise-function-async': 'off', // Conflicts with other async rules
'@typescript-eslint/require-await': 'error',
'@typescript-eslint/switch-exhaustiveness-check': 'error',
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/type-annotation-spacing': 'error',
'@typescript-eslint/unified-signatures': 'error',
},
},
],
};
================================================
FILE: lerna.json
================================================
{
"packages": [
"packages/*"
],
"version": "4.0.1-alpha.0",
"command": {
"publish": {
"allowBranch": "master"
}
},
"$schema": "node_modules/lerna/schemas/lerna-schema.json"
}
================================================
FILE: package.json
================================================
{
"name": "@visx/root",
"license": "MIT",
"version": "0.0.0",
"description": "Low-level visualization components",
"keywords": [
"react",
"d3",
"visualization",
"visx",
"charts"
],
"engines": {
"node": ">=22.0.0",
"yarn": ">=4.0.0"
},
"contributors": [
{
"name": "Harrison Shoff",
"url": "https://github.com/hshoff"
},
{
"name": "Chris Williams",
"url": "https://github.com/williaster"
},
{
"name": "Krist Wongsuphasawat",
"url": "https://github.com/kristw"
}
],
"private": true,
"scripts": {
"build:vendor": "yarn run ts ./packages/visx-vendor/scripts/buildVendor",
"babel": "yarn run babel:cjs && yarn run babel:esm",
"babel:cjs": "lerna exec --ignore '@visx/{demo,vendor}' --parallel -- babel --root-mode upward --delete-dir-on-start src/ --out-dir lib --extensions .ts,.tsx",
"babel:esm": "ESM=true lerna exec --ignore '@visx/{demo,vendor}' --parallel -- babel --root-mode upward --delete-dir-on-start src/ --out-dir esm --extensions .ts,.tsx",
"babel:pkg": "lerna exec --scope $PKG -- babel --root-mode upward --delete-dir-on-start src/ --out-dir lib --extensions .ts,.tsx && lerna exec --scope $PKG -- ESM=true babel --root-mode upward --delete-dir-on-start src/ --out-dir esm --extensions .ts,.tsx",
"build": "yarn run build:vendor && yarn run babel && yarn run type",
"build:sizes": "yarn run ts ./scripts/computeBuildSizes.ts",
"build:release": "yarn run ts ./scripts/performRelease/index.ts",
"dev:demo": "yarn run docs:generate && yarn workspace @visx/demo dev",
"check:sizes": "yarn run ts ./scripts/compareBuildSizes.ts",
"clean": "rm -rf ./packages/**/{lib,esm}",
"docs:generate": "yarn run ts ./scripts/generateDocs.ts",
"format": "lerna exec --parallel -- prettier --write --ignore-path ../../.prettierignore .",
"lint": "eslint packages/ --quiet",
"lint:fix": "yarn run lint --fix",
"postinstall": "yarn run ts ./scripts/postInstall.ts",
"prepare-release": "git checkout master && git pull --rebase origin master && lerna updated",
"release": "yarn run prepare-release && lerna publish --exact",
"setup": "yarn run build",
"test": "yarn run vitest run",
"test:watch": "yarn run vitest watch",
"vitest": "vitest",
"vitest:ui": "vitest --ui",
"ts": "ts-node --project ./tsconfig.node.json",
"type": "yarn type:clean && yarn type:build",
"type:build": "lerna exec -- tsc --build",
"type:clean": "find . -type f -name 'tsconfig.tsbuildinfo' -delete",
"type:pkg": "lerna exec --scope $PKG -- tsc --build --verbose",
"type:update-refs": "yarn run ts ./scripts/updateTsReferences.ts",
"vendor-check": "yarn run ts ./packages/visx-vendor/scripts/flagVendorRequirements.ts"
},
"devDependencies": {
"@babel/cli": "^7.26.4",
"@babel/core": "^7.26.0",
"@babel/eslint-parser": "^7.25.9",
"@babel/preset-env": "^7.26.0",
"@babel/preset-react": "^7.26.3",
"@babel/preset-typescript": "^7.26.0",
"@octokit/openapi-types": "18.0.0",
"@octokit/rest": "18.1.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.0",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^13.5.0",
"@types/jsdom": "27.0.0",
"@types/node": "^22.10.2",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@typescript-eslint/eslint-plugin": "^8.18.1",
"@typescript-eslint/parser": "^8.18.1",
"@vitest/coverage-v8": "4",
"@vitest/eslint-plugin": "^1.4.0",
"@vitest/ui": "4",
"airbnb-js-shims": "^2.2.1",
"chalk": "4.1.0",
"eslint": "^8.57.0",
"eslint-config-airbnb": "^19.0.4",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-react": "^7.31.11",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-unicorn": "^45.0.2",
"fast-glob": "3.2.5",
"filesize": "6.1.0",
"jsdom": "27.0.0",
"lerna": "^9.0.0",
"prettier": "^2.8.1",
"react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0",
"react-dom": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0",
"timezone-mock": "^1.1.0",
"ts-node": "^10.9.1",
"typescript": "^5.7.0",
"vitest": "4"
},
"packageManager": "yarn@4.10.3",
"workspaces": {
"packages": [
"./packages/*"
]
},
"resolutions": {
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0"
}
}
================================================
FILE: packages/sizes.json
================================================
{"visx-annotation":{"esm":19643,"lib":23755},"visx-axis":{"esm":13359,"lib":18132},"visx-bounds":{"esm":1493,"lib":1915},"visx-brush":{"esm":41994,"lib":44001},"visx-chord":{"esm":1901,"lib":2683},"visx-clip-path":{"esm":1507,"lib":2713},"visx-curve":{"esm":351,"lib":2594},"visx-delaunay":{"esm":1466,"lib":2489},"visx-demo":{"esm":0,"lib":0},"visx-drag":{"esm":9053,"lib":11025},"visx-event":{"esm":3548,"lib":5257},"visx-geo":{"esm":8057,"lib":11674},"visx-glyph":{"esm":7192,"lib":11421},"visx-gradient":{"esm":6872,"lib":11581},"visx-grid":{"esm":9592,"lib":12892},"visx-group":{"esm":499,"lib":1030},"visx-heatmap":{"esm":3490,"lib":4564},"visx-hierarchy":{"esm":8639,"lib":13458},"visx-legend":{"esm":14799,"lib":20754},"visx-marker":{"esm":3822,"lib":6120},"visx-mock-data":{"esm":325638,"lib":330728},"visx-network":{"esm":3058,"lib":5105},"visx-pattern":{"esm":7349,"lib":10317},"visx-point":{"esm":776,"lib":1901},"visx-react-spring":{"esm":8697,"lib":11009},"visx-responsive":{"esm":9041,"lib":11514},"visx-sankey":{"esm":2498,"lib":3894},"visx-scale":{"esm":18819,"lib":30918},"visx-shape":{"esm":51809,"lib":74614},"visx-stats":{"esm":9249,"lib":10690},"visx-text":{"esm":5953,"lib":7453},"visx-threshold":{"esm":2064,"lib":2758},"visx-tooltip":{"esm":9173,"lib":13672},"visx-vendor":{"esm":2974,"lib":3226},"visx-visx":{"esm":1538,"lib":3999},"visx-voronoi":{"esm":1298,"lib":2088},"visx-wordcloud":{"esm":1987,"lib":2995},"visx-xychart":{"esm":125113,"lib":155595},"visx-zoom":{"esm":12499,"lib":14835}}
================================================
FILE: packages/visx-annotation/.npmrc
================================================
package-lock=false
================================================
FILE: packages/visx-annotation/Readme.md
================================================
# @visx/annotation
SVG `Annotation`s enable you to label points, thresholds, or regions of a visualization to provide
additional context to for your chart consumer. This package is heavily inspired by
[Susie Lu](https://github.com/susielu/)'s
[`react-annotation`](https://github.com/susielu/react-annotation) library.
Each annotation consists of three (optional) parts:
1. `Subject` (`CircleSubject`, `LineSubject`, more 🔜) – what part of a chart is being annotated
(point, line, region)
2. `Label` – the text component for the annotation. Handles SVG text wrapping using `@visx/text`,
and supports `title` and `subtitle` customization as well as vertical & horizontal anchors /
alignment
3. `Connector` – line connecting a subject and label
The `Annotation` or `EditableAnnotation` component wrappers allow you to compose these components
and simplify their individual positioning:
```tsx
...}
>
```
Components can also be used in isolation, in which case you must specify exact positions for each
item:
```tsx
() => (
)
```
##### ⚠️ `ResizeObserver` dependency
The `Label` component relies on `ResizeObserver`s for auto-sizing. If you need a polyfill, you can
either polute the `window` object or inject it cleanly through props:
```tsx
import { ResizeObserver } from 'your-favorite-polyfill';
function App() {
return
```
## Installation
```
npm install --save @visx/annotation
```
================================================
FILE: packages/visx-annotation/package.json
================================================
{
"name": "@visx/annotation",
"version": "4.0.1-alpha.0",
"description": "visx annotation",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./esm/index.js",
"require": "./lib/index.js"
}
},
"files": [
"lib",
"esm"
],
"repository": {
"type": "git",
"url": "git+https://github.com/airbnb/visx.git"
},
"keywords": [
"visx",
"react",
"d3",
"visualizations",
"charts"
],
"author": "@hshoff",
"license": "MIT",
"bugs": {
"url": "https://github.com/airbnb/visx/issues"
},
"homepage": "https://github.com/airbnb/visx#readme",
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0"
},
"dependencies": {
"@types/react": "*",
"@visx/drag": "workspace:*",
"@visx/group": "workspace:*",
"@visx/text": "workspace:*",
"classnames": "^2.3.1",
"react-use-measure": "^2.0.4"
},
"devDependencies": {
"@juggle/resize-observer": "^3.3.1"
},
"publishConfig": {
"access": "public"
}
}
================================================
FILE: packages/visx-annotation/src/components/Annotation.tsx
================================================
import { useMemo, type ReactNode } from 'react';
import AnnotationContext from '../context/AnnotationContext';
import type { AnnotationContextType } from '../types';
export type AnnotationProps = Pick & {
/** Annotation children (Subject, Label, Connector) */
children: ReactNode;
};
export default function Annotation({ x, y, dx, dy, children }: AnnotationProps) {
const value = useMemo(() => ({ x, y, dx, dy }), [x, y, dx, dy]);
return {children} ;
}
================================================
FILE: packages/visx-annotation/src/components/CircleSubject.tsx
================================================
import { useContext, type SVGProps } from 'react';
import cx from 'classnames';
import type { AnnotationContextType } from '../types';
import AnnotationContext from '../context/AnnotationContext';
export type CircleSubjectProps = Pick & {
/** Optional className to apply to CircleSubject in addition to 'visx-annotation-subject'. */
className?: string;
/** Color of CircleSubject. */
stroke?: string;
/** Radius of CircleSubject. */
radius?: number;
};
export default function CircleSubject({
className,
x: propsX,
y: propsY,
stroke = '#222',
radius = 16,
...restProps
}: CircleSubjectProps & Omit, keyof CircleSubjectProps>) {
// if props are provided, they take precedence over context
const annotationContext = useContext(AnnotationContext);
return (
);
}
================================================
FILE: packages/visx-annotation/src/components/Connector.tsx
================================================
import { useContext, type SVGProps } from 'react';
import cx from 'classnames';
import type { AnnotationContextType } from '../types';
import AnnotationContext from '../context/AnnotationContext';
// @TODO
// add end marker support
export type ConnectorProps = Pick & {
/** Optional className to apply to container in addition to 'visx-annotation-connector'. */
className?: string;
/** Connector type. */
type?: 'line' | 'elbow';
/** Color of the connector line. */
stroke?: string;
/** Optional additional props. */
pathProps?: SVGProps;
};
export default function Connector({
className,
x: propsX,
y: propsY,
dx: propsDx,
dy: propsDy,
type = 'elbow',
stroke = '#222',
pathProps,
}: ConnectorProps) {
// if props are provided, they take precedence over context
const annotationContext = useContext(AnnotationContext);
const x0 = propsX == null ? annotationContext.x ?? 0 : propsX;
const y0 = propsY == null ? annotationContext.y ?? 0 : propsY;
const dx = propsDx == null ? annotationContext.dx ?? 0 : propsDx;
const dy = propsDy == null ? annotationContext.dy ?? 0 : propsDy;
let x1: number = x0; // only used with elbow type
let y1: number = y0;
const x2 = x0 + dx;
const y2 = y0 + dy;
if (type === 'elbow') {
// if dx < dy, find the intesection of y=x or y=-x from subject, with vertical line to label
if (Math.abs(dx) <= Math.abs(dy)) {
// target line is vertical x = x2
x1 = x2;
// intersection with y=x line (if dy > 0) or y=x (if dy < 0)
const sign = dy > 0 ? 1 : -1;
y1 = y0 + sign * Math.abs(x1 - x0);
}
// if dx > dy, find the intesection of y=x or y=-x from subject, with horizontal line to label
else {
// target line is horizontal y = y2
y1 = y2;
// find intersection with y=-x line (if dx > 0) or y=x (if dx < 0)
const sign = dx > 0 ? 1 : -1;
x1 = x0 + sign * Math.abs(y1 - y0);
}
}
return (
);
}
================================================
FILE: packages/visx-annotation/src/components/EditableAnnotation.tsx
================================================
/* eslint-disable react/jsx-handler-names */
import { useCallback, useRef } from 'react';
import type { SVGProps, ReactNode, MouseEvent, TouchEvent } from 'react';
import type { UseDrag, HandlerArgs as DragHandlerArgs } from '@visx/drag';
import { useDrag } from '@visx/drag';
import type { AnnotationContextType } from '../types';
import Annotation from './Annotation';
export type EditableAnnotationProps = Pick & {
/** Width of the possible drag canvas (e.g., SVG container). */
width: number;
/** Height of the possible drag canvas (e.g., SVG container). */
height: number;
/** Annotation children (Subject, Label, Connector) */
children: ReactNode;
/** Whether the Label position (dx, dy) is editable. */
canEditLabel?: boolean;
/** Whether the Subject position (x, y) is editable. */
canEditSubject?: boolean;
/** Optional circle props to set on the subject drag handle. */
subjectDragHandleProps?: SVGProps;
/** Optional circle props to set on the label drag handle. */
labelDragHandleProps?: SVGProps;
/** Callback invoked on drag start. */
onDragStart?: ({ x, y, dx, dy, event }: HandlerArgs) => void;
/** Callback invoked on drag move. */
onDragMove?: ({ x, y, dx, dy, event }: HandlerArgs) => void;
/** Callback invoked on drag end. */
onDragEnd?: ({ x, y, dx, dy, event }: HandlerArgs) => void;
};
export type HandlerArgs = {
x: number;
y: number;
dx: number;
dy: number;
event: MouseEvent | TouchEvent;
};
const defaultDragHandleProps = {
r: 10,
fill: 'transparent',
stroke: '#777',
strokeDasharray: '4,2',
strokeWidth: 2,
};
export default function EditableAnnotation({
canEditLabel = true,
canEditSubject = true,
children,
dx: labelDx = 0,
dy: labelDy = 0,
height,
labelDragHandleProps,
onDragEnd,
onDragMove,
onDragStart,
subjectDragHandleProps,
width,
x: subjectX = 0,
y: subjectY = 0,
}: EditableAnnotationProps) {
// chicken before the egg, we need these to reference drag state
// in drag callbacks which are defined before useDrag() state is available
const subjectDragRef = useRef(undefined);
const labelDragRef = useRef(undefined);
const handleDragStart = useCallback(
({ event }: DragHandlerArgs) => {
if (onDragStart) {
onDragStart({
event,
x: subjectX + (subjectDragRef.current?.dx ?? 0),
y: subjectY + (subjectDragRef.current?.dy ?? 0),
dx: labelDx + (labelDragRef.current?.dx ?? 0),
dy: labelDy + (labelDragRef.current?.dy ?? 0),
});
}
},
[labelDx, labelDy, onDragStart, subjectX, subjectY],
);
const handleDragMove = useCallback(
({ event }: DragHandlerArgs) => {
if (onDragMove) {
onDragMove({
event,
x: subjectX + (subjectDragRef.current?.dx ?? 0),
y: subjectY + (subjectDragRef.current?.dy ?? 0),
dx: labelDx + (labelDragRef.current?.dx ?? 0),
dy: labelDy + (labelDragRef.current?.dy ?? 0),
});
}
},
[labelDx, labelDy, onDragMove, subjectX, subjectY],
);
const handleDragEnd = useCallback(
({ event }: DragHandlerArgs) => {
if (onDragEnd) {
onDragEnd({
event,
x: subjectX + (subjectDragRef.current?.dx ?? 0),
y: subjectY + (subjectDragRef.current?.dy ?? 0),
dx: labelDx + (labelDragRef.current?.dx ?? 0),
dy: labelDy + (labelDragRef.current?.dy ?? 0),
});
}
},
[labelDx, labelDy, onDragEnd, subjectX, subjectY],
);
const subjectDrag = useDrag({
onDragStart: handleDragStart,
onDragMove: handleDragMove,
onDragEnd: handleDragEnd,
x: subjectX,
y: subjectY,
});
const labelDrag = useDrag({
onDragStart: handleDragStart,
onDragMove: handleDragMove,
onDragEnd: handleDragEnd,
x: labelDx,
y: labelDy,
});
// enable referencing these in the callbacks defined before useDrag is called
subjectDragRef.current = subjectDrag;
labelDragRef.current = labelDrag;
return (
<>
{children}
{subjectDrag.isDragging && (
)}
{canEditSubject && (
)}
{labelDrag.isDragging && (
)}
{canEditLabel && (
)}
>
);
}
================================================
FILE: packages/visx-annotation/src/components/HtmlLabel.tsx
================================================
import { useContext, useMemo } from 'react';
import type { ReactNode, CSSProperties } from 'react';
import cx from 'classnames';
import useMeasure from 'react-use-measure';
import { Group } from '@visx/group';
import AnnotationContext from '../context/AnnotationContext';
import AnchorLine from './LabelAnchorLine';
import type { LabelProps } from './Label';
const wrapperStyle = { display: 'inline-block' };
export type HtmlLabelProps = Pick<
LabelProps,
| 'anchorLineStroke'
| 'className'
| 'horizontalAnchor'
| 'resizeObserverPolyfill'
| 'showAnchorLine'
| 'verticalAnchor'
| 'x'
| 'y'
> & {
/** Pass in a custom element as the label to style as you like. Renders inside a , be aware that most non-browser SVG renderers will not render HTML s. See: https://github.com/airbnb/visx/issues/1173#issuecomment-1014380545. */
children?: ReactNode;
/** Optional styles to apply to the HTML container. */
containerStyle?: CSSProperties;
};
export default function HtmlLabel({
anchorLineStroke = '#222',
children,
className,
containerStyle,
horizontalAnchor: propsHorizontalAnchor,
resizeObserverPolyfill,
showAnchorLine = true,
verticalAnchor: propsVerticalAnchor,
x: propsX,
y: propsY,
}: HtmlLabelProps) {
// we must measure the rendered title + subtitle to compute container height
const [labelRef, titleBounds] = useMeasure({
polyfill: resizeObserverPolyfill,
});
const { width, height } = titleBounds;
// if props are provided, they take precedence over context
const { x = 0, y = 0, dx = 0, dy = 0 } = useContext(AnnotationContext);
// offset container position based on horizontal + vertical anchor
const horizontalAnchor =
propsHorizontalAnchor || (Math.abs(dx) < Math.abs(dy) ? 'middle' : dx > 0 ? 'start' : 'end');
const verticalAnchor =
propsVerticalAnchor || (Math.abs(dx) > Math.abs(dy) ? 'middle' : dy > 0 ? 'start' : 'end');
const containerCoords = useMemo(() => {
let adjustedX: number = propsX == null ? x + dx : propsX;
let adjustedY: number = propsY == null ? y + dy : propsY;
if (horizontalAnchor === 'middle') adjustedX -= width / 2;
if (horizontalAnchor === 'end') adjustedX -= width;
if (verticalAnchor === 'middle') adjustedY -= height / 2;
if (verticalAnchor === 'end') adjustedY -= height;
return { x: adjustedX, y: adjustedY };
}, [propsX, x, dx, propsY, y, dy, horizontalAnchor, verticalAnchor, width, height]);
return (
{children}
{showAnchorLine && (
Math.abs(dy) ? 'vertical' : 'horizontal'}
anchorLineStroke={anchorLineStroke}
verticalAnchor={verticalAnchor}
horizontalAnchor={horizontalAnchor}
width={width}
height={height}
/>
)}
);
}
================================================
FILE: packages/visx-annotation/src/components/Label.tsx
================================================
import { useContext, useMemo } from 'react';
import type { SVGProps, CSSProperties } from 'react';
import cx from 'classnames';
import { Group } from '@visx/group';
import type { TextProps } from '@visx/text';
import { Text, useText } from '@visx/text';
import type { Options as UseMeasureOptions } from 'react-use-measure';
import useMeasure from 'react-use-measure';
import AnnotationContext from '../context/AnnotationContext';
import AnchorLine from './LabelAnchorLine';
export type LabelProps = {
/** Stroke color of anchor line. */
anchorLineStroke?: string;
/** Background color of label. */
backgroundFill?: string;
/** Padding of text from background. */
backgroundPadding?: number | { top?: number; right?: number; bottom?: number; left?: number };
/** Additional props to be passed to background SVGRectElement. */
backgroundProps?: SVGProps;
/** Optional className to apply to container in addition to 'visx-annotation-label'. */
className?: string;
/** Color of title and subtitle text. */
fontColor?: string;
/** Whether the label is horizontally anchored to the start, middle, or end of its x position. */
horizontalAnchor?: TextProps['textAnchor'];
/** Optionally inject a ResizeObserver polyfill, else this *must* be globally available. */
resizeObserverPolyfill?: UseMeasureOptions['polyfill'];
/** Whether to render a line indicating label text anchor. */
showAnchorLine?: boolean;
/** Whether to render a label background. */
showBackground?: boolean;
/** Optional subtitle. */
subtitle?: string;
/** Optional title font size. */
subtitleFontSize?: TextProps['fontSize'];
/** Optional title font weight. */
subtitleFontWeight?: TextProps['fontWeight'];
/** The vertical offset of the subtitle from the title. */
subtitleDy?: number;
/** Optional subtitle Text props (to override color, etc.). */
subtitleProps?: Partial;
/** Optional title. */
title?: string;
/** Optional title font size. */
titleFontSize?: TextProps['fontSize'];
/** Optional title font weight. */
titleFontWeight?: TextProps['fontWeight'];
/** Optional title Text props (to override color, etc.). */
titleProps?: Partial;
/** Whether the label is vertically anchored to the start, middle, or end of its y position. */
verticalAnchor?: TextProps['verticalAnchor'];
/** Width of annotation, including background, for text wrapping. */
width?: number;
/** Max width of annotation, including background, for text wrapping. */
maxWidth?: number;
/** Left offset of entire AnnotationLabel, if not specified uses x + dx from Annotation. */
x?: number;
/** Top offset of entire AnnotationLabel, if not specified uses y + dy from Annotation. */
y?: number;
};
const DEFAULT_PADDING = { top: 12, right: 12, bottom: 12, left: 12 };
function getCompletePadding(padding: LabelProps['backgroundPadding']) {
if (typeof padding === 'undefined') return DEFAULT_PADDING;
if (typeof padding === 'number') {
return { top: padding, right: padding, bottom: padding, left: padding };
}
return { ...DEFAULT_PADDING, ...padding };
}
export default function Label({
anchorLineStroke = '#222',
backgroundFill = '#eaeaea',
backgroundPadding,
backgroundProps,
className,
fontColor = '#222',
horizontalAnchor: propsHorizontalAnchor,
resizeObserverPolyfill,
showAnchorLine = true,
showBackground = true,
subtitle,
subtitleDy = 4,
subtitleFontSize = 12,
subtitleFontWeight = 200,
subtitleProps,
title,
titleFontSize = 16,
titleFontWeight = 600,
titleProps,
verticalAnchor: propsVerticalAnchor,
width: propWidth,
maxWidth = 125,
x: propsX,
y: propsY,
}: LabelProps) {
// we must measure the rendered html content to compute container height
const [titleRef, titleBounds] = useMeasure({
polyfill: resizeObserverPolyfill,
});
const [subtitleRef, subtitleBounds] = useMeasure({
polyfill: resizeObserverPolyfill,
});
const padding = useMemo(() => getCompletePadding(backgroundPadding), [backgroundPadding]);
// if props are provided, they take precedence over context
const { x = 0, y = 0, dx = 0, dy = 0 } = useContext(AnnotationContext);
const height = Math.floor(
padding.top + padding.bottom + (titleBounds.height ?? 0) + (subtitleBounds.height ?? 0),
);
const { wordsByLines: titleWordsByLine } = useText({
children: title,
verticalAnchor: 'start',
capHeight: titleFontSize,
fontSize: titleFontSize,
fontWeight: titleFontWeight,
fontFamily: titleProps?.fontFamily,
width: maxWidth,
...titleProps,
});
const { wordsByLines: subtitleWordsByLine } = useText({
children: subtitle,
verticalAnchor: 'start',
capHeight: subtitleFontSize,
fontSize: subtitleFontSize,
fontWeight: subtitleFontWeight,
fontFamily: subtitleProps?.fontFamily,
width: maxWidth,
...subtitleProps,
});
const titleMeasuredWidth = titleWordsByLine.reduce(
(maxTitleWidth, line) => Math.max(maxTitleWidth, line.width ?? 0),
0,
);
const subtitleMeasuredWidth = subtitleWordsByLine.reduce(
(maxSubtitleWidth, line) => Math.max(maxSubtitleWidth, line.width ?? 0),
0,
);
const textMeasuredWidth = Math.ceil(
Math.min(maxWidth, Math.max(titleMeasuredWidth, subtitleMeasuredWidth)),
);
const measuredWidth = padding.right + padding.left + textMeasuredWidth;
const width = propWidth ?? measuredWidth;
const innerWidth = width - padding.left - padding.right;
// offset container position based on horizontal + vertical anchor
const horizontalAnchor =
propsHorizontalAnchor || (Math.abs(dx) < Math.abs(dy) ? 'middle' : dx > 0 ? 'start' : 'end');
const verticalAnchor =
propsVerticalAnchor || (Math.abs(dx) > Math.abs(dy) ? 'middle' : dy > 0 ? 'start' : 'end');
const containerCoords = useMemo(() => {
let adjustedX: number = propsX == null ? x + dx : propsX;
let adjustedY: number = propsY == null ? y + dy : propsY;
if (horizontalAnchor === 'middle') adjustedX -= width / 2;
if (horizontalAnchor === 'end') adjustedX -= width;
if (verticalAnchor === 'middle') adjustedY -= height / 2;
if (verticalAnchor === 'end') adjustedY -= height;
return { x: adjustedX, y: adjustedY };
}, [propsX, x, dx, propsY, y, dy, horizontalAnchor, verticalAnchor, width, height]);
const titleFontFamily = titleProps?.fontFamily;
const titleStyle = useMemo(
() => ({
fontSize: titleFontSize,
fontWeight: titleFontWeight,
fontFamily: titleFontFamily,
}),
[titleFontSize, titleFontWeight, titleFontFamily],
) as CSSProperties;
const subtitleFontFamily = subtitleProps?.fontFamily;
const subtitleStyle = useMemo(
() => ({
fontSize: subtitleFontSize,
fontWeight: subtitleFontWeight,
fontFamily: subtitleFontFamily,
}),
[subtitleFontSize, subtitleFontWeight, subtitleFontFamily],
) as CSSProperties;
return !title && !subtitle ? null : (
{showBackground && (
)}
{showAnchorLine && (
Math.abs(dy) ? 'vertical' : 'horizontal'}
anchorLineStroke={anchorLineStroke}
verticalAnchor={verticalAnchor}
horizontalAnchor={horizontalAnchor}
width={width}
height={height}
/>
)}
{title && (
{title}
)}
{subtitle && (
{subtitle}
)}
);
}
================================================
FILE: packages/visx-annotation/src/components/LabelAnchorLine.tsx
================================================
import type { TextProps } from '@visx/text';
interface AnchorLineProps {
anchorLineOrientation: 'horizontal' | 'vertical';
verticalAnchor: TextProps['verticalAnchor'];
horizontalAnchor: TextProps['textAnchor'];
anchorLineStroke: string;
width: number;
height: number;
}
export default function AnchorLine({
anchorLineOrientation,
anchorLineStroke,
verticalAnchor,
horizontalAnchor,
width,
height,
}: AnchorLineProps) {
const backgroundOutline = { stroke: anchorLineStroke, strokeWidth: 2 };
return (
<>
{anchorLineOrientation === 'horizontal' && verticalAnchor === 'start' && (
)}
{anchorLineOrientation === 'horizontal' && verticalAnchor === 'end' && (
)}
{anchorLineOrientation === 'vertical' && horizontalAnchor === 'start' && (
)}
{anchorLineOrientation === 'vertical' && horizontalAnchor === 'end' && (
)}
>
);
}
================================================
FILE: packages/visx-annotation/src/components/LineSubject.tsx
================================================
import { useContext } from 'react';
import type { SVGProps } from 'react';
import cx from 'classnames';
import AnnotationContext from '../context/AnnotationContext';
export type LineSubjectProps = {
/** Optional className to apply to LineSubject in addition to 'visx-annotation-subject'. */
className?: string;
/** Color of LineSubject. */
stroke?: string;
/** strokeWidth of LineSubject. */
strokeWidth?: number;
/** Orientation of line. */
orientation?: 'vertical' | 'horizontal';
/** x position of LineSubject (for vertical LineSubjects). */
x?: number;
/** y position of LineSubject (for horizontal LineSubjects). */
y?: number;
/** The minimum coordinate of the line. */
min: number;
/** The maximum coordinate of the line. */
max: number;
};
export default function LineSubject({
className,
x: propsX,
y: propsY,
orientation = 'vertical',
min,
max,
stroke = '#222',
...restProps
}: LineSubjectProps & Omit, keyof LineSubjectProps>) {
// if props are provided, they take precedence over context
const annotationContext = useContext(AnnotationContext);
const lineIsVertical = orientation === 'vertical';
return (
);
}
================================================
FILE: packages/visx-annotation/src/context/AnnotationContext.tsx
================================================
import { createContext } from 'react';
import type { AnnotationContextType } from '../types';
const AnnotationContext = createContext({});
export default AnnotationContext;
================================================
FILE: packages/visx-annotation/src/index.ts
================================================
// @visx/annotation
export { default as Connector } from './components/Connector';
export { default as Label } from './components/Label';
export { default as HtmlLabel } from './components/HtmlLabel';
export { default as CircleSubject } from './components/CircleSubject';
export { default as LineSubject } from './components/LineSubject';
export { default as Annotation } from './components/Annotation';
export { default as EditableAnnotation } from './components/EditableAnnotation';
export { default as AnnotationContext } from './context/AnnotationContext';
export type * from './types';
export type { AnnotationProps } from './components/Annotation';
export type { CircleSubjectProps } from './components/CircleSubject';
export type { ConnectorProps } from './components/Connector';
export type { EditableAnnotationProps } from './components/EditableAnnotation';
export type { HtmlLabelProps } from './components/HtmlLabel';
export type { LabelProps } from './components/Label';
export type { LineSubjectProps } from './components/LineSubject';
================================================
FILE: packages/visx-annotation/src/types/index.ts
================================================
export type AnnotationContextType = {
/** x position of the Subject. */
x?: number;
/** y position of the Subject. */
y?: number;
/** x delta of the Label from the Subject. */
dx?: number;
/** y delta of the Label from the Subject. */
dy?: number;
};
================================================
FILE: packages/visx-annotation/test/Annotation.test.tsx
================================================
import { render } from '@testing-library/react';
import React, { useContext } from 'react';
import { Annotation } from '../src';
import AnnotationContext from '../src/context/AnnotationContext';
describe(' ', () => {
it('should be defined', () => {
expect(Annotation).toBeDefined();
});
it('should provide AnnotationContext', () => {
expect.assertions(1);
const annotation = { x: -50, y: 100, dx: 1000, dy: 7 };
function AnnotationChild() {
const annotationContext = useContext(AnnotationContext);
expect(annotationContext).toEqual(annotation);
return null;
}
render(
,
);
});
});
================================================
FILE: packages/visx-annotation/test/CircleSubject.test.tsx
================================================
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { CircleSubject } from '../src';
describe(' ', () => {
it('should be defined', () => {
expect(CircleSubject).toBeDefined();
});
it('should render a circle', () => {
const { container } = render(
,
);
const circle = container.querySelector('circle');
expect(circle).toBeInTheDocument();
expect(circle).toHaveAttribute('cx', '10');
expect(circle).toHaveAttribute('cy', '10');
});
});
================================================
FILE: packages/visx-annotation/test/Connector.test.tsx
================================================
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { Connector } from '../src';
describe(' ', () => {
it('should be defined', () => {
expect(Connector).toBeDefined();
});
it('should render a path', () => {
const { container } = render(
,
);
expect(container.querySelector('path')).toBeInTheDocument();
});
});
================================================
FILE: packages/visx-annotation/test/EditableAnnotation.test.tsx
================================================
import { vi } from 'vitest';
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import EditableAnnotation from '../src/components/EditableAnnotation';
import { addMock, removeMock } from './svgMock';
describe(' ', () => {
type EditableAnnotationProps = React.ComponentProps;
const defaultProps: EditableAnnotationProps = {
width: 100,
height: 100,
x: 0,
y: 0,
dx: 0,
dy: 0,
children: Child content,
};
function renderComponent(props?: Partial) {
return render(
,
);
}
beforeEach(() => {
vi.clearAllMocks();
addMock();
});
afterEach(removeMock);
it('should be defined', () => {
expect(() => renderComponent()).not.toThrow();
});
it('should render two resize handles by default', () => {
const { container } = renderComponent();
const circles = container.querySelectorAll('circle');
expect(circles).toHaveLength(2);
});
it('should render one resize handle if canEditLabel is false', () => {
const { container } = renderComponent({ canEditLabel: false });
const circles = container.querySelectorAll('circle');
expect(circles).toHaveLength(1);
});
it('should render one resize handle if canEditSubject is false', () => {
const { container } = renderComponent({ canEditSubject: false });
const circles = container.querySelectorAll('circle');
expect(circles).toHaveLength(1);
});
it('should render children content', () => {
const { getByTestId } = renderComponent();
expect(getByTestId('child-content')).toBeInTheDocument();
});
it('should render with correct initial positions', () => {
const { container } = renderComponent({
x: 10,
y: 20,
dx: 30,
dy: 40,
});
const circles = container.querySelectorAll('circle');
const [subjectHandle, labelHandle] = Array.from(circles);
expect(subjectHandle).toHaveAttribute('cx', '10');
expect(subjectHandle).toHaveAttribute('cy', '20');
expect(labelHandle).toHaveAttribute('cx', '40'); // x + dx
expect(labelHandle).toHaveAttribute('cy', '60'); // y + dy
});
});
================================================
FILE: packages/visx-annotation/test/HtmlLabel.test.tsx
================================================
import React from 'react';
import { ResizeObserver } from '@juggle/resize-observer';
import { render } from '@testing-library/react';
import { HtmlLabel } from '../src';
describe(' ', () => {
it('should render HTML content', () => {
const { container } = render(
,
);
const h1Element = container.querySelector('h1');
expect(h1Element).not.toBeNull();
});
});
================================================
FILE: packages/visx-annotation/test/Label.test.tsx
================================================
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ResizeObserver } from '@juggle/resize-observer';
import { Label } from '../src';
import { addMock, removeMock } from './svgMock';
describe('', () => {
const renderLabel = (props: React.ComponentProps) =>
render(
,
);
beforeEach(addMock);
afterEach(removeMock);
it('should be defined', () => {
expect(Label).toBeDefined();
});
it('should render title text', async () => {
const { findByText } = renderLabel({
title: 'title',
resizeObserverPolyfill: ResizeObserver,
});
expect(await findByText('title')).toBeInTheDocument();
});
it('should render subtitle text', async () => {
const { findByText } = renderLabel({
title: 'title',
subtitle: 'subtitle',
resizeObserverPolyfill: ResizeObserver,
});
expect(await findByText('subtitle')).toBeInTheDocument();
});
it('should render background', () => {
const { container } = renderLabel({
title: 'title',
showBackground: true,
resizeObserverPolyfill: ResizeObserver,
});
const rect = container.querySelector('rect');
expect(rect).toBeInTheDocument();
});
it('should render anchor line', () => {
const { container } = renderLabel({
title: 'title',
showAnchorLine: true,
resizeObserverPolyfill: ResizeObserver,
});
const line = container.querySelector('line');
expect(line).toBeInTheDocument();
});
});
================================================
FILE: packages/visx-annotation/test/LineSubject.test.tsx
================================================
import React from 'react';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { LineSubject } from '../src';
describe(' ', () => {
it('should be defined', () => {
expect(LineSubject).toBeDefined();
});
it('should render a line', () => {
const { container } = render(
,
);
const lineElement = container.querySelector('line');
expect(lineElement).toBeInTheDocument();
});
});
================================================
FILE: packages/visx-annotation/test/svgMock.ts
================================================
// @ts-expect-error
let originalFn: typeof SVGElement.prototype.getComputedTextLength;
/**
* JSDom does not implement getComputedTextLength()
* so this function add mock implementation for testing.
*/
export function addMock() {
// @ts-expect-error
originalFn = SVGElement.prototype.getComputedTextLength;
// @ts-expect-error
SVGElement.prototype.getComputedTextLength = function getComputedTextLength() {
// Make every character 10px wide
return (this.textContent?.length ?? 0) * 10;
};
}
/**
* Remove mock from addMock()
*/
export function removeMock() {
// @ts-expect-error
SVGElement.prototype.getComputedTextLength = originalFn;
}
================================================
FILE: packages/visx-annotation/test/tsconfig.json
================================================
{
"compilerOptions": {
"composite": false,
"emitDeclarationOnly": false,
"noEmit": true,
"rootDir": ".",
"types": ["vitest/globals"]
},
"extends": "../../../tsconfig.options.json",
"include": ["**/*", "../types/**/*", "../../../types/**/*"],
"references": [
{
"path": ".."
}
]
}
================================================
FILE: packages/visx-annotation/tsconfig.json
================================================
{
"compilerOptions": {
"declarationDir": "lib",
"outDir": "lib",
"rootDir": "src"
},
"exclude": [
"lib",
"test"
],
"extends": "../../tsconfig.options.json",
"include": [
"src/**/*",
"types/**/*",
"../../types/**/*"
],
"references": [
{
"path": "../visx-drag"
},
{
"path": "../visx-group"
},
{
"path": "../visx-text"
}
]
}
================================================
FILE: packages/visx-annotation/vitest.config.ts
================================================
import { defineConfig } from 'vitest/config';
import path from 'path';
export default defineConfig({
test: {
name: '@visx/annotation',
globals: true,
environment: 'jsdom',
setupFiles: [],
coverage: {
provider: 'v8',
reporter: ['lcov', 'json-summary', 'html', 'json', 'text'],
include: ['packages/visx-annotation/src/**/*.{ts,tsx}'],
exclude: ['**/node_modules/**', '**/esm/**', '**/lib/**', '**/test/**', '**/dist/**'],
reportsDirectory: './coverage',
},
},
resolve: {
alias: {
'@visx/annotation': path.resolve(__dirname, './src'),
},
},
});
================================================
FILE: packages/visx-axis/.npmrc
================================================
package-lock=false
================================================
FILE: packages/visx-axis/Readme.md
================================================
# @visx/axis
An axis component consists of a line with ticks, tick labels, and an axis label that helps viewers
interpret your graph.
You can use one of the 4 pre-made axes, or you can create your own based on the ` ` element.
Note that the `@visx/react-spring` package exports an `AnimatedAxis` variant with animated ticks.
## Installation
```
npm install --save @visx/axis
```
================================================
FILE: packages/visx-axis/package.json
================================================
{
"name": "@visx/axis",
"version": "4.0.1-alpha.0",
"description": "visx axis",
"sideEffects": false,
"main": "lib/index.js",
"module": "esm/index.js",
"types": "lib/index.d.ts",
"exports": {
".": {
"types": "./lib/index.d.ts",
"import": "./esm/index.js",
"require": "./lib/index.js"
}
},
"files": [
"lib",
"esm"
],
"repository": {
"type": "git",
"url": "git+https://github.com/airbnb/visx.git"
},
"keywords": [
"visx",
"react",
"d3",
"visualizations",
"charts"
],
"author": "@hshoff",
"license": "MIT",
"bugs": {
"url": "https://github.com/airbnb/visx/issues"
},
"homepage": "https://github.com/airbnb/visx#readme",
"dependencies": {
"@types/react": "*",
"@visx/group": "workspace:*",
"@visx/point": "workspace:*",
"@visx/scale": "workspace:*",
"@visx/shape": "workspace:*",
"@visx/text": "workspace:*",
"classnames": "^2.3.1"
},
"peerDependencies": {
"react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0"
},
"publishConfig": {
"access": "public"
}
}
================================================
FILE: packages/visx-axis/src/axis/Axis.tsx
================================================
import cx from 'classnames';
import { Group } from '@visx/group';
import { getTicks, coerceNumber } from '@visx/scale';
import type { SharedAxisProps, AxisScale } from '../types';
import AxisRenderer from './AxisRenderer';
import getTickPosition from '../utils/getTickPosition';
import getTickFormatter from '../utils/getTickFormatter';
import createPoint from '../utils/createPoint';
import type { OrientationType } from '../constants/orientation';
import Orientation from '../constants/orientation';
import getAxisRangePaddingConfig from '../utils/getAxisRangePaddingConfig';
export type AxisProps = SharedAxisProps & {
orientation?: OrientationType;
};
export default function Axis({
children = AxisRenderer,
axisClassName,
hideAxisLine = false,
hideTicks = false,
hideZero = false,
innerRef,
left = 0,
numTicks = 10,
orientation = Orientation.bottom,
rangePadding = 0,
scale,
tickFormat,
tickLength = 8,
tickValues,
top = 0,
...restProps
}: AxisProps) {
const format = tickFormat ?? getTickFormatter(scale);
const isLeft = orientation === Orientation.left;
const isTop = orientation === Orientation.top;
const horizontal = isTop || orientation === Orientation.bottom;
const tickPosition = getTickPosition(scale);
const tickSign = isLeft || isTop ? -1 : 1;
const range = scale.range();
const rangePaddingConfig = getAxisRangePaddingConfig(rangePadding);
const axisFromPoint = createPoint(
{ x: Number(range[0]) + 0.5 - rangePaddingConfig.start, y: 0 },
horizontal,
);
const axisToPoint = createPoint(
{ x: Number(range[range.length - 1]) + 0.5 + rangePaddingConfig.end, y: 0 },
horizontal,
);
const filteredTickValues = (tickValues ?? getTicks(scale, numTicks))
.filter((value) => !hideZero || (value !== 0 && value !== '0'))
.map((value, index) => ({ value, index }));
const ticks = filteredTickValues.map(({ value, index }) => {
const scaledValue = coerceNumber(tickPosition(value));
return {
value,
index,
from: createPoint({ x: scaledValue, y: 0 }, horizontal),
to: createPoint({ x: scaledValue, y: tickLength * tickSign }, horizontal),
formattedValue: format(value, index, filteredTickValues),
};
});
return (
{children({
...restProps,
axisFromPoint,
axisToPoint,
hideAxisLine,
hideTicks,
hideZero,
horizontal,
numTicks,
orientation,
rangePadding,
scale,
tickFormat: format,
tickLength,
tickPosition,
tickSign,
ticks,
})}
);
}
================================================
FILE: packages/visx-axis/src/axis/AxisBottom.tsx
================================================
import cx from 'classnames';
import Axis from './Axis';
import Orientation from '../constants/orientation';
import type { SharedAxisProps, AxisScale } from '../types';
export const bottomTickLabelProps = {
dy: '0.25em',
fill: '#222',
fontFamily: 'Arial',
fontSize: 10,
textAnchor: 'middle',
} as const;
export default function AxisBottom({
axisClassName,
labelOffset = 8,
tickLength = 8,
tickLabelProps,
...restProps
}: SharedAxisProps) {
const tickLabelPropsFinal =
typeof tickLabelProps === 'function'
? tickLabelProps
: {
...bottomTickLabelProps,
...tickLabelProps,
};
return (
);
}
================================================
FILE: packages/visx-axis/src/axis/AxisLeft.tsx
================================================
import cx from 'classnames';
import Axis from './Axis';
import Orientation from '../constants/orientation';
import type { SharedAxisProps, AxisScale } from '../types';
export const leftTickLabelProps = {
dx: '-0.25em',
dy: '0.25em',
fill: '#222',
fontFamily: 'Arial',
fontSize: 10,
textAnchor: 'end',
} as const;
export default function AxisLeft({
axisClassName,
labelOffset = 36,
tickLength = 8,
tickLabelProps,
...restProps
}: SharedAxisProps) {
const tickLabelPropsFinal =
typeof tickLabelProps === 'function'
? tickLabelProps
: {
...leftTickLabelProps,
...tickLabelProps,
};
return (
);
}
================================================
FILE: packages/visx-axis/src/axis/AxisRenderer.tsx
================================================
import cx from 'classnames';
import { Line } from '@visx/shape';
import { Text } from '@visx/text';
import type { TextProps } from '@visx/text';
import getLabelTransform from '../utils/getLabelTransform';
import Orientation from '../constants/orientation';
import type { AxisRendererProps, AxisScale } from '../types';
import Ticks from './Ticks';
const defaultTextProps: Partial = {
textAnchor: 'middle',
fontFamily: 'Arial',
fontSize: 10,
fill: '#222',
};
export default function AxisRenderer({
axisFromPoint,
axisLineClassName,
axisToPoint,
hideAxisLine,
hideTicks,
horizontal,
label = '',
labelClassName,
labelOffset = 14,
labelProps,
orientation = Orientation.bottom,
scale,
stroke = '#222',
strokeDasharray,
strokeWidth = 1,
tickClassName,
tickComponent,
tickLineProps,
tickLabelProps,
tickLength = 8,
tickStroke = '#222',
tickTransform,
ticks,
ticksComponent = Ticks,
}: AxisRendererProps) {
const combinedLabelProps = {
...defaultTextProps,
...labelProps,
};
const tickLabelPropsDefault = {
...defaultTextProps,
...(typeof tickLabelProps === 'object' ? tickLabelProps : null),
};
// compute the max tick label size to compute label offset
const allTickLabelProps = ticks.map(({ value, index }) =>
typeof tickLabelProps === 'function'
? tickLabelProps(value, index, ticks)
: tickLabelPropsDefault,
);
const maxTickLabelFontSize = Math.max(
10,
...allTickLabelProps.map((props) => (typeof props.fontSize === 'number' ? props.fontSize : 0)),
);
return (
<>
{ticksComponent({
hideTicks,
horizontal,
orientation,
scale,
tickClassName,
tickComponent,
tickLabelProps: allTickLabelProps,
tickStroke,
tickTransform,
ticks,
strokeWidth,
tickLineProps,
})}
{!hideAxisLine && (
)}
{label && (
{label}
)}
>
);
}
================================================
FILE: packages/visx-axis/src/axis/AxisRight.tsx
================================================
import cx from 'classnames';
import Axis from './Axis';
import Orientation from '../constants/orientation';
import type { SharedAxisProps, AxisScale } from '../types';
export type AxisRightProps = SharedAxisProps;
export const rightTickLabelProps = {
dx: '0.25em',
dy: '0.25em',
fill: '#222',
fontFamily: 'Arial',
fontSize: 10,
textAnchor: 'start',
} as const;
export default function AxisRight({
axisClassName,
labelOffset = 36,
tickLength = 8,
tickLabelProps,
...restProps
}: AxisRightProps) {
const tickLabelPropsFinal =
typeof tickLabelProps === 'function'
? tickLabelProps
: {
...rightTickLabelProps,
...tickLabelProps,
};
return (
);
}
================================================
FILE: packages/visx-axis/src/axis/AxisTop.tsx
================================================
import cx from 'classnames';
import Axis from './Axis';
import Orientation from '../constants/orientation';
import type { SharedAxisProps, AxisScale } from '../types';
export type AxisTopProps = SharedAxisProps;
export const topTickLabelProps = {
dy: '-0.75em',
fill: '#222',
fontFamily: 'Arial',
fontSize: 10,
textAnchor: 'middle',
} as const;
export default function AxisTop({
axisClassName,
labelOffset = 8,
tickLength = 8,
tickLabelProps,
...restProps
}: AxisTopProps) {
const tickLabelPropsFinal =
typeof tickLabelProps === 'function'
? tickLabelProps
: {
...topTickLabelProps,
...tickLabelProps,
};
return (
);
}
================================================
FILE: packages/visx-axis/src/axis/Ticks.tsx
================================================
import cx from 'classnames';
import { Line } from '@visx/shape';
import { Group } from '@visx/group';
import { Text } from '@visx/text';
import Orientation from '../constants/orientation';
import type { TicksRendererProps, AxisScale } from '../types';
export default function Ticks({
hideTicks,
horizontal,
orientation,
tickClassName,
tickComponent,
tickLabelProps: allTickLabelProps,
tickStroke = '#222',
tickTransform,
ticks,
strokeWidth,
tickLineProps,
}: TicksRendererProps) {
return ticks.map(({ value, index, from, to, formattedValue }) => {
const tickLabelProps = allTickLabelProps[index] ?? {};
const tickLabelFontSize = Math.max(
10,
(typeof tickLabelProps.fontSize === 'number' && tickLabelProps.fontSize) || 0,
);
const tickYCoord =
to.y + (horizontal && orientation !== Orientation.top ? tickLabelFontSize : 0);
return (
{!hideTicks && (
)}
{tickComponent ? (
tickComponent({
...tickLabelProps,
x: to.x,
y: tickYCoord,
formattedValue,
})
) : (
{formattedValue}
)}
);
});
}
================================================
FILE: packages/visx-axis/src/constants/orientation.ts
================================================
import type { ValueOf } from '@visx/scale';
const Orientation = {
top: 'top',
left: 'left',
right: 'right',
bottom: 'bottom',
} as const;
export type OrientationType = ValueOf;
export default Orientation;
================================================
FILE: packages/visx-axis/src/index.ts
================================================
// @visx/axis
export { default as Axis } from './axis/Axis';
export type { AxisProps } from './axis/Axis';
export { default as AxisLeft } from './axis/AxisLeft';
export { default as AxisRight } from './axis/AxisRight';
export { default as AxisTop } from './axis/AxisTop';
export { default as AxisBottom } from './axis/AxisBottom';
export { default as Orientation } from './constants/orientation';
export type * from './types';
================================================
FILE: packages/visx-axis/src/types.ts
================================================
import type { D3Scale, NumberLike, ScaleInput, ValueOf } from '@visx/scale';
import type { TextProps } from '@visx/text';
import type { ReactNode, Ref, SVGProps } from 'react';
import type Orientation from './constants/orientation';
// In order to plot values on an axis, output of the scale must be number.
// Some scales return undefined.
export type AxisScaleOutput = number | NumberLike | undefined;
/** A catch-all type for scales that are compatible with axis */
export type AxisScale