Repository: uber/react-vis Branch: master Commit: 92e2af2eac82 Files: 393 Total size: 1.3 MB Directory structure: gitextract_5pxsgs97/ ├── .editorconfig ├── .eslintrc ├── .gitattributes ├── .gitignore ├── .prettierrc ├── .stylelintrc ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── DEPRECATED.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── babel.config.json ├── docs/ │ ├── animation.md │ ├── arc-series.md │ ├── area-series.md │ ├── axes.md │ ├── bar-series.md │ ├── borders.md │ ├── chart-label.md │ ├── clip.md │ ├── colors.md │ ├── contour-series.md │ ├── crosshair.md │ ├── custom-svg-series.md │ ├── decorative-axis.md │ ├── examples/ │ │ ├── building-things-other-than-charts.md │ │ ├── extensibility.md │ │ ├── iris-dashboard.md │ │ ├── responsive-vis.md │ │ ├── showcases/ │ │ │ ├── axes-showcase.md │ │ │ ├── legends-showcase.md │ │ │ ├── misc-showcase.md │ │ │ ├── plots-showcase.md │ │ │ ├── radar-chart-showcase.md │ │ │ ├── radial-showcase.md │ │ │ ├── sankeys-showcase.md │ │ │ ├── sunburst-showcase.md │ │ │ └── treemaps-showcase.md │ │ └── stream-graph.md │ ├── flexible-plots.md │ ├── getting-started/ │ │ ├── getting-started.md │ │ ├── installing-react-vis.md │ │ ├── new-react-vis-project.md │ │ ├── react-vis-in-codepen.md │ │ └── your-first-chart.md │ ├── gradients.md │ ├── grids.md │ ├── heatmap-series.md │ ├── hexbin-series.md │ ├── highlight.md │ ├── hint.md │ ├── interaction.md │ ├── label-series.md │ ├── legends.md │ ├── line-mark-series.md │ ├── line-series.md │ ├── mark-series.md │ ├── parallel-coordinates.md │ ├── polygon-series.md │ ├── presentation.md │ ├── radar-chart.md │ ├── radial-chart.md │ ├── rect-series.md │ ├── sankey.md │ ├── scales-and-data.md │ ├── series.md │ ├── style.md │ ├── sunburst.md │ ├── treemap.md │ ├── voronoi.md │ ├── whisker-series.md │ └── xy-plot.md ├── package.json ├── packages/ │ ├── react-vis/ │ │ ├── .babelrc.json │ │ ├── build-browser.sh │ │ ├── jest.config.js │ │ ├── jest.setup.js │ │ ├── jestBabelTransform.js │ │ ├── package.json │ │ ├── src/ │ │ │ ├── animation.js │ │ │ ├── index.js │ │ │ ├── legends/ │ │ │ │ ├── continuous-color-legend.js │ │ │ │ ├── continuous-size-legend.js │ │ │ │ ├── discrete-color-legend-item.js │ │ │ │ ├── discrete-color-legend.js │ │ │ │ └── searchable-discrete-color-legend.js │ │ │ ├── main.scss │ │ │ ├── make-vis-flexible.js │ │ │ ├── parallel-coordinates/ │ │ │ │ └── index.js │ │ │ ├── plot/ │ │ │ │ ├── axis/ │ │ │ │ │ ├── axis-line.js │ │ │ │ │ ├── axis-ticks.js │ │ │ │ │ ├── axis-title.js │ │ │ │ │ ├── axis.js │ │ │ │ │ ├── decorative-axis-ticks.js │ │ │ │ │ ├── decorative-axis.js │ │ │ │ │ ├── x-axis.js │ │ │ │ │ └── y-axis.js │ │ │ │ ├── borders.js │ │ │ │ ├── chart-label.js │ │ │ │ ├── circular-grid-lines.js │ │ │ │ ├── content-clip-path.js │ │ │ │ ├── crosshair.js │ │ │ │ ├── gradient-defs.js │ │ │ │ ├── grid-lines.js │ │ │ │ ├── highlight.js │ │ │ │ ├── hint.js │ │ │ │ ├── horizontal-grid-lines.js │ │ │ │ ├── series/ │ │ │ │ │ ├── abstract-series.js │ │ │ │ │ ├── arc-series.js │ │ │ │ │ ├── area-series.js │ │ │ │ │ ├── bar-series-canvas.js │ │ │ │ │ ├── bar-series.js │ │ │ │ │ ├── canvas-wrapper.js │ │ │ │ │ ├── contour-series.js │ │ │ │ │ ├── custom-svg-series.js │ │ │ │ │ ├── heatmap-series.js │ │ │ │ │ ├── hexbin-series.js │ │ │ │ │ ├── horizontal-bar-series-canvas.js │ │ │ │ │ ├── horizontal-bar-series.js │ │ │ │ │ ├── horizontal-rect-series-canvas.js │ │ │ │ │ ├── horizontal-rect-series.js │ │ │ │ │ ├── label-series.js │ │ │ │ │ ├── line-mark-series-canvas.js │ │ │ │ │ ├── line-mark-series.js │ │ │ │ │ ├── line-series-canvas.js │ │ │ │ │ ├── line-series.js │ │ │ │ │ ├── mark-series-canvas.js │ │ │ │ │ ├── mark-series.js │ │ │ │ │ ├── polygon-series.js │ │ │ │ │ ├── rect-series-canvas.js │ │ │ │ │ ├── rect-series.js │ │ │ │ │ ├── vertical-bar-series-canvas.js │ │ │ │ │ ├── vertical-bar-series.js │ │ │ │ │ ├── vertical-rect-series-canvas.js │ │ │ │ │ ├── vertical-rect-series.js │ │ │ │ │ └── whisker-series.js │ │ │ │ ├── vertical-grid-lines.js │ │ │ │ ├── voronoi.js │ │ │ │ └── xy-plot.js │ │ │ ├── radar-chart/ │ │ │ │ └── index.js │ │ │ ├── radial-chart/ │ │ │ │ └── index.js │ │ │ ├── sankey/ │ │ │ │ ├── index.js │ │ │ │ └── sankey-link.js │ │ │ ├── styles/ │ │ │ │ ├── examples.scss │ │ │ │ ├── legends.scss │ │ │ │ ├── plot.scss │ │ │ │ ├── radial-chart.scss │ │ │ │ └── treemap.scss │ │ │ ├── sunburst/ │ │ │ │ └── index.js │ │ │ ├── theme.js │ │ │ ├── treemap/ │ │ │ │ ├── index.js │ │ │ │ ├── treemap-dom.js │ │ │ │ ├── treemap-leaf.js │ │ │ │ └── treemap-svg.js │ │ │ └── utils/ │ │ │ ├── axis-utils.js │ │ │ ├── chart-utils.js │ │ │ ├── data-utils.js │ │ │ ├── react-utils.js │ │ │ ├── scales-utils.js │ │ │ ├── series-utils.js │ │ │ └── styling-utils.js │ │ └── tests/ │ │ ├── .eslintrc │ │ ├── components/ │ │ │ ├── animation.test.js │ │ │ ├── arc-series.test.js │ │ │ ├── area-series.test.js │ │ │ ├── axes.test.js │ │ │ ├── axis-tick-format.test.js │ │ │ ├── axis-title.test.js │ │ │ ├── bar-series.test.js │ │ │ ├── borders.test.js │ │ │ ├── canvas-component.test.js │ │ │ ├── circular-grid-lines.test.js │ │ │ ├── color-article.test.js │ │ │ ├── contour-series.test.js │ │ │ ├── crosshair.test.js │ │ │ ├── custom-svg-series.test.js │ │ │ ├── data-article.test.js │ │ │ ├── decorative-axis.test.js │ │ │ ├── gradient.test.js │ │ │ ├── grid-lines.test.js │ │ │ ├── heatmap.test.js │ │ │ ├── hexbin-series.test.js │ │ │ ├── highlight.test.js │ │ │ ├── hints.test.js │ │ │ ├── interaction-article.test.js │ │ │ ├── label-series.test.js │ │ │ ├── legends.test.js │ │ │ ├── line-series-canvas.test.js │ │ │ ├── line-series.test.js │ │ │ ├── make-vis-flexible.test.js │ │ │ ├── mark-series.test.js │ │ │ ├── parallel-coordinates.test.js │ │ │ ├── polygon-series.test.js │ │ │ ├── radar-chart.test.js │ │ │ ├── radial.test.js │ │ │ ├── rect-series.test.js │ │ │ ├── sankey.test.js │ │ │ ├── sunburst.test.js │ │ │ ├── treemap.test.js │ │ │ ├── voronoi.test.js │ │ │ ├── whisker-series.test.js │ │ │ └── xy-plot.test.js │ │ ├── components.js │ │ ├── index.js │ │ ├── jsconfig.json │ │ ├── plot/ │ │ │ ├── __snapshots__/ │ │ │ │ └── content-clip-path.test.js.snap │ │ │ └── content-clip-path.test.js │ │ ├── setup.js │ │ ├── test-utils.js │ │ └── utils/ │ │ ├── axis-utils.test.js │ │ ├── chart-utils.test.js │ │ ├── data-utils.test.js │ │ ├── react-utils.test.js │ │ ├── scales-utils.test.js │ │ ├── series-utils.test.js │ │ └── styling-utils.test.js │ ├── showcase/ │ │ ├── .babelrc.json │ │ ├── app.js │ │ ├── axes/ │ │ │ ├── axis-on-0.js │ │ │ ├── custom-axes-orientation.js │ │ │ ├── custom-axes.js │ │ │ ├── custom-axis-tick-element.js │ │ │ ├── custom-axis-tick-format.js │ │ │ ├── custom-axis.js │ │ │ ├── decorative-axes-criss-cross.js │ │ │ ├── dynamic-complex-edge-hints.js │ │ │ ├── dynamic-crosshair-scatterplot.js │ │ │ ├── dynamic-crosshair.js │ │ │ ├── dynamic-hints.js │ │ │ ├── dynamic-programmatic-rightedge-hints.js │ │ │ ├── dynamic-simple-edge-hints.js │ │ │ ├── dynamic-simple-topedge-hints.js │ │ │ ├── empty-chart.js │ │ │ ├── padded-axis.js │ │ │ ├── parallel-coordinates-example.js │ │ │ ├── static-crosshair.js │ │ │ └── static-hints.js │ │ ├── color/ │ │ │ ├── line-chart-many-colors.js │ │ │ └── mini-color-examples.js │ │ ├── data/ │ │ │ └── mini-data-examples.js │ │ ├── datasets/ │ │ │ ├── car-data.json │ │ │ ├── d3-flare-example.json │ │ │ ├── energy.json │ │ │ ├── iris.json │ │ │ └── old-faithful.json │ │ ├── examples/ │ │ │ ├── candlestick/ │ │ │ │ ├── candlestick-example.js │ │ │ │ ├── candlestick.js │ │ │ │ └── candlestick.scss │ │ │ ├── force-directed-graph/ │ │ │ │ ├── force-directed-example.js │ │ │ │ ├── force-directed-graph.js │ │ │ │ ├── force-directed.scss │ │ │ │ └── les-mis-data.json │ │ │ ├── iris-dashboard/ │ │ │ │ ├── iris-dashboard.js │ │ │ │ └── iris-dashboard.scss │ │ │ ├── responsive-vis/ │ │ │ │ ├── responsive-bar-chart.js │ │ │ │ ├── responsive-scatterplot.js │ │ │ │ ├── responsive-vis-example.js │ │ │ │ ├── responsive-vis-utils.js │ │ │ │ └── responsive-vis.scss │ │ │ └── streamgraph/ │ │ │ ├── streamgraph-example.js │ │ │ └── streamgraph-example.scss │ │ ├── flexible/ │ │ │ └── flexible-examples.js │ │ ├── index.html │ │ ├── index.js │ │ ├── interaction/ │ │ │ └── interaction-examples.js │ │ ├── legends/ │ │ │ ├── continuous-color.js │ │ │ ├── continuous-size.js │ │ │ ├── horizontal-discrete-color.js │ │ │ ├── horizontal-discrete-custom-palette.js │ │ │ ├── searchable-discrete-color-hover.js │ │ │ ├── searchable-discrete-color.js │ │ │ └── vertical-discrete-color.js │ │ ├── misc/ │ │ │ ├── 2d-dragable-plot.js │ │ │ ├── animation-example.js │ │ │ ├── clip-example.js │ │ │ ├── dragable-chart-example.js │ │ │ ├── gradient-example.js │ │ │ ├── label-series-example.js │ │ │ ├── null-data-example.js │ │ │ ├── selection-plot-example.js │ │ │ ├── synced-charts.js │ │ │ ├── time-chart.js │ │ │ ├── triangle-example.js │ │ │ ├── voronoi-line-chart.js │ │ │ └── zoomable-chart-example.js │ │ ├── package.json │ │ ├── parallel-coordinates/ │ │ │ ├── animated-parallel-coordinates.js │ │ │ ├── basic-parallel-coordinates.js │ │ │ └── brushed-parallel-coordinates.js │ │ ├── plot/ │ │ │ ├── area-chart-elevated.js │ │ │ ├── area-chart.js │ │ │ ├── axis-with-turned-labels.js │ │ │ ├── bar-chart.js │ │ │ ├── big-base-bar-chart.js │ │ │ ├── clustered-stacked-bar-chart.js │ │ │ ├── complex-chart.js │ │ │ ├── contour-series-example.js │ │ │ ├── custom-scales.js │ │ │ ├── custom-svg-all-the-marks.js │ │ │ ├── custom-svg-example.js │ │ │ ├── custom-svg-root-level.js │ │ │ ├── difference-chart.js │ │ │ ├── faux-radial-scatterplot.js │ │ │ ├── grid.js │ │ │ ├── heatmap-chart.js │ │ │ ├── hex-heatmap.js │ │ │ ├── hexbin-size-example.js │ │ │ ├── histogram.js │ │ │ ├── labeled-heatmap.js │ │ │ ├── labeled-stacked-vertical-bar-chart.js │ │ │ ├── line-chart-canvas.js │ │ │ ├── line-chart-with-style.js │ │ │ ├── line-chart.js │ │ │ ├── line-series-canvas-nearest-xy-example.js │ │ │ ├── linemark-chart.js │ │ │ ├── mixed-stacked-chart.js │ │ │ ├── scatterplot-canvas.js │ │ │ ├── scatterplot.js │ │ │ ├── stacked-histogram.js │ │ │ ├── stacked-horizontal-bar-chart.js │ │ │ ├── stacked-vertical-bar-chart.js │ │ │ ├── whisker-chart.js │ │ │ └── width-height-margin.js │ │ ├── radar-chart/ │ │ │ ├── animated-radar-chart.js │ │ │ ├── basic-radar-chart.js │ │ │ ├── four-quadrant-radar-chart.js │ │ │ ├── radar-chart-series-tooltips.js │ │ │ └── radar-chart-with-tooltips.js │ │ ├── radial-chart/ │ │ │ ├── arc-series-example.js │ │ │ ├── custom-radius-radial-chart.js │ │ │ ├── donut-chart.js │ │ │ ├── gradient-pie.js │ │ │ └── simple-radial-chart.js │ │ ├── sankey/ │ │ │ ├── basic.js │ │ │ ├── energy-sankey.js │ │ │ ├── link-event.js │ │ │ ├── link-hint.js │ │ │ └── voronoi.js │ │ ├── showcase-app.js │ │ ├── showcase-components/ │ │ │ ├── showcase-button.js │ │ │ ├── showcase-dropdown.js │ │ │ ├── showcase-utils.js │ │ │ └── source-linker.js │ │ ├── showcase-index.js │ │ ├── showcase-links.js │ │ ├── showcase-sections/ │ │ │ ├── axes-showcase.js │ │ │ ├── legends-showcase.js │ │ │ ├── misc-showcase.js │ │ │ ├── parallel-coordinates-showcase.js │ │ │ ├── plots-showcase.js │ │ │ ├── radar-showcase.js │ │ │ ├── radial-showcase.js │ │ │ ├── sankeys-showcase.js │ │ │ ├── sunburst-showcase.js │ │ │ └── treemap-showcase.js │ │ ├── showcase-utils.js │ │ ├── sunbursts/ │ │ │ ├── animated-sunburst.js │ │ │ ├── basic-sunburst.js │ │ │ ├── clock-example.js │ │ │ └── sunburst-with-tooltips.js │ │ ├── treemap/ │ │ │ ├── dynamic-treemap.js │ │ │ └── simple-treemap.js │ │ └── webpack.config.js │ └── website/ │ ├── .storybook/ │ │ ├── addons.js │ │ ├── config.js │ │ └── storybook.css │ ├── html.config.js │ ├── package.json │ ├── src/ │ │ ├── components/ │ │ │ └── Hero.js │ │ ├── config.js │ │ ├── demos.js │ │ ├── mdRoutes.js │ │ └── styles/ │ │ ├── _variables.scss │ │ └── index.scss │ ├── static/ │ │ └── .gitkeep │ ├── storybook/ │ │ ├── areaseries-story.js │ │ ├── axis-story.js │ │ ├── barseries-story.js │ │ ├── index.js │ │ ├── legend-story.js │ │ ├── lineseries-story.js │ │ ├── markseries-story.js │ │ ├── misc-story.js │ │ ├── radial-story.js │ │ ├── storybook-data.js │ │ └── storybook-utils.js │ └── webpack.config.js ├── publish-docs.sh ├── publish.sh └── remove-refs-to-unpm.pl ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig: http://editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ================================================ FILE: .eslintrc ================================================ { "extends": [ "eslint:recommended", "plugin:react/recommended", "plugin:react-hooks/recommended", "plugin:jest/recommended", "prettier", "prettier/react" ], "parser": "babel-eslint", "plugins": [ "react", "react-hooks", "prettier", "babel", "jest" ], "ignorePatterns": [ "node_modules", "bundle.js", "**/dist/", "**/es/" ], "settings":{ "react":{ "version": "detect" } }, "env": { "es6": true, "browser": true, "jest": true }, "rules": { "consistent-return": 0, "max-len": [1, 110, 4], "max-params": ["error", 6], "object-curly-spacing": 0, "babel/object-curly-spacing": 2, "jest/require-top-level-describe":"error", "react/prop-types": "off", "prettier/prettier": "warn" } } ================================================ FILE: .gitattributes ================================================ yarn.lock -diff ================================================ FILE: .gitignore ================================================ node_modules/ .sass-cache/ .nyc_output/ coverage/ dist/ es/ npm-debug.log* yarn-error.log* .DS_Store .idea/ public/ .vscode/ bundle.js bundle.css packages/showcase/app.css /index.html ================================================ FILE: .prettierrc ================================================ { "singleQuote": true, "trailingComma": "none", "bracketSpacing": false, "jsxBracketSameLine": false, "semi": true, "parser": "babel" } ================================================ FILE: .stylelintrc ================================================ { "extends": "stylelint-config-standard", "rules": { "selector-list-comma-newline-after": "always" } } ================================================ FILE: .travis.yml ================================================ language: node_js node_js: - '10' script: - npm run lint - cd packages/react-vis - npm run cover after_success: - npm install -g coveralls - cat coverage/lcov.info | coveralls ================================================ FILE: CHANGELOG.md ================================================ ## v1.12.0 - Upgraded all d3 dependencies - The layout of Sankey and Contour chart are changed due to the d3 upgrade - Build fixes - Revert #1358 (note that this PR was not included in 1.11.7) --- ## v1.9.2 - we removed the check-stylesheets warnings. - it's now possible to pass better styling options for radial charts labels. It's also possible to position axis titles along the axis. - react-motion and react minimal versions have been updated. ## v1.0.0 Breaking changes We recently made a major jump to version v1, which naturally includes some breaking changes. Specifically these include *Table is deprecated*: There are other substantially better tables in the ecosystem, so we decided to stick to what we do best, charts and plots. *Stylesheet has been moved*: the stylesheet for react-vis can now be found within the dist folder, so simply modify your style import to be: ``` @import './node_modules/react-vis/dist/main'; ``` *Default Opacity*: The default opacity behavior has been modified. Previously, react-vis asserted you had a linear scale with range [0.1, 1] and place your value within that range. Now react-vis presents a literal-scale by default. Check your opacities to make sure they are correct. *tickSizeInner & tickSizeOuter have been reversed*: the names of these props on the axes component have been switched. We feel this arrangement offers a more natural way to interact with the plot. *ALIGN.TOP_RIGHT was removed from hint.js*: this case did not match the orientation scheme followed by this component so was removed. ### v0.10.1 In this release we release a new chart type, a large repo refactor, address a variety of bugs, and a host of additional features! - **New Chart**: Type: Sankey Diagram: this chart type allows users visualize data flows and transfers. We are initially releasing this chart in alpha, so that we can gather feedback, and iterate to make the best chart that we can! Check out the docs here here! - **Bug Fix**: Fix numerous bugs on the radial plot, including mouse interaction issues, incorrect domains, and props falling out of sync - **Refactor**: We reorganized the way that we are keeping/organizing our repo, installed yarn, added webpack for the examples. (Pro tip, if you are having trouble running the examples after upgrading rm -rf your dist) - **Feature**: Allow custom crosshair orientation - **Feature**: Added interaction listeners for the tree map ### v0.9.0 This release addresses a couple of bugs and improves our dep tree. The only psuedo-breaking change is to the layout of radial plot. The way that it now works is that the pie is centered within the given width/height and then allowed to grow to an innerWidth/innerHeight that is computed from the margins and the width/height. - Bug: Modify margin system for radial chart - Improvement: Support for classname on legends - Chore: Remove duplicated styles - Bug: Add default props to classname for axis - Chore: Update deps, fix lint errors ### v0.8.0 This release adds two new props (and set of illustrative examples) to Hint component: ```align``` and ```getAlignStyle```. ```align``` (replacing ```orientation``` prop) is an object with two props — ```horizontal``` and ```vertical``` — and set of values that support existing and new hint placement: a) around a data point in one of four quadrants (imagine the point bisected by two axes — vertical, horizontal — creating 4 quadrants around a data point). b) **New**: pin to an edge of chart/plot area and position along that edge using data point's other dimension value. Developers wanting total control can use the ```getAlignStyle(align, x, y)``` function that returns an inline style object with one or more of the following props (```left, right, top, bottom```). The ```orientation``` prop is supported for backwards compatibility but will be deprecated in future release. See the following figure explaining the two props (```horizontal, vertical```) for the ```align``` prop object. ![react-vis-hint](https://cloud.githubusercontent.com/assets/2983206/21572148/f1529198-ce8a-11e6-8dc3-ef5f320ab9a1.png) ### v0.7.0 This release adds a new series: rectSeries. Rect series operates similarly to barSeries: they consist of a series of rectangles of various size that be stacked, but with one key difference. Where the bar series operates on the assumption of an ordinal axis, rect series operates on an assumption of a continuous one. This allows users to specify the positions of the edges of their rectangles! ![alt text](https://cloud.githubusercontent.com/assets/6854312/21075697/47f1bbfa-becd-11e6-9f67-9c1ab5ad5e83.png "example histogram") Rect series are great for histograms, as they allow you to exactly specify the bounds of buckets. They can be accessed via VerticalRectSeries and HorizontalRectSeries. Check out the examples for more details. This is non breaking change, and can be dropped in immediately. ### v0.6.8 - Feature: Export Abstract series and the rest of the functions in scale utils. - Feature: Add specific class names to x and y axes ### v0.6.6 - Improvement: added line smoothing via d3-shape curve functions ([#185](https://github.com/uber/react-vis/pull/185)). - Improvement: Expose GridLines, AxisLines, and ScaleUtils to export - Improvement: Add className prop to all series - Documentation: Expand tree map example - Documentation: Add elevated area chart example ### 0.6.4 - Bugfix: Fixed the issue with numeric titles in legends ([#154](https://github.com/uber/react-vis/pull/154)). ### 0.6.3 - Bugfix: fix the broken event listeners for radial charts ([#150](https://github.com/uber/react-vis/issues/150)); - Bugfix: compatibility fix: do not treat `null` and `undefined` in scale props as existing values ([#149](https://github.com/uber/react-vis/issues/149)). ### 0.6.2 * Feature: added a new `tickLabelAngle` property that rotates the tick label ([see the documentation](docs/xy-plot.md#ticklabelangle-optional) for details). * Improvement: added a little bit of examples for the axes. * Bugfix: fixed misplaced axis title when orientation is set to `'top'` or `'right'` ([#146](https://github.com/uber/react-vis/issues/146)). ### 0.6.1 * Bugfix: axis is misplaced when `orientation` is set to `'right'`([#143](https://github.com/uber/react-vis/issues/143)). ### 0.6 #### TL;DR New legends (sic!), new animations, faster rendering of components, no d3 in actual rendering process, new examples and more. #### Breaking changes * `animation` property works differently: duration is removed in favor of stiffness, damping and precision. Please refer to the documentation for the latest changes. * `undefined` and `null` values of important scale-related attributes for domains and ranges are now treated as real values (and not ignored anymore). #### Non-breaking changes * Feature: added the first version of legends (discrete and continuous color legends, continuous size legend). Please refer to the [docs for legends](docs/legends.md) for more details. * Improvement: got rid of assigning properties with d3 after rendering, currently all attributes and event listeners are attached using React (and it is faster). * Improvement: eliminated the use of `d3-selection` and `d3-transition` modules and made the source code smaller. * Improvement: added some structure into the examples and simplified their source code ([check them out](http://uber.github.com/react-vis)). * Bugfix: fixed crashing on animation (#114). * Improvement: `onNearestX` event now returns the index of the selected data point as an attribute (more details [here](docs/xy-plot.md#onnearestx-optional)) * Bugfix: added the donut chart to the list of examples ([#83](https://github.com/uber/react-vis/issues/83)). * Bugfix: fixed failing bar charts when the number of segments was changed ([#55](https://github.com/uber/react-vis/issues/55)). ### 0.5 #### TL;DR Upgraded to modular d3, compiled code became smaller, changed the API for axes and grids, fixed several bugs. #### Breaking changes * d3-axis is no longer used, the rendering of axes and grids is made by react (and works faster). * The API of axes (`XAxis` and `YAxis`) was significantly changed: * [the API of axes](docs/xy-plot.md#axes) is now (almost) compatible to the API of 'd3-axis'. * `labelFormat` and `labelValues` attributes for the axes are **removed**: similar results can be done achieved when `tickFormat` and `tickValues` attributes are used (see the [the updated documentation for axes](docs/xy-plot.md#axes) for more details). * `tickFormat` function is now gets only **one (value) argument instead of two (value and index)**. * The API of grids (`VerticalGridLines` and `HorizontalGridLines`) was significantly changed: it partially replicates the API of the axes. Please refer to [the updated documentation ](docs/xy-plot.md#grids) for more detail. #### Non-breaking changes * Bugfix: `margin` for the radial and ortogonal chart is now able to receive partial objects (e. g.`` instead of margins for each side) and numbers (e.g. ``) * Bugfix: `makeVisFlexible` doesn't fail anymore (see [#118](https://github.com/uber-common/react-vis/issues/118)). * Minor bugfixes and improvements. Please find [full change log here](https://github.com/uber/react-vis/releases). ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project email address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at oss-conduct@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]. [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: CONTRIBUTING.md ================================================ # Want to contribute? Great! That's why this is an open source project. We use this project in our infrastructure at Uber, and we hope that it's useful to others as well. Before you get started, here are some suggestions: - Check open issues for what you want. - If there is an open issue, comment on it. Otherwise open an issue describing your bug or feature with use cases. - Before undertaking a major change, please discuss this on the issue. We'd hate to see you spend a lot of time working on something that conflicts with other goals or requirements that might not be obvious. - Write code to fix the problem, then open a pull request with tests and documentation. - The pull requests gets reviewed and then merged assuming there are no problems. - A new release version gets cut. ## Hints Want to make sure your PR gets a speedy review and a quick merge? Here are some tips: - Add Tests - Add documentation about the feature you added - Make sure you have a clear description of what your PR is about - Include screenshots - Add Tests ## Releases Declaring formal releases requires peer review. - A reviewer of a pull request should recommend a new version number (patch, minor or major). - Once your change is merged feel free to bump the version as recommended by the reviewer. - A new version number should not be cut without peer review unless done by the project maintainer. ### Cutting a new version - Get your branch merged on master - Run `npm version major` or `npm version minor` or `npm version patch` - `git push origin master --tags` - If you are a project owner, then `npm publish` ================================================ FILE: DEPRECATED.md ================================================ # Deprecated Unfortunately, `react-vis` currently has no active maintainers. As such, we have decided to deprecate the library. This deprecation means that `react-vis` won't receive any patches or new features. If you're interested to take on ownership, please discuss on #1303. Anyone is welcome to fork this library. We're working on a new charting library that we'll introduce in 2020. We will keep you folks updated on this repo on the new library! ================================================ FILE: LICENSE ================================================ Copyright (c) 2016 Uber Technologies, Inc. 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: README.md ================================================

deprecated version build build downloads

react-vis | Demos | Docs

A COMPOSABLE VISUALIZATION SYSTEM
![demo](docs/assets/react-vis.gif?raw=true) > **_NOTE:_** This repository is now under new management. Please reach out to the new administrators if you have any questions. ## Overview A collection of react components to render common data visualization charts, such as **line/area/bar charts**, **heat maps**, **scatterplots**, **contour plots**, **hexagon heatmaps**, **pie and donut charts**, **sunbursts**, **radar charts**, **parallel coordinates**, and **tree maps**. Some notable features: - Simplicity. `react-vis` doesn't require any deep knowledge of data visualization libraries to start building your first visualizations. - Flexibility. `react-vis` provides a set of basic building blocks for different charts. For instance, separate X and Y axis components. This provides a high level of control of chart layout for applications that need it. - Ease of use. The library provides a set of defaults which can be overridden by the custom user's settings. - Integration with React. `react-vis` supports the React's lifecycle and doesn't create unnecessary nodes. ## Usage Install react-vis via npm. npm install react-vis --save Include the built main CSS file in your HTML page or via SASS: ```scss @import "~react-vis/dist/style"; ``` You can also select only the styles you want to use. This helps minimize the size of the outputted CSS. Here's an example of importing only the legends styles: ```scss @import "~react-vis/dist/styles/legends"; ``` Import the necessary components from the library... ```jsx import {XYPlot, XAxis, YAxis, HorizontalGridLines, LineSeries} from 'react-vis'; ``` … and add the following code to your `render` function: ```jsx ``` If you're working in a non-node environment, you can also directly include the bundle and compiled style using basic html tags. ```html ``` The global `reactVis` object will now be available for you to play around. You can checkout these example CodePens: [#1](https://codepen.io/Apercu/pen/mmLOpY?editors=0010), [#2](https://codepen.io/jckr/pen/oWZPJe?editors=0010), [#3](https://codepen.io/jckr/pen/BRpReQ?editors=0010) or [#4](https://codepen.io/jckr/pen/aWmRGx?editors=0010) ## More information Take a look at the [folder with examples](docs/examples) or check out some docs: - Common concepts: * [Scales and Data](docs/scales-and-data.md) about how the attributes can be adjusted. * [Animations](docs/animation.md) about how to tweak animations in the library. - Components: * [XYPlot](docs/xy-plot.md) about orthogonal charts. * [RadialChart](docs/radial-chart.md) about radial charts. * [Treemap](docs/treemap.md) about making tree maps. * [Sankey](docs/sankey.md) about making sankey diagrams. * [Radar Chart](docs/radar-chart.md) about making radar charts. * [Parallel Coordinates](docs/parallel-coordinates.md) about making parallel coordinate charts. * [Sunbursts](docs/sunburst.md) about making sunburst diagrams. * [Legends](docs/legends.md) about the legends. ## Development Make sure you are using the correct version of `node` and `yarn`. To do so, check `package.json` and find the entry "volta", e.g. ``` "volta": { "node": "14.18.0", "yarn": "1.22.4" } ``` It's recommanded to install [volta](https://volta.sh/) to manage node and yarn. To develop on react-vis, navigate to `packages/react-vis`, and install the dependencies and then build and watch the static files: yarn && yarn start Once complete, you can view the component's example in your browser (will open automatically). Any changes you make to the example code will run the compiler to build the files again. To run the tests, and create code coverage reports: yarn cover ## Requirements react-vis makes use of ES6 array methods such as [`Array.prototype.find`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find). If you make use of react-vis, in an environment without these methods, you'll see errors like `TypeError: Server rendering error: Object x,y,radius,angle,color,fill,stroke,opacity,size has no method 'find'`. You can use [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/) to polyfill these methods. ================================================ FILE: ROADMAP.md ================================================ # Roadmap This document will be updated with the middle-term ambition for React-vis. ## Current priority Right now the priority is to get React-Vis healthy and bug free while addressing as many open issues as possible. These efforts are essentially focused towards what we consider the core of react-vis: AbstractSeries, ArcSeries, AreaSeries, AxisUtils, ContinuousColorLegend, ContinuousSizeLegend, Crosshair, DiscreteColorLegend, GridLines, Hint, HorizontalBarSeries, HorizontalGridLines, LabelSeries, LineSeries, MarkSeries, RadialChart, ScaleUtils, SearchableDiscreteColorLegend, VerticalBarSeries, VerticalGridLines, XAxis, XYPlot and YAxis. ================================================ FILE: babel.config.json ================================================ { "babelrcRoots": [ ".", "./packages/*" ], "env": { "production": { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator" ] }, "development": { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator" ] }, "es": { "presets": [ [ "@babel/preset-env", { "modules": false } ], "@babel/preset-react" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator" ] }, "browser": { "presets": [ "@babel/preset-env", "@babel/preset-react" ], "plugins": [ "@babel/plugin-proposal-class-properties", "@babel/plugin-proposal-export-default-from", "@babel/plugin-proposal-optional-chaining", "@babel/plugin-proposal-nullish-coalescing-operator", ["@babel/plugin-transform-runtime", { "regenerator": true } ] ] } } } ================================================ FILE: docs/animation.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Animation Animation makes your charts feel physical, it makes them feel alive, shoot it makes them feel l33t. `react-vis` offers a simple portal into the [react-motion](https://github.com/chenglou/react-motion) animation system by exposing a simple animation prop on most components! This prop accepts three types of values: *Booleans*: if true is present then `react-vis` will use the `no-wobble` preset (see below) *Strings*: react-motion offers several different motion presets that cover most use cases. To access them set your animation prop to one of [noWobble, gentle, wobbly, stiff]. *Objects*: react-motion expects an object formatting like `{damping: NUMBER, stiffness: NUMBER}`, and if you want to give us an object like that, we will hand it direct to react-motion. You can also pass an object with `{nonAnimatedProps: ['foo', 'bar']}` to prevent those props from being interpolated by d3-interpolator. The above example has `animation: {damping: 9, stiffness: 300}`! **NOTE** In Jsx the presence of the animation prop is enough to trigger an animation, eg ```javascript ``` Will be treated as true. If you want to include the animation prop but not have animation be engaged, you need to use animation={null}! ================================================ FILE: docs/arc-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## ArcSeries: The arc series allows users to specify arbitrary arcs in the plane! This is useful for making pie charts, sunbursts, and anything else circular. ```javascript ``` A key point: XYPlot infers the necessary x and y domains by converting the angular coordinates to cartesian. If you want to guarantee a centered plot it is advisable to set the x and y domain's yourself, as above. ## Data format Reference Arc series works a little bit different than other series. The most natural language to describe arcs is using polar coordinates, so we allow users to write just that: ```javascript const myData = [ {angle0: 0, angle: Math.PI / 4, opacity: 0.2, radius: 2, radius0: 1}, {angle0: PI / 4, angle: 2 * PI / 4, radius: 3, radius0: 0}, {angle0: 2 * PI / 4, angle: 3 * PI / 4, radius: 2, radius0: 0}, {angle0: 3 * PI / 4, angle: 4 * PI / 4, radius: 2, radius0: 0}, {angle0: 4 * PI / 4, angle: 5 * PI / 4, radius: 2, radius0: 0}, {angle0: 0, angle: 5 * PI / 4, radius: 1.1, radius0: 0.8} ] ``` angle0 describes the start of the arc in radians, and angle describes the end of the arc, again in radians. radius0 describes the inner distance from the origin, while radius describes the outer distance to the origin. It is recommended to provide all four of these quantities to format your arcs well. #### angle0 Type: `number` The start position of the arc in radians. This quantity is returned literally by default. #### angle Type: `number` The end position of the arc in radians. This quantity is returned literally by default. #### radius0 Type: `number` The distance between the origin and the inside of the arc. This values is scaled linearly by default #### radius Type: `number` The distance between the origin and the outside of the arc. This values is scaled linearly by default #### radiusDomain Type: `array of numbers` The domain over which the radius is scaled. This can be an essential element in getting your arcs to look right, the automatic inference for the prop tends to be somewhat inaccurate, so it is highly encourage that you set it for your self as appropriate. For example: [0, 3]. See the code for ArcSeriesExample for more. #### color (optional) Type: `string|number` The color of an arc in the series. By default, the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. See [colors](colors.md) #### fill (optional) Type: `string|number` The inner color of an arc in the series. If `fill` and `color` are provided, `fill` will override `color`. By default, the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop fillType="literal" to the series itself. This property can also be defined on the series level. See [colors](colors.md) #### stroke (optional) Type: `string|number` The outer color of an arc in the series (i.e. its outline). If `stroke` and `color` are provided, `stroke` will override `color`. By default, the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop strokeType="literal" to the series itself. This property can also be defined on the series level. See [colors](colors.md) #### opacity (optional) Type: `string|number` Default: 1 The opacity of an arc in the series, from 0 (transparent) to 1 (opaque). #### padAngle (optional) Type: `number|function` The padding to be applied between arcs. ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### center Type: `Object` of the form `{x, y}`, where x and y are in coordinates This allows users to specify the origin of their arcs. #### color Type: `string|number` The color for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### className (optional) Type: `string` Provide an additional class name for the series. #### data Type: `Array` Array of data for the series. See above data format reference. #### fill Type: `string|number` The inner color for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### opacity Type: `string|number` The opacity for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### stroke Type: `string|number` The outer color for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### style (optional) Type: `object` SVG paths (which is what the arc series is made up of) have numerous manipulable properties, so rather than trying to prescribe all of them as props we offer a port to let you style it for yourself. ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onValueMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on right click // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` ================================================ FILE: docs/area-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # AreaSeries In addition to the LineSeries, react-vis offers a similar chart type for area charts. Like LineSeries, AreaSeries: - are styled at the series level, not at the mark level. - can have a curve property for a different interpolation between points. Unlike LineSeries, AreaSeries: - have a fill property. By default, the color property affects both the fill color and the outline color of the area charts. However, these two can be set independently, - don't have an API to style the stroke beyond color. It's still possible to use the style property, though. - can be stacked, - do not have a canvas equivalent. The stroke property of an AreaChart creates an outline around the whole shape of the chart (including to its left, right and bottom.) To create a chart that has a fill, no distinct lines to the left, right or bottom, but a different line style at the top, you may create an area chart with a line chart on top. ## Data format reference #### x Type: `number` Left-to-right position of marks in the series. #### y Type: `number` Top-to-bottom position of the top edge of the series. #### y0 Type: `number` Default: `0` Top-to-bottom position of the bottom edge of the series. ## API Reference #### color (optional) Type: `string|number` Default: see [colors](colors.md) A color for both the fill and the outline of the area series. Will be overridden by both the fill and the stroke property, if provided. #### curve (optional) Type: `string|function` Default: `null` Apply the provided or named curve function from the D3 shape library to smooth the line series plot, see [the D3 documentation](https://github.com/d3/d3-shape#curves) for function names and instructions. Providing the function, not the name, will require importing the d3-shape package in order to configure it: ```javascript // Setting up with only a name const stringCurveProp = ; const configuredCurve = d3Shape.curveCatmullRom.alpha(0.5); const funcCurveProp = ; ``` #### data Type: `Array` Array of data for the series. See above data format reference. #### fill (optional) Type: `string|number` Default: see [colors](colors.md) A color for the fill of the area series. Will override the color property if both are provided. #### getNull (optional) Type: `function` Default: `null` A function that will be invoked for each data element that will return a boolean that specifies if the data point should be drawn or not. For more information see [the D3 documentation](https://github.com/d3/d3-shape#area_defined). ```javascript // Only draw datapoints where the y value is not equal to null d.y !== null} data={data} /> ``` #### opacity (optional) Type: `number` Default: `1` Opacity of the area chart from 0 (transparent) to 1 (opaque). #### stroke (optional) Type: `string|number` Default: see [colors](colors.md) A color for the outline of the area series. Will override the color property if both are provided. #### style (optional) Type: `object` An object which holds CSS properties that will be applied to the SVG element(s) rendered by the series. This allows you to style series beyond the other explicitly defined properties and without having to use CSS classnames and stylesheets. See [style](style.md) ```jsx ``` ### Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) ```jsx { // does something on mouseover // you can access the value of the event }} ``` #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on an AreaSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves an AreaSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over an AreaSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on mouse over // you can access the value of the event }} ``` ================================================ FILE: docs/axes.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Axes `XAxis` and `YAxis` shows are responsible for the axis in the chart. They can be used simply ```javascript ``` Which will automatically interpolate across the relevant domains of the data (ie it will present reasonable values for x and y). It can also be used to create more complex axes Which is produced via ```javascript `Value is ${v}`} tickLabelAngle={-90} /> v * v}/> WORDS[v]}/> ``` For greater control over the specific styling and placement of the axis label, please see [ChartLabel](chart-label.md). ## API Reference #### title (optional) Type: `string` Shows the title for the axis. #### orientation (optional) Type: `'top'|'left'|'bottom'|'right'` The position of the axis inside the chart. By default **it is already set** to `'bottom'` for `XAxis` and to `'left'` for `YAxis`. Similar to how the axis are oriented in d3-axis. #### position (optional) Type: `'end'|'middle'|'start'` The position of the title relative to the axis. This value is set to `'end'` by default (i.e. towards the left of a horizontal axis, towards the top of a vertical axis.) #### tickTotal (optional) Type: `number` Total number of ticks on the axis. Already set by default. Similar to the `tickTotal()` method of d3-axis. #### tickValues (optional) Type: `Array<*>` An array of values (not coordinates!) that where the ticks should be shown. Similar to the `tickValues()` method of d3-axis. #### tickFormat (optional) Type: `function(value, index, scale, tickTotal)` Format function for the tick label. Similar to the `tickFormat()` method of d3-axis. Typically the value that is returned is a string or a number, however this function also supports rendering SVG React elements. To wit, I could have formatting function like ```javascript function myFormatter(t, i) { return ( MY VALUE {t} ); } ``` Or you can customize the tick formatting by calling the `tickFormat()` function on the d3-scale by yourself and pass additional formatting parameters (e.g s for SI-prefix). ```javascript function mySIPrefixFormatter(value, index, scale, tickTotal) { return `${scale.tickFormat(tickTotal, 's')(value)}Wh`;// -> e.g. 1.2kWh } ``` **Note!** The return value will be wrapped with a `` node if it's a string, ``, or ``. In all other cases the returned element will replace the `` node. In case it's a custom React element it will also receive two additional props: `containerWidth` and `tickCount`. This way you can e.g. render a `
` to truncate long labels: ```javascript const MyLabel = props => (
{props.children}
); function myFormatter(value) { return {value}; } ``` #### tickSize (optional) Type: `number` Default: `6` Tick size for the axis. Sets both inner and outer sizes of the tick line. Similar to the `tickSize()` method of d3-axis. #### tickSizeOuter (optional) Type: `number` Default: `null` Tick size for the axis. Sets the outer size of the tick line. Similar to the `tickSizeOuter()` method of d3-axis. NOTE: 1.0.0 and onwards now properly draws outer tick using this value. Previously, this value affected the drawing of inner tick. #### tickSizeInner (optional) Type: `number` Default: `null` Tick size for the axis. Sets the inner size of the tick line. Similar to the `tickSizeInner()` method of d3-axis. NOTE: v1.0.0+ properly draws inner tick using this value. Previously, this value affected the drawing of outer tick. #### tickPadding (optional) Type: `number` Default: `2` Distance between the tick and the text of the tick in pixels. Similar to the `tickPadding()` method of d3-axis. #### tickLabelAngle (optional) Type: `number` Default: `0` The angle of the tick label. Can be used to fit the long labels of the axis without truncation. #### left (optional) Type: `number` Horizontal position of the axis in pixels. **Already set by default**, but can be overridden by the user. #### top (optional) Type: `number` Vertical position of the axis in pixels. **Already set by default**, but can be overridden by the user. #### width (optional) Type: `number` Width of the axis in pixels. **Already set by default**, but can be overridden by the user. #### height (optional) Type: `number` Height of the axis in pixels. **Already set by default**, but can be overridden by the user. #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### style (optional) Type: `object` An object that contains CSS properties with which the axis component can be entirely re-styled. As the Axis component is composite, it is possible to style its different parts individually. See [style](style.md) The various parts of the axis can be styled by passing an object to the `line`, `ticks`, `text` and `title` properties: ```jsx ``` ================================================ FILE: docs/bar-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Bar Series **TLDR**: use bar series to make bar charts, but not histograms. Bar series allows users to construct charts that contain rectangles that are oriented either left-right or top-bottom. This type: of series is generally used to visualize mappings of categorical data to quantitative data. For instance if you had counts of pigeon sightings by season, that would be a perfect bar series (`[{x: 'winter', y: 10}, {x: 'spring', y: 100}, {x: 'summer', y: 10000}, {x: 'fall', y: 10}]`), while if that data was represented as the individual records of your sightings of pigeons (`[{x: May 1st 2pm}, {x: May 12th 1am}]`) you might either want a mark-series (to make a scatterplot) or a rect-series (to make a histogram). Bar series come in two flavors, `HorizontalBarSeries` and `VerticalBarSeries`. VerticalBarSeries have vertical bars, HorizontalBarSeries have horizontal bars, plain and simple! ## Data format Reference Like other series, it is required that the data be an array of objects, formatted like so: ```javascript const myData = [ {x: 'A', y: 10}, {x: 'B', y: 5}, {x: 'C', y: 15} ] ``` Where x and y are required quantities and additional properties may be stapled on. #### x Type: (VerticalBarSeries): `string|number` Type: (HorizontalBarSeries): `number` The x position in coordinates of the box to be used. This quantity is treated as a category (at least in VerticalBarSeries) and so considers the exact left-right positioning to be not that important (which is something to watch out for if you are providing exact numbers, in such a case it is better to the rect-series). #### y Type: (VerticalBarSeries): `number` Type: (HorizontalBarSeries): `string|number` The y position in coordinates of the box to be used. For VerticalBarSeries this value is considered a number, and is scaled against it's domain into pixels. #### y0 (Optional) Type: (VerticalBarSeries): `number` Type: (HorizontalBarSeries): `string|number` The y0 position in coordinates of the box to be used, this is where the bottom of the bar is placed, defaults to zero. Use is not recommended with stacked bars. For VerticalBarSeries this value is considered a number, and is scaled against it's domain into pixels. #### color (optional) Type: `string|number` The color of a bar in the series. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. #### opacity (optional) Type: `number|Object` Opacity of the individual box to be rendered. By default opacity is scaled by `literal`, so the exact value provided will be used. This property can also be defined on the series level. #### stroke (optional) Type: `number|Object` The color of the outline of the box to be rendered. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. #### fill (optional) Type: `number|Object` The color of the inside of the box to be rendered. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### color Type: `string|number` The color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md) #### className (optional) Type: `string` Provide an additional class name for the series. #### cluster Supply a clustering key for this series. When used with the `stackBy` attribute, creates a clustered stacked bar chart. Returning to our pigeon example from earlier, if you had multiple years of pigeon sightings by season and you wanted to compare the season, clustering would be a great way to do that. #### data Type: `Array` Array of data for the series. See above data format reference. #### fill Type: `string|number` The inner color for all elements in the series, this property will be over-ridden by fill specified in the data attribute. See [colors](colors.md) #### opacity Type: `string|number` The opacity for all elements in the series, this property will be over-ridden by color specified in the data attribute.) #### stroke Type: `string|number` The outer color for all elements in the series, this property will be over-ridden by stroke specified in the data attribute. See [colors](colors.md) #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) #### barWidth Type: `Number` The percentage for which each bar fills the designated bucket. 1.0 means that the bar fills the whole bucket (no padding between bars), while a smaller percentage means more whitespace between the bars. ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onValueMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` ================================================ FILE: docs/borders.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Borders Sometimes when modifying the domain of the XYPlot it can be useful to enforce a border, so that some components appear, and others do not. One way to do this is to use the `Borders` component. It is a simple component that creates rectangles the directly correspond to the margins of the plot. For example, a set up like this: ```jsx ``` would cause the first area series to be truncated underneath the borders, while the second one would not be! This level of granular border control can be useful if you are using multiple kinds of series, for instance if you have a mark series that you wish to show the entire mark for, and a line series that you are alright with truncating at the border. ## API Reference #### className (optional) Type: `String` A class name to apply to each of the borders, as well as the root border container. It will be enumerates on top the borders using suffixes, eg if className={"my-cool-class"} the top rectangle will have a class name "my-cool-class-top". #### style (optional) Type: `Object` You can pass a style object to your Hint component to apply your own styles. See [style](style.md) ```jsx ``` Because border its made up of four individual rectangular components (there being four borders on an XYPlot) it is advisable to specify styles for all four rectangles. This can be done using either the style object or css-classes. Alternatively, if all the borders should be treated the same, this can be achieved by supplying an all object to style. This can be then over-ridden: ```jsx ``` ================================================ FILE: docs/chart-label.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## ChartLabel When you are styling your chart sometimes you just want to take complete control of label placements. Maybe you want to annotate something, or maybe you just want to place your axis labels in a very specific place, ChartLabel allows you to do just that. Let's look at an example: ```jsx ``` This usage is the same as using title on the XAxis or YAxis, however it allows us greatly flexibility in terms of styles and placement. It is significantly more verbose than using the basic methods on Axis, but the it allows you to do whatever you want. You could place your axis label in the center! You could make them diagonal or a really big. The world is your oyster. This element is different then the [LabelSeries](label-series.md) because the elements that it describes ARE NOT data carrying. This element will not affect the computed domain or range. It'll just go where you place it and it won't affect anything else. ## API Reference #### text Type: `string` The content of the label #### className (optional) Type: `string` Provide an additional class name the label. #### includeMargin (optional) Type: `Boolean` Defaults to true Whether or not to use compute the percentage placement with the margins or not. #### xPercent Type: `Number` (between 0 and 1) Where to place the label on the charts width, expressed as percentage (of the width). If the includeMargin flag is included, then this percentage is of the total width, if not then it is of just the inner chart area. #### yPercent Type: `Number` (between 0 and 1) Where to place the label on the charts height, expressed as percentage (of the height). If the includeMargin flag is included, then this percentage is of the total height, if not then it is of just the inner chart area. #### style Type: `object` The specific styles to apply to the text element of the label. These styles are applied directly to the dom object and are interpreted as SVG styles (as opposed to CSS styles). ================================================ FILE: docs/clip.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Clip Depending on the data and domain, sometimes the series in the plot will extend into the axis. This can either be solved with a [Border](border.md), or the elements can be clipped. To have the rendered series, clipped you will need to set up a `clipPath` (see [MDN](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/clipPath)) and tell the series to use it. As seen below, the `clipPath` can be created with the `ContentClipArea` component, and its `id` can be referenced by the components that want to be clipped. ```jsx ``` ## API Reference #### id (optional) Type: `String` The id to assign to the `clipArea`. If not provided, this will default to `content-area` ================================================ FILE: docs/colors.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Color Color can be set and affected in many ways in React-vis. The main principles are: * sensible defaults - your chart should look good even if you don't do anything; * respect of specificity - you can change things at a high level (ie component) but override this at the series level and, when relevant, at the mark level. * flexibility - everything down to the humble tick can be colored and recolored. ### Setup In this document, let's look at how different color choices affect 3 different mini charts. Each chart in the doc is made of 3 series with x going from 0 to 9 and random values of y between 0 and 10. The left-most chart is made of VerticalBarSeries, the middle one is made of 3 LineSeries, and the right-most one is made of MarkSeries. So it goes like this: ```jsx ``` ### Cases We do nothing: With no color instruction, colors are automatically set by series according to the default react-vis palette, which is: We specify color in XYPlot ```jsx ``` Without any further instruction, all the series are red. Note that in the case of LineSeries, we have to use stroke instead of color for this effect to work. We specify color by series The next step is passing colors to by series. When we do that, we add a color prop to each series component: ```jsx ``` How this color information is going to be treated depends on a number of factors. Color scales Once it's passed through series, color works like a [scale](scales-and-data.md); in other words, it transforms data into a visual representation. There are several types of scales. A linear scale works with a range of numerical values on one hand ("domain"), and two colors on the other hand ("range"). If given a numerical value in the domain, it transforms it into a color in the range depending on how far into the domain that value was. If given the minimum value of the domain, the scale will return the first color of the range. If given the maximum value of the domain, it will return the second color of the range. And if given a value in between, it will return an interpolation between these two colors - the closer that value is from the minimum, the more it will look like the first color, and the closer it is to the maximum, the more it will look like the second. Else, it's a proportional mix of the two. For example, if a domain is [0, 1] and the range is ['black', 'white'], 0 will become 'black', 1 will become 'white', and 0.2 will become '#333333' (20% between black and white) The linear scale can be extended to work with multi-point domains and range. If you pass 3 (ordered) values to the domain, and 3 values to the range, when given a data point, the scale will figure out which segment of the domain this data point corresponds to, and will match it with the corresponding segment of the range. If our domain is [0, 1, 2] and our range is now ['black', 'white', 'blue'], 0.2 will still be '#333333' (20% between the first 2 values), but 1.5 will become '#8080ff' (halfway between white and blue) A categorical color scale associates a discrete number of values (also called domain) to a discrete number of colors (also called range). One big difference is that the values can be number or strings. For instance, if a categorical color scale has the domain: ['yes', 'maybe', 'no'] and the range ['blue', 'yellow', 'red'], it will transform 'yes' into 'blue' and 'no' into 'red'. There will be no interpolation. If it finds a value which is not in its domain, it will return undefined (which will be represented in black). Finally, the literal color scale just returns whatever is provided as is. With a literal color scale, we can have color names in the dataset, and they will be used without transformation. Categorical colors at series level For this example, the XYPlot props are: ```jsx ``` As you can see, __using categorical color at the series level doesn't work for bar charts or scatterplots__. It does for line charts though. Linear colors at series level ```jsx ``` Likewise, __using linear color at the series level only works for line charts__. Literal colors at series level ```jsx ``` However, setting color at the series level works for all kinds of charts. It's not even necessary to specify a color type, a domain or a range. We specify color information at mark level For this second series of charts, we are going to specify color information inside of our dataset (ie the series which will be passed to the props "data"). Previously, our datasets only included x and y information: ```js const series1 = [ {x: 0, y: 2}, {x: 1, y: 6}, ... ]; ``` Now, they will have a color information as well. * For our categorical examples, that color value will be a random integer between 0 and 10. * For our linear examples, that color value will be a random number between 0 and 10 (not necessarily an integer). * Finally, for our literal example, the color information will be the name of a color in hex format. Categorical colors at mark level ```jsx ``` So what happens here? For line charts, __nothing!__ They ignore colors at mark level. So they behave just like the default case (as if we passed no color information at all) For the 2 other charts, marks are colored according to the default extended palette: Here, I have specified the colorType prop at the XYPlot level. I could have done so at the series level, inside of each series component (it cascades down). However, I haven't specified a colorRange or a colorDomain. It's going to use the default extended palette as the color range. We'll override this in the next example. As for domain, it's going to associate the first color value it finds in the dataset with the first color of the palette, the second distinct color it finds with the second color of the palette, and so on and so forth. With this syntax, we'll render marks which have different color information in different colors, but we don't control which color. If we want to control which color a specific value is going to be associated with, we have to pass a colorDomain. Categorical colors at mark level, custom palette ```jsx ``` This time, I'm passing a custom palette: Behavior for line chart is still identical, but the colors are different for our bar charts and scatterplots. As I'm not passing a color domain, I still don't control which value will be associated with which color - not super important since my color values are random numbers. But if order matters, a colorDomain is required. Linear colors at mark level, default palette ```jsx ``` The linear color scale is the default color scale. So, to get that behavior, we don't need to specify this colorType in XYPlot. Its associated color range was conceived by someone who really likes orange: I haven't specified the color range either. React-Vis will compute it by looking at the minimum and maximum value associated with color in all the series of a given XYPlot, and use that as the domain. The line charts are still unaffected. Linear colors at mark level, custom palette ```jsx ``` Here's the same code, but we define the color range. This green palette comes from ColorBrewer. Literal colors at mark level, default palette ```jsx ``` Finally, we can pass literal color names in our dataset from our custom palette. The line charts are still not affected. ### Going beyond Independently control fill and stroke The line chart series (LineSeries) is only a line, but most other series (AreaSeries, ArcSeries, BarSeries, HeatmapSeries, HexbinSeries, MarkSeries, RectSeries and their derivatives, including LineMarkSeries) involve 2D shapes that have both a fill color and a stroke color. In SVG, those correspond to the fill and the stroke css properties (fillStyle and strokeStyle in canvas). When we pass color information, we set both the fill and stroke. However, we can set them independently by using "fill" or "stroke" instead of color. As of this writing, ContourSeries and PolygonSeries don't follow this model and their color can only be controlled by "color". ```jsx ``` Here, we set a stroke value at the XYPlot level for all of our charts. What happens? The bar chart outerbox is now of that color, The line series are now represented in that color - this takes over the default behavior, The scatterplot dots are also now surrounded with that color. Note that in the case of a LineMarkSeries (a combination of a LineSeries and a MarkSeries) the stroke property will control both the color of the line and the stroke of the marks. If you want a different color, you can just instead create a LineSeries and a MarkSeries with the same data: ```jsx ``` Here, I want my dots to have a white outline. Why did I specify the color of each of my series? You might have to scroll all the way to the top for the answer! If I had done nothing all the colors of my series would have been taken from the default palette for each new series. So the first line series would have had the first color, then the first mark series would have had the _second_ color... and so on and so forth. By specifying a color, we are guaranteeing that the dots and the lines have the same color. Using styles We can pass style information to anything - XYPlot, series, mark - and override the look and feel of that element. Styles don't have to be static objects - they can be computed at run time. Styles are a different way to control colors. While using the color prop, or a color property in a dataset, can be much more concise, everything can be affected by styles - including non-mark elements such as ticks or gridlines. See [style](style.md) for more info. Using specificity We've seen that we can set color information at the plot level, at the series level and at the mark level. But what happens when we do it at several levels at the same time? The most specific wins. If you need to color one element (say, one mark) differently from all the others, you can specify color at a higher level (say, the series or the plot) and only pass color information to the exception, rather than pass color information to all elements. ```jsx ``` Notes: * For the line series, which behave differently than other series, you must use stroke instead of color for this to work. * For the scatterplot series, I'm using specificity twice: there's a color at the plot level, overridden by a color at the first series level, overridden by a color on the 7th mark of the series. Using gradients Why use a boring solid color when you can use gradients? We're not sure either! Once you define gradients (see [gradients](gradients.md)) you can use them instead of color (or fill, or stroke) at the series level. ```jsx const gradient = ( ); return (
{gradient} {gradient} {gradient}
) ``` Note that I'm using the userSpaceOnUse gradient unit, so the colors are set independently of the size of the object. I'm borrowing the colors of the gradient from the ones used on the activity sparklines in GitHub. ================================================ FILE: docs/contour-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## ContourSeries The contour series allows for the easy creation of contour density plots. These can be more effective for visualizing heat map data than a rectangular heat map! Given a number of points in a space the relative contour lines are computed, so as to simplify the output into a more legible format! The ContourSeries expects a similar data input as would be fed to either the MarkSeries or the HeatmapSeries. It can be as easy as just providing a well formatted data prop (an array of object containing numerically valued x and y keys), or more complex such as below: ```javascript ``` ## API reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### bandwidth (optional) A parameter that directly maps into d3-contour's bandwidth parameter. See the [docs for more](https://github.com/d3/d3-contour#density_bandwidth) #### className (optional) Type: `string` Provide an additional class name for the series. #### data Type: `Array` Array of data for the series. Follows the usual pattern of an array of objects formatted with x and y coordinates, [{x: 0, y: 0}, ...]. #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) ================================================ FILE: docs/crosshair.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Crosshair `Crosshair` is a tooltip for multiple values at the same time. Its purpose is to combine several values with the similar X coordinate in one tooltip. Crosshair is automatically aligned by the x coordinate depending on what values are passed. In case if custom representation of crosshair is needed, the component is able to wrap the user's JSX. In this case no CSS is applied to that. Here's a short example: ```jsx

Values of crosshair:

Series 1: {myValues[0].x}

Series 2: {myValues[1].x}

``` #### className (optional) Type: `string` Provide an additional class name for the series. #### itemsFormat (optional) Type: `function` The function that formats the list of items for the crosshair. Receives the list of data points, should return an array of objects containing `title` and `value` properties. _Note: please pass custom contents in case if you need different look for the crosshair._ #### style (optional) Type: `object` An object that contains objects of CSS properties with which the component can be entirely re-styled. As the Crosshair is composed of several elements, it is possible to provide style objects for any and all parts of the tree. See [style](style.md) Most generally, there are three top level keys: `line`, `title`, and `box`. These in turn lead to their corresponding style objects. #### titleFormat (optional) Type: `function` The function that formats the title for the crosshair. Receives the list of data points, should return an object containing `title` and `value` properties. _Note: please pass custom contents in case if you need different look for the crosshair._ #### values Type: `Array` The array of data points to show the crosshair at. Crosshair will automatically align to the horizontal position of the points passed there. ================================================ FILE: docs/custom-svg-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## CustomSVGSeries When creating visualizations, it is sometimes necessary to get your hands dirty and completely take control over what SVG components will be shown. This could be necessary in situations where you have predefined SVG code that you just want to appear the way you drew it in Sketch (but positioned using coordinates), or maybe you have multiline text annotations that you want to formatted in a particular way, or you just want to use an alternative type of mark instead of the usual scatterplot mark to differentiate series in a set. To serve these and many other tasks, we use the CustomSVGSeries. The premise of the series is that it simply puts a `` element at a desired x,y location, and the offers you a variety of ways to fill in the contents of that `` element. Here's an example of the data format: ```javascript const myData = [ {x: 1, y: 10, customComponent: 'circle', size: 10}, {x: 1.7, y: 12, size: 20, style: {stroke: 'red', fill: 'orange'}}, {x: 2, y: 5}, {x: 3, y: 15}, {x: 2.5, y: 7, customComponent: (row, positionInPixels) => { return ( {`x: ${positionInPixels.x}`} {`y: ${positionInPixels.y}`} ); }} ] ``` Just like other series, x and y are used to position the group. The customComponent key word is used to determine how to fill in the svg (see below), and then size is used modify the size of the contents when using a string. Used in context of the series: ```javascript ``` ### Defining your marks The type of custom svg marks can be determined in one of several ways: - As a string on a series level - As a function on a series level - As a string on row level - As a function on a row level There are currently four types of string accessible custom marks: **star**, **square**, **circle**, and **diamond**. If using a string, it can be useful to specify a size for the mark. This is done on a row level (see above data api example), with the size prop. Size is expressed in pixels, and is NOT scaled with the normal react-vis size keyword. They look like this: If using a function to defined your mark, it is important to note that the function receives three arguments (customComponent, positionInPixels, globalStyle), where customComponent is the row of data as you have defined it. Thus if you are defining a function for the series as a whole you can make modifications based on the individual row as you go! ## API reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### customComponent (optional) Type: `string|function` Provides the mark type for the entire series. Defaults to a 'circle'. See `Defining You Marks` above. #### data Type: `Array` Array of data for the series. See above data format reference. #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onValueMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. ================================================ FILE: docs/decorative-axis.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## DecorativeAxis In react-vis we try to express all of our components in terms of x and y coordinates. This is splendid and allows to separate a lot of our rendering logic from components! However, sometimes it is necessary to create labels that don't necessarily correspond to the underlying coordinates. For instance in cases of parallel coordinates (above) we want to mark up space in a series of discrete channels to show change across many different variables. To fill this need we use the ```DecorativeAxis``` component! ```javascript ``` In the above example we start be setting our domain on the XYPlot (though this would be accomplished automatically if any of it's children had a data prop), and then specified where in the XY space we want our Axis to be (axisStart/axisEnd). Finally we specify the domain that we wish to show across that axis. **WHAT IS THIS FOR** Labeling sections of XY space when we wish the viewer to interpret space in a different way. This could be as part of a Radar chart or radial chart! Or even, the inherently bad Dual Y Axis chart. **WHAT IS NOT THIS FOR** Using in place of XAxis or YAxis, which should cover most of use cases in which space is being used normally. This type of axis allows for a lot of freedom in it's usage, however that can be dangerous. Most of the time, if you can't get XAxis and YAxis to do what you want, you maybe don't need axes. Be careful! ## API Reference #### axisStart Type: `Object` Specify a start point for the decorativeAxis. It should be expressed in terms of coordinates (not pixels!) as a object like ```{x: 10, y: 1}``` #### axisEnd Type: `Object` Specify a start point for the decorativeAxis. It should be expressed in terms of coordinates (not pixels!) as a object like ```{x: 10, y: 1}``` #### axisDomain Type: `Array` This array of numbers allows the user to specify the values that will be interpolated across on the axis. #### tickTotal (optional) Type: `number` Total number of ticks on the axis. Already set by default. Similar to the `tickTotal()` method of d3-axis. #### tickSize (optional) Type: `number` Default: `5` Tick size for the axis. Sets both inner and outer sizes of the tick line. Similar to the `tickSize()` method of d3-axis. #### tickValue (optional) Type: `function(*)` Format function for the tick label. Similar to the `tickFormat()` method of d3-axis. #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### style (optional) Type: `object` An object that contains CSS properties with which the axis component can be entirely re-styled. As the Axis component is composite, it is possible to style its different parts individually. See [style](style.md) The various parts of the axis can be styled by passing an object to the `line`, `ticks`, `text` and `title` properties: ```jsx ``` ================================================ FILE: docs/examples/building-things-other-than-charts.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. [Source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/force-directed-graph/force-directed-graph.js) ================================================ FILE: docs/examples/extensibility.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Extensibility react-vis is easily extensible! If we don't have what you want it's easy to make! For instance, the above chart was made by simply extending abstract series and adding a little sugar. [Source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/candlestick/candlestick.js) ================================================ FILE: docs/examples/iris-dashboard.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. Click and drag on the charts! The iris data set is a well beloved data set for getting to know various technical topics. Here we use it to show how to make a small multiples dashboard with react vis. You can find out more about it at it's [wikipedia page](https://en.wikipedia.org/wiki/Iris_flower_data_set) [Source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/iris-dashboard/iris-dashboard.js) ================================================ FILE: docs/examples/responsive-vis.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. This demo explores the concept of "Responsive Data Visualization" (As coined by Nick Rabinowitz). The basic notion is lifted from responsive design: some features work for some screen resolutions, while others do not. Responsive design determines whether or not to use a given feature by consulting an aspect ratio (width by height). Through this notation we are able to create beautiful web experiences that work seamlessly between phones, tablets, and computers. Taking this idea on step farther we introduce a third element into the fray: data size. In data visualization, we often need to create applications that work with enormous ranges of sizes of data. Sometimes the data might be small (10 - 100 rows), or it might be gigantic (100k-1M+ row): throughout the entire range it just needs to work. Again, following our cues from responsive design, we note that maybe labels on scatterplots look great when you have under 50 data points, but bad when you have 2000. Checkout Nicks [original demo](http://nrabinowitz.github.io/rdv/) for more details on the theory, as well to see his rad implementation in raw d3. [Scatterplot source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/responsive-vis/responsive-scatterplot.js) [Barchart source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/responsive-vis/responsive-bar-chart.js) ================================================ FILE: docs/examples/showcases/axes-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/legends-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/misc-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/plots-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/radar-chart-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/radial-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/sankeys-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/sunburst-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/showcases/treemaps-showcase.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ================================================ FILE: docs/examples/stream-graph.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. [Source code](https://github.com/uber/react-vis/blob/master/packages/showcase/examples/streamgraph/streamgraph-example.js) ================================================ FILE: docs/flexible-plots.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Flexible plots By default, XYPlot requires a width and a height. There are times, however, when you'd like your chart to take all the space it can. For these cases, React-vis provides three different types of `XYPlot` with different flexible dimensions: - FlexibleWidthXYPlot - FlexibleHeightXYPlot - FlexibleXYPlot and the associated helper functions that have been used to create these flexible components. ```jsx import { FlexibleXYPlot, FlexibleWidthXYPlot, FlexibleHeightXYPlot } from 'react-vis'; ``` `FlexibleWidthXYPlot` modifies `XYPlot` so that it no longer requires a width, since it will take all the with in its container div. Likewise, `FlexibleHeightXYPlot` modifies `XYPlot` so that is no longer requires a height, and its height will be that of its container div. Finally, `FlexibleXYPlot` modifies `XYPlot` so that it no longer requires either a width and a height, its dimensions will be that of its container. These components can be used exactly as `XYPlot`: ```jsx ``` ### No worries about resizing A flexible plot is useful when you don't know for sure the size of the container where the chart will go. On top of that, flexible plots resize themselves when the window size changes. You can try that by changing the size of this window. ### Size of parent container is not the same as "all the available space" Flexible plots will inherit dimensions from their container. This is not the same thing as taking all the available space; if there are other elements in that container, a flexible plot won't deduce their dimensions from its own. For best results, a flexible plot should be the only child of its container. ### Responsive visualizations We can go one step beyond and not simply adjust the dimensions of the chart of the available space, but change how a dataset is being represented. See the example [responsive visualizations](#/examples/charts/responsive-vis) ### Custom flexible components By using the provided flexible helpers, you can use them to make your own components flexible, like we did to create `XYPlot` flexible alternatives: ```jsx import { XYPlot, makeVisFlexible, makeWidthFlexible, makeHeightFlexible, } from 'react-vis'; const FlexibleXYPlot = makeVisFlexible(XYPlot); const FlexibleWidthXYPlot = makeWidthFlexible(XYPlot); const FlexibleHeightXYPlot = makeHeightFlexible(XYPlot); ``` ================================================ FILE: docs/getting-started/getting-started.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ### Getting started #### Jump right in on codepen! You can use react-vis directly on [codepen](https://codepen.io/ubervisualization/pen/BZOeZB) (or equivalent). Each published version of react-vis is accessible via [unpkg.com](https://unpkg.com). Add react files, and a link to the latest react-vis version - such as https://unpkg.com/react-vis@1.6.7/dist/dist.min.js. But you can simply just use that [pen](https://codepen.io/ubervisualization/pen/BZOeZB) and take it from there. #### Install the react-vis module If you want to use react-vis in your project, add it from the command line: ``` npm install react-vis ``` (or yarn add react-vis - the following will assume that you use npm for concision's sake but you can substitute yarn if installed) #### Create a new project with react-vis Let's create a new vis app from scratch. To do this, let's use [create-react-app](https://github.com/facebookincubator/create-react-app), the popular Facebook scaffold. If you haven't installed yet, do so: ``` npm install -g create-react-app ``` And now: ``` create-react-app my-awesome-vis-app cd my-awesome-vis-app npm install react-vis ``` That's it! you are now ready to create amazing charts. Let's edit create-react-app's default App.js: ```jsx import React, { Component } from 'react'; import './App.css'; import '../node_modules/react-vis/dist/style.css'; import {XYPlot, LineSeries} from 'react-vis'; class App extends Component { render() { const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2}, {x: 9, y: 0} ]; return (
); } } export default App; ``` and then on the command line interface: ``` npm run start ``` and your chart is in the browser. Note that on line 3, I have imported the react-vis stylesheet. There are many ways to do that, and it is actually optional. But apps made with create-react-app will let you import stylesheets directly, so that's a simple way to do so. #### Your first chart We tried to make react-vis syntax as close to the traditional react syntax. You have components which have props and possibly children. Every react-vis chart is inside a component called XYPlot, for which a height and a width must be specified: ```jsx ``` And all the elements of a chart - series, axes, gridlines, etc. are other components, which will be children of XYPlot. ```jsx ``` And like in traditional react, order matters as components are drawn in order. In the previous example, the gridlines are drawn below the line series, but in this next example, they will be drawn above it. ```jsx ``` ================================================ FILE: docs/getting-started/installing-react-vis.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ### Install the react-vis module If you want to use react-vis in your project, add it from the command line: ``` npm install react-vis ``` (or yarn add react-vis - the following will assume that you use npm for concision's sake but you can substitute yarn if installed) ================================================ FILE: docs/getting-started/new-react-vis-project.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ### Create a new project with react-vis Let's create a new vis app from scratch. To do this, let's use [create-react-app](https://github.com/facebookincubator/create-react-app), the popular Facebook scaffold. If you haven't installed yet, do so: ``` npm install -g create-react-app ``` And now: ``` create-react-app my-awesome-vis-app cd my-awesome-vis-app npm install react-vis ``` That's it! you are now ready to create amazing charts. Let's edit create-react-app's default App.js: ```jsx import React, { Component } from 'react'; import './App.css'; import '../node_modules/react-vis/dist/style.css'; import {XYPlot, LineSeries} from 'react-vis'; class App extends Component { render() { const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2}, {x: 9, y: 0} ]; return (
); } } export default App; ``` and then on the command line interface: ``` npm run start ``` and your chart is in the browser. Note that on line 3, I have imported the react-vis stylesheet. There are many ways to do that, and it is actually optional. But apps made with create-react-app will let you import stylesheets directly, so that's a simple way to do so. ================================================ FILE: docs/getting-started/react-vis-in-codepen.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ### Jump right in on codepen! You can use react-vis directly on [codepen](https://codepen.io/ubervisualization/pen/BZOeZB) (or equivalent). Each published version of react-vis is accessible via [unpkg.com](https://unpkg.com). Add react files, and a link to the latest react-vis version - such as https://unpkg.com/react-vis@1.6.7/dist/dist.min.js. But you can simply just use that [pen](https://codepen.io/ubervisualization/pen/BZOeZB) and take it from there. ================================================ FILE: docs/getting-started/your-first-chart.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ### Your first chart We tried to make react-vis syntax as close to the traditional react syntax. You have components which have props and possibly children. Every react-vis chart is inside a component called XYPlot, for which a height and a width must be specified: ```js ``` And all the elements of a chart - series, axes, gridlines, etc. are other components, which will be children of XYPlot. ```js const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2}, {x: 9, y: 0} ]; ``` And like in traditional react, order matters as components are drawn in order. In the previous example, the gridlines are drawn below the line series, but in this next example, they will be drawn above it. ```js ``` ================================================ FILE: docs/gradients.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Gradient Sometimes it is useful to style our svg components using gradients. The way that this is done in React-vis is by making use of the GradientDefs component, which is a simple wrapper on the svg tag. Simply write gradient commands as you would normally as children of the GradientDefs component, and reference them from your series! ```javascript ``` This approach works with both types of gradients (Linear and circular gradients)! The biggest gotcha is that react doesn't play nice the style prop that is normally specified on the gradientTags, so it is best to specify each property directly on the component as above. ## Component API Reference #### className (optional) Type: `string` Provide an additional class name for the series. ================================================ FILE: docs/grids.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Cartesian Grids `VerticalGridLines` and `HorizontalGridLines` show a grid inside the chart. Here is a short example: ```jsx ``` Currently both components have following properties: #### tickTotal (optional) Type: `number` Total number of lines on the grid. Already set by default, depends on the size of the grid. Similar to the `tickTotal()` method of d3-axis. #### tickValues (optional) Type: `Array<*>` An array of values (not coordinates!) that where the lines should be shown. Similar to the `tickValues()` method of d3-axis. #### left (optional) Type: `number` Horizontal position of the grid lines in pixels. **Already set by default**, but can be overridden by the user. #### top (optional) Type: `number` Vertical position of the grid lines in pixels. **Already set by default**, but can be overridden by the user. #### width (optional) Type: `number` Width of the grid lines in pixels. **Already set by default**, but can be overridden by the user. #### height (optional) Type: `number` Height of the grid lines in pixels. **Already set by default**, but can be overridden by the user. #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `String` A class name to apply to the gridlines. #### style (optional) Type: `object` An CSS object that will style these gridlines. ## Polar Grids `CircularGridLines` allows you to draw circular grid lines. This might be useful for a polar scatterplot, as shown above, or a radar chart or any of a wide host of additional contexts. Usage example ``` ({ ...row, x: Math.cos(row.theta) * row.r, y: Math.sin(row.theta) * row.r }))}/> ``` It can often be useful to specify the x and y domains on the surrounding XYPLot. CircularGridLines accepts all of the same props as the cartesian grids, but also accepts two more: #### centerX (optional) Type: `number` The left-right value in coordinates of where the circles should be centered. #### centerY (optional) Type: `number` The top-bottom value in coordinates of where the circles should be centered. #### rRange (optional) Type:[`number`, `number`] This allows users to specify the exact pixel range over which they wish their rings to appear. #### style (optional) Type: `object` An CSS object that will style these gridlines. See [style](style.md) ================================================ FILE: docs/heatmap-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## HeatmapSeries: The heatmap series enables users to create a 2d binning of the plane. These series often come in useful in situations when you might be using a scatterplot, but have too many rows of data for the reader to easily understand what is going on. So arises HeatmapSeries! ```javascript ``` Another way to think of the heatmap, is as a 2D histogram, where each cell is colored by it's value rather than height-ed or width-ed. #### Color in heatmaps The Heatmap's color can be manipulated in two data driven ways, first by setting the setting colorRange on the series ```javascript ``` Which assumes that each row of data has a number specifying it's color attribute. Alternatively you can change the scale type of color to allow yourself to specify color of the cell directly: ```javascript ``` The color can also usefully be set using a color accessor, ```javascript d.color === 'bad' ? '#f00' : '#0f0'} data={[ {x: 1, y: 0, color: 'bad'}, {x: 1, y: 5, color: 'bad'}, {x: 1, y: 10, color: 'good'} ]}/> ``` Finally, the color could also be specified on the series itself, however that would probably not be the best use of a heatmap as it would color every cell in the series the same color. ## Data format Reference Like other series, it is required that the data be an array of objects, formatted like so: ```javascript const myData = [ {x: 1, y: 0, color: 10}, {x: 1, y: 5, color: 10}, {x: 1, y: 10, color: 6}, {x: 1, y: 15, color: 7}, {x: 2, y: 0, color: 12}, {x: 2, y: 5, color: 2}, {x: 2, y: 10, color: 1}, {x: 2, y: 15, color: 12}, {x: 3, y: 0, color: 9}, {x: 3, y: 5, color: 2}, {x: 3, y: 10, color: 6}, {x: 3, y: 15, color: 12} ] ``` Where x and y are required quantities and additional properties may be stapled on. #### x Type: `number` The x position in coordinates of the box to be used. #### y Type: `number` The y position in coordinates of the box to be used. #### color (optional) Type: `string|number` The color of a box in the series. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### color Type: `string|number` The color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md) #### className (optional) Type: `string` Provide an additional class name for the series. #### data Type: `Array` Array of data for the series. See above data format reference. #### fill Type: `string|number` The inner color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md) #### opacity Type: `string|number` The opacity for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### stroke Type: `string|number` The outer color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md) #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onValueClick (optional) Type: `function(d, {event})` `click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueRightClick (optional) Type: `function(d, {event})` `right-click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. ================================================ FILE: docs/hexbin-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## HexbinSeries The hexbin series enables the easy creation of aggregated and binned data. This can be useful if you have a lot of overlapping data or if you simply want to provide a courser representation of data to your user. Unlike many other series this one performs the aggregation computation, simply provide a scatterplot like collection of data (points in linear x and y space) and your off! Points are binned into hexagonal containers, which are then rendered as svg paths. These svg hexes can encode their counts through color and size! It can be particularly effective to pair this series with a [border](borders.md) element. ```javascript d.waiting} getY={d => d.eruptions} onMouseLeave={() => this.setState({hoveredNode: null})} height={300}> this.setState({hoveredNode: d})} colorRange={['white', 'black']} radius={radius} data={data}/> ``` ## API reference #### animation (optional) Type: `Boolean` See the [Animation](animation.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### countDomain (optional) Type: `Array of two numbers` Provide the specific counts to the hexagon binning between. If not provided defaults to [0, ]. #### colorRange (optional) Type: `Array of two strings` Provide the colors for hexagons to interpolate between. #### data Type: `Array` Array of data for the series. Follows the usual pattern of an array of objects formatted with x and y coordinates, [{x: 0, y: 0}, ...]. #### radius Type: `Number` The maximum size of the hexagon, specified in pixels. #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. These style elements are applied directly to each individual hexagon. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) for more information. #### sizeHexagonsWithCount Type: `Boolean` Size the hexagons based on the number of values in side of the hexagon. Ranges between [0, ]. #### xOffset (optional) Type: `Number` Default: `0` Size of aggregation offset form base value, this enables fine tuning along the x axis. #### yOffset (optional) Type: `Number` Default: `0` Size of aggregation offset form base value, this enables fine tuning along the y axis. ## Interaction handlers #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` It is very important to note that data point is specified in pixels NOT in data coordinates. This can have serious consequences for how interactivity works. See the HexHeatmap example above for a worked example. #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` It is very important to note that data point is specified in pixels NOT in data coordinates. This can have serious consequences for how interactivity works. See the HexHeatmap example above for a worked example. #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` It is very important to note that data point is specified in pixels NOT in data coordinates. This can have serious consequences for how interactivity works. See the HexHeatmap example above for a worked example. #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` It is very important to note that data point is specified in pixels NOT in data coordinates. This can have serious consequences for how interactivity works. See the HexHeatmap example above for a worked example. ================================================ FILE: docs/highlight.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Highlight The highlight component enables use interaction via direct manipulation of chart through dragging and brushing. This component is stateful and can maintain a notion of a dragged box. It can be applied either in two directions or in one! It is quite easy to drop this functionality into an existing chart, for example: ```jsx highlightPoint(d) ? '#EF5D28' : '#12939A'} data={data}/> this.setState({filter: area})} onDrag={area => this.setState({filter: area})}/> ``` An important point to notice here is that direction responsiveness (the thing that makes calling on brush and on drag return meaningful values) is OPT OUT. VERY IMPORTANT! See examples for more details. In drag mode (activated by including the prop drag) you are able to drag a selection box around in the chart space. When putting this shape you first execute a drag action to define the size of the box and then are able to move it around. See above and below for examples. When designing your listeners it is important to be mindful the lifecycle of this component as there are a lot of edge cases. To wit, if you NOT using drag mode then the life cycle will always be brushStart > brush > brushEnd. While if you are in drag mode it will be brushStart > brush > brushEnd when you are making the box and then dragStart > drag > dragEnd while dragging the box. The biggest gotchas revolve around click to clear type events. In order to implement this, make sure to include an on End listener to set update your state. In click events there isn't a middle state between start and end because your user does not move the mouse. Be aware! See the code for the examples for more details. It is important to note that brushing over non-continuous scales is not supported! Specifically this means that you can not brush over category or ordinal scales. ## API Reference #### className (optional) Type: `String` Add css class to Voronoi container #### drag (optional) Type: `Boolean` Enable dragging interactions #### enableX (optional) Type: `Boolean` Defaults to `true` Enable brushing and dragging in the x direction #### enableY (optional) Type: `Boolean` Defaults to `true` Enable brushing and dragging in the y direction #### highlightX (optional) Type: `String or Number` Defaults to left edge Position in x coordinate space to place the left edge of the highlight bar. #### highlightY (optional) Type: `String or Number` Defaults to top edge Position in y coordinate space to place the top edge of the highlight bar. #### highlightHeight (optional) Type: `Number` Defaults to full height The height of highlight bar in pixels. #### highlightWidth (optional) Type: `Number` Defaults to full width The width of highlight bar in pixels. #### onBrushStart (optional) Type: `Function` Function called on the start of brushing. #### onBrush (optional) Type: `Function` Function called on the start of brushing. #### onBrushEnd (optional) Type: `Function` Function called on the start of brushing. #### onDragStart (optional) Type: `Function` Function called on the start of dragging. #### onDrag (optional) Type: `Function` Function called on the start of dragging. #### onDragEnd (optional) Type: `Function` Function called on the start of dragging. ================================================ FILE: docs/hint.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Hint `Hint` is a simple component that shows tooltips inside the chart. `Hint` places itself to the place which is set by your data. In case a custom representation is needed, the component is also able to wrap custom JSX. Here is a short example: ```jsx

Value of hint

{myValue.x}

``` Hints can be placed in two ways a) around a data point in one of four quadrants (imagine the point bisected by two axes -vertical, horizontal- creating 4 quadrants around a data point). b) Pin to an edge of chart/plot area and position along that edge using data point's other dimension value. #### value Type: `Object` The data point to show the value at. Hint component will automatically find the place where the data point is and put the hint there. #### format (optional) Type: `function` Format function for a tooltip. Receives the data point, should return an array of objects containing `title` and `value` properties. Each item of an array is shown on a separate line by default. _Note: please pass custom contents in case if you need different look for the hint._ #### align (optional) Type: `Object` Default: `{horizontal: 'auto', vertical: 'auto'}` The way to align the hint inside the chart. Within the object, specify the horizontal alignment `(auto|left|right)` and the vertical alignment `(auto|top|bottom)`. For example, to see a "conventional" hint alignment: `{vertical: 'top', horizontal: 'left'}`. #### className (optional) Type: `String` A class name to apply to the hint container. #### style (optional) Type: `Object` You can pass a style object to your Hint component to apply your own styles. See [style](style.md) ```jsx ``` Style is a composite component, and individual parts of it can receive different parts. The different parts are: content, row, title, value. To style a specific part, you can pass an object to the property with that name. ```jsx ``` ================================================ FILE: docs/interaction.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Interaction Interaction in react-vis happens through _event handlers_ which are triggered by certain interactive events, such as mouse movement or clicks. These events can be implemented either at the XYPlot level or at the plot level: * At the plot level: this is for events that affect the whole chart. The mouse events that can be captured are: down, enter, leave, move. For instance, you can use `onMouseLeave` to reset the visualization when the user's mouse cursor is no longer on it. * At the series level, there are three kind of handlers. * Some series (arc, bar, heatmap, label, mark, rect) support interaction at the individual mark level. These series have onValueClick, onValueMouseOut and onValueMouseOver, which, in addition to passing the event, also pass the datapoint corresponding to the mark with which the user interacted. * The above series, and some others (area, line, polygon) support interaction at the series level. These series have handlers like onSeriesClick, onSeriesMouseOut, onSeriesMouseOver. Those handlers only pass the mouseevent that triggered them. * Finally, all series support onNearestX and onNearestXY. These two special handlers are triggered when the user moves their mouse on the plot area, and can access the datapoint of the nearest mark, in addition to the mouse event. * You can also interact with your plot through specialized components, such as the [highlight](highlight.md) for brushing and dragging or the [voronoi](voronoi.md) for mouse overs. ### What handlers are implemented by series type | Series | Proximity handlers (onNearestX, onNearestY) | series level handlers (onSeriesClick, onSeriesRightClick, onSeriesMouseOver, onSeriesMouseOut) | mark-level handlers (onValueClick, onValueRightClick, onValueMouseOver, onValueMouseOut) | |---------------------------------------|---------------------------------------------|------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | [ArcSeries](arc-series.md) | ✔︎ | ✔︎ | ✔︎ | | [AreaSeries](area-series.md) | ✔︎ | ✔︎ | | | [BarSeries](bar-series.md) | ✔︎ | ✔︎ | ✔︎ | | [ContourSeries](contour-series.md) | ✔︎ | | | | [HeatmapSeries](heatmap-series.md) | ✔︎ | ✔︎ | ✔︎ | | [HexbinSeries](hexbin-series.md) | ✔︎ | ✔︎ | ✔︎ | | [LabelSeries](label-series.md) | ✔︎ | ✔︎ | ✔︎ | | [LineSeries](line-series.md) | ✔︎ | ✔︎ | | | [LineMarkSeries](line-mark-series.md) | ✔︎ | ✔︎ | ✔︎ | | [MarkSeries](mark-series.md) | ✔︎ | ✔︎ | ✔︎ | | [PolygonSeries](polygon-series.md) | ✔︎ | ✔︎ | | | [RectSeries](rect-series.md) | ✔︎ | ✔︎ | ✔︎ | | [WhiskerSeries](whisker-series.md) | ✔︎ | ✔︎ | ✔︎ | How to read this table: For some series types (Arc, Bar, Rect, Label, Mark) - onValueClick, onValueMouseOut and onValueMouseOver handlers will work at the mark type. When the user clicks on the series, or moves their mouse on our out of it, an event handler will be fired and will pass the datapoint corresponding to the mark that the user interacted with. The other series types (area, line, polygon) have an onSeriesClick, onSeriesMouseOut and onSeriesMouseOver respectively. These event handler will work at the series level and will not pass a specific datapoint. In all cases, onNearestX and onNearestXY can be implemented at the series level, but when fired, they will also pass a specific datapoint. ### Note - the contour series doesn't support interaction other than onNearestX or onNearestXY - whenever the datapoint-level handlers are supported, they can also catch all the events happening at the series level. ## API ### XYPlot event handlers #### onMouseDown Type: `function` Default: none This event handler is triggered whenever the mousebutton of the user is down while their mouse cursor is in the plot area. It passes a mouse event. #### onMouseUp Type: `function` Default: none This event handler is triggered whenever the user release the mouse button while their mouse cursor is in the plot area. It passes a mouse event. #### onMouseEnter Type: `function` Default: none This event handler is triggered whenever the mouse of the user enters the plot area. It passes a mouse event. #### onMouseLeave Type: `function` Default: none This event handler is triggered whenever the mouse of the user exits the plot area. It passes a mouse event. #### onMouseMove Type: `function` Default: none This event handler is triggered whenever the mouse of the user moves while in the plot area. It passes a mouse event. #### onTouchStart Type: `function` The event handler is triggered whenever the finger of the user first touches the plot area. It passes a touch event. #### onTouchMove Type: `function` This event handler is triggered whenever the finger of the user moves while in the plot area. It passes a touch event. #### onTouchEnd Type: `function` This event handler is triggered when a touch point of the user lifts off the plot area. It passes a touch event. #### onTouchCancel Type: `function` This event handler is triggered when a touch point of the user has been disrupted in an implementation-specific manner ### Series event handlers #### onNearestX Type: `function` Default: none This handler fires when the user moves their mouse somewhere on the plot. The handler fires a function that takes two argument: the datapoint with the x value closest to the cursor or touch point, and a second object containing: the `innerX` value (x coordinates of the cursor relative to the left of the plot), `index` (position of this datapoint in the dataset, where 0 is the first datapoint, 1 is the second, etc) plus the actual event as `event`. onNearestX is at the series level, not at the plot level. If you attach onNearestX to several series, each time the user moves their mouse or touch point, each onNearestX handler will be triggered once with the closest mark of each series. ```jsx { // does something on mouseover // you can access the value of the event }} ``` #### onNearestXY Type: `function` Default: none This handler is nearly identical to `onNearestX`. The difference is that it will return datapoint corresponding to the mark closest to the cursor or touch point, not just the one with the closest x coordinate. onNearestXY will supersede onNearestX, so if both exist for the same series, only onNearestXY will be fired. This handler fires when the user moves their mouse or touch point somewhere on the plot. The handler fires a function that takes two argument: the datapoint which is closest to the cursor or touch point, and a second object containing: the `innerX` and `innerY` value (x, y coordinates of the cursor or touch point relative to the top left of the plot), `index` (position of this datapoint in the dataset, where 0 is the first datapoint, 1 is the second, etc) plus the actual event as `event`. onNearestXY is at the series level, not at the plot level. If you attach onNearestX to several series, each time the user moves their mouse or touch point, each onNearestX handler will be triggered once with the closest mark of each series. ```jsx { // does something on mouseover // you can access the value of the event }} ``` #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onValueClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onValueRightClick, it doesn't pass a specific datapoint. ```jsx { // does something on right click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` ## Interaction strategies and examples ### Interaction and hints or crosshairs This was the first use case we built for interaction. We have one line chart on a plot, we want to show the value of the nearest mark to the cursor, without requiring the user to actually be over the plot proper. ```jsx const DATA = [ [ {x: 1, y: 10}, {x: 2, y: 7}, {x: 3, y: 15} ], [ {x: 1, y: 20}, {x: 2, y: 5}, {x: 3, y: 15} ] ]; export default class DynamicCrosshair extends React.Component { constructor(props) { super(props); this.state = { crosshairValues: [] }; } render() { return ( this.setState({crosshairValues: []})} width={300} height={300}> this.setState({crosshairValues: DATA.map(d => d[index])})} data={DATA[0]}/> ); } } ``` A few notes: - here, we are using the state to record the position of the crosshair. So, we're using the class syntax vs the React functional API. - We use onMouseLeave, at the plot level, to reset the visualization if the user's mouse cursor leaves the plot area. - There are 2 LineSeries components, but we only outfit one of them with a onNearestX handler. - In that handler, we get the datapoint value as first argument, and the index part of the object as 2nd argument. Actually, we only care about that index value. We're using it to generate values for the crosshair. - Having the handler on the 2nd LineSeries would have the exact same effect. We would change the state twice, to the same value. This is why it's not useful to call it several times. ### Mousing over near scatterplot marks ```jsx class ScatterPlotOnNearestXY extends Component { constructor() { super(); this.state = {index: null}; } render() { const {index} = this.state; const data = scatterPlotData.map((d, i) => ({...d, color: i === index ? 1 : 0})); return this.setState({index: null})} > this.setState({index})} /> } } ``` Notes: - Here, we could have used the onMouseOver prop but that would require the user to move their mouse on each dot. But those dots are small! it would be more comfortable to select whichever dot is the nearest from the cursor. - Again, we're going to use the state for interaction, so we use the class syntax. - Each time we're rendering this component, we're going to regenerate a dataset that takes the state into account. The selected datapoint will have its color property set to 1, for others it will be 0. - the colorDomain of XYPlot will be set to [0, 1]. This is because else, when no point is selected, the colorDomain would be [0, 0] and all dots would have the color of the higher bound (light orange). - As above, when the mouse leaves the plot, the state of the visualization is reset. - As above, in the onNearestXY handler, we're actually only interested in the index part of the second argument. ### Mousing over series - the 2nd series group option ```jsx class LineChartMouseOverSeries extends Component { constructor() { super(); this.state = {index: null}; } render() { const {index} = this.state; return this.setState({index: null})} > {lineData.map((d, i) => ())} {lineData.map((d, i) => ( this.setState({index: null})} onSeriesMouseOver={() => this.setState({index: i})} strokeWidth={10} stroke="transparent"} />))} } } ``` Here, we are going to explore 2 strategies to handle highlighting one line series among several on screen. We could do that with a simple onSeriesMouseOver but, again, that would require mousing over exactly on a line series, which are notoriously narrow. Instead, we create a second set of LineSeries whose only purpose is to handle interaction. That second set of LineSeries is thicker (here, the stroke width is set at a generous 10px) and is also transparent. In the embedded example, I'm highlighting each lineSeries as it is moused over, but I'm not reflecting this on the snippet of code. Here, lineData is an array of arrays. The LineSeries are created by mapping over that array, which is quite common. When creating the LineSeries for mouseover, I write: ```jsx {lineData.map((d, i) => ( this.setState({index: i})} ``` In the onSeriesMouseOver handler, I don't pass any argument in the handler proper, but I'm using the index of the array from the mapping method. So, for the first LineSeries, that value is 0, then 1, then 2. ### Mousing over series - the extra scatterplot option ```jsx const allData = lineData.reduce((prev, curr, i) => { return [...prev, ...curr.map((d) => ({...d, index: i}))] }, []); class LineChartMouseOverXY extends Component { constructor() { super(); this.state = {index: null}; } render() { const {index} = this.state; return this.setState({ highlightedSeries: null, pointUsed: null })} > {lineData.map((d, i) => ())} this.setState({index})} /> } } ``` Here's a slightly different strategy to obtain a similar effect. Prior to rendering this component, I've created an "allData" dataset which is a flat array of all the datapoints in the 3 lineSeries' datasets, only we've added to each of these datapoints an extra property, index, which will be 0, 1 or 2 depending from which of the 3 dataset each datapoint comes. So allData will be an array of objects of the form: {x: ..., y: ..., index: ...}. Index, the last property, will be ignored by scales and won't affect the rendering in any way. Then, on top of our 3 lineSeries, we're adding a MarkSeries with an onNearestXY handler, just as 2 examples above. The first argument of the onNearestXY handler is the datapoint proper. So again: an object of the form: {x:... ,y:..., index:...}. While the index property was not taken into account for rendering, it is definitely part of the first argument. Actually, it's the only part that we care about, so we just write ({index}) =>. In the embedded version of this example, I'm highlighting the mark that triggers onNearestXY, however I haven't added this in the snippet of code above. ### Linked Charts ```jsx class LinkedCharts extends Component { constructor() { super(); this.state = {index: null}; this.handleMouseOver = this.handleMouseOver.bind(this); } handleMouseOver(index) { this.setState({index}); } render() { const {index} = this.state; return (
{lineData.map((d, i) => (
))}
); } } function LineChart({data, index, handleMouseOver}) { return ( handleMouseOver(null)} > handleMouseOver(index)} /> {index === null ? null : } {index === null ? null : } ); } ``` More often than not, you want to be able to handle an action that happened in one of the charts and reflect it in different parts of your app, i.e. outside the chart. This is a common React problem, and we're going to deal with it in a proven React way - with a container that has a state, and which will pass this state and actions to presentation components. These presentation components will be represented according to the state of that container, and will be able to fire actions that will impact it. Our container is a simple class. We create this method, handleMouseOver, that we'll have to bind to the container because we're going to pass it down. When we're doing that, we'll want that method to affect the state of the container. Our container will create three "LineChart" components, to which it will pass the dataset of a line (i.e. an array of {x, y} objects, the contents of the state - which is simply an index value, if there has been a mouseOver action, or null if there hasn't been - and our handleMouseOver method. Inside the LineChart components, we'll use onMouseLeave and onNearestX to trigger the handler, as in the examples above. So, if the user mouses over any of these line charts, this changes the state of the container above, which will trickle down to all 3 components again. Finally, we create a dynamic line and dot gizmo to represent where the user is mousing over. We can easily generate a dataset for that, since index is the x value of where both the line and the dot should be. ================================================ FILE: docs/label-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## LabelSeries: Sometimes you just need to write on your data, and labelSeries has your back. This simple series has a similar API as the markSeries except it adds a label property to each of the rows. This label is then rendered as part of the svg tree. ```javascript ``` This can be useful for annotating points, as above, or in labeling wedges as (as in the radial chart). ## Data format Reference Like other series the `labelSeries` requires the data be formatted as an array of objects with several required keys and several options ones. Here's an example ```javascript const myData = [ {x: 0, y: 0, label: 'woah!', style: {fontSize: 10}}, {x: 1, y: 0, label: 'dope city', yOffset: 5}, {x: 0, y: 1, label: 'cool Dog friend', xOffset: 5, rotation: 34} ] ``` The above would render three points with labels as suggested! #### x Type: `number` The x position in coordinates of the label. #### y Type: `number` The y position in coordinates of the label. #### label Type: `string` The actual text to be offered. #### xOffset Type: `number` A number in pixels for the label to be offset from the x position specified on the row. #### yOffset Type: `number` A number in pixels for the label to be offset from the y position specified on the row. #### rotation Type: `number` Number in degrees for the text to be rotated about its xy point. ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### allowOffsetToBeReversed (optional) The allows the offset specified on the data rows to flipped if the label is too close to an edge. This allows you to make sure your labels never get randomly clipped by going offscreen. #### className (optional) Type: `string` Provide an additional class name for the series. #### data Type: `Array` Array of data for the series. See above data format reference. #### style Type: `object` SVG text objects (which is what the labelSeries is made up of) accept a ton of different styles, so rather than prescribe every single one we just accept a general grab bag pf the styles. check out the [mozilla](https://developer.mozilla.org/en-US/docs/Web/SVG/Element/text) page for more details. #### labelAnchorX Type: `string` This attribute is used to align (start-, middle- or end-alignment) the label text horizontally relative to the data point. (Sets the text-anchor attribute for the element https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/text-anchor) #### labelAnchorY Type: `string` This attribute is used to align the label text vertically relative to the datapoint. (Sets the dominant-baseline attribute for the element https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline) ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onValueClick (optional) Type: `function(d, {event})` `click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueRightClick (optional) Type: `function(d, {event})` `right-click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. ================================================ FILE: docs/legends.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Legends Currently following types of legends are supported: - for colors: * DiscreteColorLegend (for a fixed number of colors, good for series); * SearchableDiscreteColorLegend) (same as DiscreteColorLegend, but with search on top); * ContinuousColorLegend (for gradually changing colors); - for sizes: * ContinuousSizeLegend (for gradually changing size). ## Color legends ### DiscreteColorLegend #### items (required) Type: `Array` Array of items that should be shown on the legend. The array should consist from either objects (`title`, optional `color`, optional `strokeDasharray`, optional `strokeStyle`, optional `strokeWidth`, and optional `disabled` flag) or strings (treated as titles). The stroke properties should match those in your series (see [line series](line-mark-series.md)) #### orientation (optional) Type: `(vertical|horizontal)` Default: `'vertical'` String either `horizontal` or `vertical` representing which direction the legend elements are rendered. #### onItemClick Type: `function(Object, number): void` Default: noop Click callback for the item in the list. Gets the clicked item and its index as parameters. #### onItemMouseEnter Type: `function` Default: noop This handler is triggered either when the user's mouse enters a legend item. The handler passes three arguments, the corresponding item, legend index and the actual event. ```jsx { // does something on mouse enter // you can access the value of the event }} ``` #### onItemMouseLeave Type: `function` Default: noop This handler is triggered either when the user's mouse leaves a legend item. The handler passes three arguments, the corresponding item, legend index and the actual event. ```jsx { // does something on mouse leave // you can access the value of the event }} ``` #### width Type: `number` Outer width of the component. Default width is not set. #### height Type: `number` Outer height of the component. Default is not set, the component stretches with the items added into it. ### SearchableDiscreteColorLegend `SearchableDiscreteColorLegend` allows the user to perform search among the items. Its API includes the API of `DiscreteColorLegend`, but adds several search-related items: #### searchText (optional) Type: `string` Default: `''` #### searchFn (optional) Type: `function(Array, string):Array` Function that is should filter out the unnecessary items by the given initial array of items and the search string. By default the function returns an array of items which titles contain a string. #### searchPlaceholder (optional) Type: `string` Default: `''` Placeholder for an search input field. #### onSearchChange (optional) Type: `function(string):void` Event handler for the change of the input field. The handler is triggered with the search field value as a parameter. #### onItemMouseEnter Type: `function` Default: noop This handler is triggered either when the user's mouse enters a legend item. The handler passes three arguments, the corresponding item, legend index and the actual event. ```jsx { // does something on mouse enter // you can access the value of the event }} ``` #### onItemMouseLeave Type: `function` Default: noop This handler is triggered either when the user's mouse leaves a legend item. The handler passes three arguments, the corresponding item, legend index and the actual event. ```jsx { // does something on mouse leave // you can access the value of the event }} ``` ### ContinuousColorLegend #### startTitle Type: `string|number` The title that is shown in the beginning of the legend. #### midTitle Type: `string|number` The title that is show in the middle of the legend. #### endTitle Type: `string|number` The title that is show in the end of the legend. #### startColor (optional) Type: `string` The initial color of the bar #### endColor (optional) Type: `string` The end color of the bar. #### midColor (optional) Type: `string` The middle color of the bar. #### width (optional) Type: `number` Outer width of the component. #### height (optional) Type: `number` Outer height of the component. ## Size Legends ### ContinuousSizeLegend #### startTitle Type: `string|number` The title that is shown in the beginning of the legend. #### endTitle Type: `string|number` The title that is show in the end of the legend. #### startSize (optional) Type: `number` Default: `2` The initial size of the circles in the legend. #### endSize (optional) Type: `number` Default: `20` The end size of the circles in the legend. #### circlesTotal (optional) Type: `number` Default: `10` Total amount of circles displayed in the legend #### width (optional) Type: `number` Outer width of the component. #### height (optional) Type: `number` Outer height of the component. ================================================ FILE: docs/line-mark-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # LineMarkSeries The Line Mark series is a combination of a LineSeries and a MarkSeries: under the hood, it creates both a LineSeries and a MarkSeries and passes them all of its properties. ## Data format reference #### x Type: `string|number|date` x will be used to determine the x position of each point on the line. The format of x depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### y Type: `string|number|date` y will be used to determine the y position of each point on the line. The format of y depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### color (optional) Type: `string|number` The color of the line and that of the marks. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. #### opacity (optional) Type: `string|number` Opacity of the individual box to be rendered. By default opacity is scaled by `literal`, so the exact value provided will be used. This property can also be defined on the series level. #### stroke (optional) Type: `string|number` Stroke affects both the color of the line, and the outline of the marks. When this value is not provided, the color attribute is used instead. This property can also be defined on the series level. #### fill (optional) Type: `string|number` The color of the inside of the marks. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. #### size (optional) Type: `string|number` Default: `5` The size of each of the marks. ## API Reference #### curve (optional) Type: `string|function` Default: `null` Apply the provided or named curve function from the D3 shape library to smooth the line series plot, see [the D3 documentation](https://github.com/d3/d3-shape#curves) for function names and instructions. Providing the function, not the name, will require importing the d3-shape package in order to configure it: ```javascript // Setting up with only a name const stringCurveProp = ; const configuredCurve = d3Shape.curveCatmullRom.alpha(0.5); const funcCurveProp = ; ``` Some, but not all line interpolations have the resulting curve going through the original coordinates of its data points. If not, the LineSeries part of the LineMarkSeries will be detached from the MarkSeries part. #### fill (optional) Type: `string|number` The inner color for all the marks in the series, this property will be over-ridden by fill specified in the data attribute. See [colors](colors.md) #### getNull (optional) Type: `function` Default: `null` A function that will be invoked for each data element that will return a boolean that specifies if the data point should be drawn or not. For more information see [the D3 documentation](https://github.com/d3/d3-shape#line_defined). ```javascript // Only draw datapoints where the y value is not equal to null d.y !== null} data={data} /> ``` #### stroke (optional) Type: `string|number` Default: see [colors](colors.md) A color for the series. Will override color if both are provided. ##### strokeDasharray (optional) Type: `string` Specify a custom `stroke-dasharray` attribute which controls the pattern of dashes and gaps used to stroke paths. This will only affect the LineSeries part of the LineMarkSeries. ##### strokeStyle (optional) Type: `string` If set to `dashed`, your series will use dashed lines. If set to `solid` or unspecified, your series will use solid lines. See `strokeDasharray` for specifying a custom stroke dash-array value. This will only affect the LineSeries part of the LineMarkSeries. ##### strokeWidth (optional) Type: `string|number` Specifies the width of the line for the series. By default, this is determined by react-vis css (2px). This will affect both the thickness of the Line and the Marks. #### style (optional) Type: `object` An object which holds CSS properties that will be applied to the SVG element(s) rendered by the series. See [style](style.md)This allows you to style series beyond the other explicitly defined properties and without having to use CSS classnames and stylesheets. ```jsx ``` `LineMarkSeries` being a composite component (a mix of [LineSeries](line-series.md) and [MarkSeries](mark-series.md)), there are two additional property in the `style` object: `line` and `mark`, which allow you to specify a style for the line or the mark part of the line mark series, respectively. ```jsx ``` ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onValueMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on right click // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ================================================ FILE: docs/line-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # LineSeries/LineMarkSeries react-vis offers two different types of LineSeries, one that renders SVG and one that renders Canvas. The SVG mode is accessed by using the normal `LineSeries`, just as above, while the Canvas mode is used by simply calling `LineSeriesCanvas` instead of `LineSeries`. -**NOTE**: using the Canvas version of this layer disables animation ## Data format reference #### x Type: `number` Left-to-right position of marks in the series. #### y Type: `number` Top-to-bottom position of the top edge of the series. ## API Reference #### color (optional) Type: `string|number` Default: see [colors](colors.md) Color of the line series. By default, you can pass a literal color to the series (i.e. "red" or "#f70"). You can also define a color scale at the top level, and pass a number which will be interpolated by the scale. If nothing is provided, lineSeries will be colored according to react-vis default scale. #### curve (optional) Type: `string|function` Default: `null` Apply the provided or named curve function from the D3 shape library to smooth the line series plot, see [the D3 documentation](https://github.com/d3/d3-shape#curves) for function names and instructions. Providing the function, not the name, will require importing the d3-shape package in order to configure it: ```javascript // Setting up with only a name const stringCurveProp = ; const configuredCurve = d3Shape.curveCatmullRom.alpha(0.5); const funcCurveProp = ; ``` #### data Type: `Array` Array of data for the series. See above data format reference. #### getNull (optional) Type: `function` Default: `null` A function that will be invoked for each data element that will return a boolean that specifies if the data point should be drawn or not. For more information see [the D3 documentation](https://github.com/d3/d3-shape#line_defined). ```javascript // Only draw datapoints where the y value is not equal to null d.y !== null} data={data} /> ``` #### opacity (optional) Type: `number` Default: `1` Opacity of the area chart from 0 (transparent) to 1 (opaque). #### stroke (optional) Type: `string|number` Default: see [colors](colors.md) A color for the series. Will override color if both are provided. ##### strokeDasharray (optional) Type: `string` Specify a custom `stroke-dasharray` attribute which controls the pattern of dashes and gaps used to stroke paths. For the canvas version of this series, this should be an array of values, ala [7, 5]. ##### strokeStyle (optional) Type: `string` If set to `dashed`, your series will use dashed lines. If set to `solid` or unspecified, your series will use solid lines. See `strokeDasharray` for specifying a custom stroke dash-array value. ##### strokeWidth (optional) Type: `string|number` Specifies the width of the line for the series. By default, this is determined by react-vis css (2px). #### style (optional) Type: `object` An object which holds CSS properties that will be applied to the SVG element(s) rendered by the series. This allows you to style series beyond the other explicitly defined properties and without having to use CSS classnames and stylesheets. See [style](style.md) ```jsx ``` ### Interaction handlers Note - interacting with a line may be difficult especially with the standard width. To address that, consider: - the proximity handlers - onNearestX, onNearestXY; - increasing the width of your line to make it easier to reach with the mouse, - creating a near-transparent line series with extra width to catch mouse events. #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a LineSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a LineSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx LineSeries ... onSeriesMouseOut={(event)=>{ // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a LineSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a LineSeries, and provides the corresponding event. See [interaction](interaction.md) ```jsx { // does something on click // you can access the value of the event }} ``` ================================================ FILE: docs/mark-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## MarkSeries & MarkSeriesCanvas The MarkSeries allows users to embed discrete information in pairs of continuous variables, that is make scatterplots. Deploying a MarkSeries is super easy: ```javascript render() { return ( ); ``` Just like other series, MarkSeries expects its data to be formatted as an array of objects, like so: ```javascript const myData = [ {x: 1, y: 10, size: 30}, {x: 1.7, y: 12, size: 10}, {x: 2, y: 5, size: 1}, {x: 3, y: 15, size: 12}, {x: 2.5, y: 7, size: 4} ] ``` react-vis offers two different types of MarkSeries, one that renders SVG and one that renders Canvas. The SVG mode is accessed by using the normal `MarkSeries`, just as above, while the Canvas mode is used by simply calling `MarkSeriesCanvas` instead of `MarkSeries`. -**NOTE**: using the Canvas version of this layer disables animation Mark series can usefully be deployed with voronois for fast and accurate mouse overs! ## Data format reference #### x Type: `string|number|date` x will be used to determine the x position of each mark. The format of x depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### y Type: `string|number|date` y will be used to determine the y position of each mark. The format of y depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### color (optional) Type: `string|number` The color of the marks. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. #### opacity (optional) Type: `string|number` Default: `1` Opacity of the individual marks, from 0 (transparent) to 1 (opaque). By default opacity is scaled by `literal`, so the exact value provided will be used. This property can also be defined on the series level. #### stroke (optional) Type: `string|number` The color of the outline of the marks. When this value is not provided, the color attribute is used instead. This property can also be defined on the series level. #### fill (optional) Type: `string|number` The color of the inside of the marks. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. #### size (optional) Type: `string|number` Default: `5` The size of each of the marks. ## API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### color (optional) Type: `string|number` Exact color for all series points or a series object. #### data Type: `Array` Array of data for the series. #### fill (optional) Type: `string|number` The inner color for all the marks in the series, this property will be over-ridden by fill specified in the data attribute. See [colors](colors.md) #### opacity (optional) Type: `string|number` Exact opacity for all series points in pixels or a series object, from 0 (transparent) to 1 (opaque) #### size (optional) Type: `string|number` Exact size for all series points in pixels or a series object. #### stroke (optional) Type: `string|number` Default: see [colors](colors.md) A color for the outline of the marks. Will override color if both are provided. #### strokeWidth (optional) Type: `string|number` Default: `1` The width of the outline of the marks. ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onValueMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onClick, it doesn't pass a specific datapoint. ```jsx { // does something on right click // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` ================================================ FILE: docs/parallel-coordinates.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Parallel Coordinates Parallel Coordinates provide a robust method for displaying many variables simultaneously. It allows for rapid at-a-glance comparisons across a bunch of dimensions. These graphics can effectively be used either with several data rows on a single chart (as below) or as a small multiple. For more information, check out the [Wiki](https://en.wikipedia.org/wiki/Parallel_coordinates), it's got some really neat examples. Imagine you have a trio of models of cars that you are trying to compare. You're being data driven so you've collected a number of measurements based on a variety of values. You know basic facts about your variables, eg the interior rating a car can have is 7 and the minimum 1. You can use all this information to produce the above chart! Viola! Informed consumer. Just like every other chart and series ParallelCoordinates expects an array of data, each row or object corresponds to a line or polygon (depending on how you have your chart styled). A key caveat for this chart type is that react-vis can not simply infer the variables from each data object that you wish to plot, so we need you to tell us! So enters the domains prop, an array of object specifying the order and behavior of each of the variables. So you have to tell react-vis a little more to get started, but you get a lot more expressiveness. Let's consider some code. You might provide the following object as props to the parallel coordinates chart: ```javascript const PARALLEL_COORDINATES_PROPS = { data: [{ neatExplosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 }], domains: [ {name: 'nice', domain: [0, 100]}, {name: 'explosions', domain: [6.9, 7.1], getValue: d => d.neatExplosions}, {name: 'wow', domain: [0, 11]}, {name: 'sickMoves', domain: [0, 20]} ], height: 300, width: 400 }; ``` In such a case, there would be ONE polygon rendered for four variables (nice/explosions/wow/sickMoves), because those values are listed in the domains prop. The ParallelCoordinates also features a stateful brushing mode in which your user can brush and drag on the the chart to selected and unselected lines. See the [highlight](highlight.md) component for more details. ## API Reference #### data Type: `arrayOf(Objects)` Each object must have keys for each of the intended display domains (eg in the above example any input row should have at neatExplosions/wow/dog/sickMoves/nice). Additional a style prop can be provided on the row of the data itself to style the line series that it corresponds to, for instance here's one of the rows from the car example: ```javascript { name: 'Honda', mileage: 8, price: 6, safety: 9, performance: 6, interior: 3, warranty: 9, style: { strokeWidth: 3, strokeDasharray: '2, 2' } } ``` It is not necessary to provide style, but it can be helpful! #### domains Type: `arrayOf(Objects)` The domains allow the user to specify the nature of the variables being plotted. This information is captured in an object formatted like: ```javascript PropTypes.shape({ name: PropTypes.string.isRequired, getValue: PropTypes.func, domain: PropTypes.arrayOf([PropTypes.number]).isRequired, tickFormat: PropTypes.func }) ``` Let's looks at each member of the object - name: generates a member of a labelSeries that shows at the end of the corresponding axis - getValue: an accessor function that grabs a value from the row being accessed, if this is not provided a default one that uses the name property is used. - domain: a pair of numbers that are interpolated between. Setting these values correctly is essential for making your graphic legible! Because it is often the case that there will only be one or two data rows in a parallel coordinates, react-vis requires the user to specify the exact domain for each variable. Without which we would be unable to plot the variables well. - tickFormat: allows the user to provide a formatting function for prettifying the way that axis interpolates between the domain values. #### width Type: `number` Width of the component. #### height Type: `number` Height of the component. #### margin (optional) Type: `Object` Default: `{left: 40, right: 10, top: 10, bottom: 40}` Margin around the chart. #### brushing Type: `Boolean` Default: false Enable stateful brushing on parallel coordinates #### style (optional) Type: `object` An object that contains CSS properties with which the axis component can be entirely re-styled. As the ParallelCoordinates is composite of several composite elements, it is possible to provide style objects for any and all parts of the tree. See [style](style.md) Most generally, there are three top level components `axes`, `labels`, and `lines`. These in turn lead to their corresponding to style objects. As an example, here is the default style object for the ParallelCoordinates: ```jsx ``` If you are using the stateful brushing mode and have filtered out a line then, in addition to the previous styles that were applied to a particular line, deselectedLineStyle will also be applied. #### animation (optional) Type: `boolean|Object` Please refer to [Animation](animation.md) doc for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### colorType (optional) Type: `string` Specify the type of color scale to be used on the parallel coordinates chart, please refer to [Scales and data](scales-and-data.md) for more information. #### showMarks (optional) Type: 'boolean' Specific whether or not to show the marks on the vertices of the lines #### tickFormat (optional) Type: 'function' Specify the tick format for all axes. Will be over-ridden by tickFormats specified on single domains. ================================================ FILE: docs/polygon-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## PolygonSeries: The polygon series allows users to specify arbitrary polygons in coordinates. This may seem un-useful, but it allows for easy creation of radar charts, fancy mark series dots, and any variety of additional things you might need polygons for! ```javascript ``` Each series corresponds to exactly **one** svg path. It is perfectly okay to many series to express many polygons! ## Data format Reference Like other series, it is required that the data be an array of objects, formatted like so: ```javascript const myData = [ {x: 0, y: 0}, {x: 1, y: 0}, {x: 0, y: 1} ] ``` Which would render a triangle. #### x Type: `number` The x position in coordinates of the box to be used. #### y Type: `number` The y position in coordinates of the box to be used. ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### color Type: `string` The color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md) #### data Type: `Array` Array of data for the series. See above data format reference. #### style Type: `object` Paths accept a ton of different styles, so rather than prescribe every single one we just accept a general grab bag pf the styles. check out the [w3](https://www.w3schools.com/graphics/svg_path.asp) page for more details and the [style] documentation (style.md). ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick (optional) Type: `function(d, {event})` `click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. See [interaction](interaction.md) #### onSeriesMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an #### onSeriesMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. See [interaction](interaction.md) object with the only `event` property. See [interaction](interaction.md) #### onSeriesRightClick (optional) Type: `function(d, {event})` `right-click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. See [interaction](interaction.md) ================================================ FILE: docs/presentation.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # React-vis __React-vis__ is a React visualization library. It's been designed with the following principles in mind: ## React-friendly: React-vis components are designed to work just like other React components. They have properties, children and callbacks. They can be composed. If you can work with React components, you can work with React-Vis. ## High-level and customizable: You can create complex charts with a minimum amount of code and sensible defaults, however, you can also customize every aspect of your chart. ## Expressive: React-vis handles a great number of charts, from area charts to treemaps; ## Industry-strong: React-vis was built to support the many internal tools at Uber. ================================================ FILE: docs/radar-chart.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Radar Charts Radar charts provide a cute method for displaying many variables simultaneously. It allows for rapid at-a-glance comparisons across a bunch of dimensions. These graphics can effectively be used either with several data rows on a single chart (as below) or as a small multiple. For more information, check out the [Wiki](https://en.wikipedia.org/wiki/Radar_chart), it's got some really neat examples. Imagine you have a trio of models of cars that you are trying to compare. You're being data driven so you've collected a number of measurements based on a variety of values. You know basic facts about your variables, eg the interior rating a car can have is 7 and the minimum 1. You can use all this information to produce the above chart! Viola! Informed consumer. Just like every other chart and series RadarChart expects an array of data, each row or object corresponds to a line or polygon (depending on how you have your chart styled). A key caveat for this chart type is that react-vis can not simply infer the variables from each data object that you wish to plot, so we need you to tell us! So enters the domains prop, an array of object specifying the order and behavior of each of the variables. So you have to tell react-vis a little more to get started, but you get a lot more expressiveness. Let's consider some code. You might provide the following object as props to the radar chart: ```javascript const RADAR_PROPS = { data: [{ neatExplosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 }], domains: [ {name: 'nice', domain: [0, 100]}, {name: 'explosions', domain: [6.9, 7.1], getValue: d => d.neatExplosions}, {name: 'wow', domain: [0, 11]}, {name: 'sickMoves', domain: [0, 20]} ], height: 300, width: 400 }; ``` In such a case, there would be ONE polygon rendered for four variables (nice/explosions/wow/sickMoves), because those values are listed in the domains prop. The radar chart accepts children if you wish to provide additional components, such as CircularGridLines. ## API Reference #### data Type: `arrayOf(Objects)` #### domains Type: `arrayOf(Objects)` The domains allow the user to specify the nature of the variables being plotted. This information is captured in an object formatted like: ```javascript PropTypes.shape({ name: PropTypes.string.isRequired, getValue: PropTypes.func, domain: PropTypes.arrayOf([PropTypes.number]).isRequired, tickFormat: PropTypes.func }) ``` Let's looks at each member of the object - name: generates a member of a labelSeries that shows at the end of the corresponding axis - getValue: an accessor function that grabs a value from the row being accessed, if this is not provided a default one that uses the name property is used. - domain: a pair of numbers that are interpolated between. Setting these values correctly is essential for making your graphic legible! Because it is often the case that there will only be one or two data rows in a radar chart, react-vis requires the user to specify the exact domain for each variable. Without which we would be unable to plot the variables well. - tickFormat: allows the user to provide a formatting function for prettifying the way that axis interpolates between the domain values. #### width Type: `number` Width of the component. #### height Type: `number` Height of the component. #### margin (optional) Type: `Object` Default: `{left: 40, right: 10, top: 10, bottom: 40}` Margin around the chart. #### style (optional) Type: `object` An object that contains CSS properties with which the axis component can be entirely re-styled. As the RadarChart is composite of several composite elements, it is possible to provide style objects for any and all parts of the tree. See [style](style.md) Most generally, there are three top level components `axes`, `labels`, and `polygons`. These in turn lead to their corresponding to style objects. As an example, here is the default style object for the RadarChart: ```jsx ``` #### animation (optional) Type: `boolean|Object` Please refer to [Animation](animation.md) doc for more information. #### hideInnerMostValues (optional) Type: `boolean` defaults to true Whether or not to hide the inner most tick values of the radar chart. This attempts to ensure that the ticks do not run over each other. #### className (optional) Type: `string` Provide an additional class name for the series. #### colorType (optional) Type: `string` Specify the type of color scale to be used on the radar chart, please refer to [Scales and data](scales-and-data.md) for more information. #### tickFormat (optional) Type: 'function' Specify the tick format for all axes. Will be over-ridden by tickFormats specified on single domains. #### startingAngle (optional) Type: `number` The angle of the first axis in radians. Defaults to PI / 2. #### renderAxesOverPolygons (optional) Type: `boolean` By default, the axes are rendered beneath the data (polygons) on the radar chart. Setting this to true will reverse the rendering, drawing the axes on top. This can be useful if you have a lot of data or your polygons have high opacity. #### onValueMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points, where each data point is a point on the radar chart. First argument received, `d`, is the relevant data point, including the `domain`, `name`, and `value` of the data point. The second argument is the `event` property. #### onValueMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onSeriesMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onSeriesMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. ================================================ FILE: docs/radial-chart.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Radial chart `RadialChart` is responsible for creating pie and donut charts. While this kind of chart is easy to overlook as insignificant, intentionally confusing, or almost always replaceable with a treemap; they can be useful for quickly showing small groups. People don't understand angles very well [(such is our biology)](https://www.interaction-design.org/literature/book/the-encyclopedia-of-human-computer-interaction-2nd-ed/data-visualization-for-human-perception), but over the last hundred years we have seen a lot of pie charts! This has caused us to become intimately familiar with them. We can leverage this familiarity to quickly transmit information to our reader. The best type of information to display in this way (in our opinion) is groups of less 6 or so. More than that becomes pretty hard to compare and the reader just sees visual noise. The radial chart is easy to deploy: ```jsx ``` The radial chart accepts children if you wish to give it them. This can be useful for adding tooltips, for example: ## Data format Reference Radial chart has a very similar API to the arc series, but with even fewer requirements. To wit the data can be as simple as ```javascript const myData = [{angle: 1}, {angle: 5}, {angle: 2}] ``` Or as complex as [ {angle: 1, radius: 10}, {angle: 2, label: 'Super Custom label', subLabel: 'With annotation', radius: 20}, {angle: 5, radius: 5, label: 'Alt Label'}, {angle: 3, radius: 14}, {angle: 5, radius: 12, subLabel: 'Sub Label only', className: 'custom-class'} ]; #### angle Type: `number` The only required property for the data, this determines the angular size of each wedge. #### radius Type: `number` The distance between the origin and the outside of the arc. This values is scaled linearly by default #### label Type: `string` The label to show next to the wedge. #### subLabel Type: `string` The subLabel to show next to the wedge. This can be used for annotations to the top label. #### color (optional) Type: `string|number` The color of a box in the series. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. #### style (optional) Type: `object` SVG paths (which is what the arc series is made up of) have numerous manipulable properties, so rather than trying to prescribe all of them as props we offer a port to let you style it for yourself. This overrides the series level version of this property. #### className (optional) Type: `string` The className to be added to an individual arc in the series. #### padAngle (optional) Type: `number|function` The padding to be applied between arcs. See above donut chart for an example of a padded angle. ## Api ##### angleDomain, angleRange, angleType Scale properties for the `angle` scale. The `angle` property _should be_ passed in the data, otherwise the chart won't be shown. Please refer to [Scales and Data](scales-and-data.md) for more information about scales. ##### animation (optional) Type: `boolean|Object` Please refer to [Animation](animation.md) doc for more information. ##### className (optional) Type: `string` DOM classNames to be added to the wrapper component. ##### colorDomain, colorRange, colorType Scale properties for the `color` scale. If `color` property is not passed in the data object, each new section of the chart gets the next color (e. g. the `'category'` scale is applied). Please refer to [Scales and Data](scales-and-data.md) for more information about scales. ##### data Type: `Array` Array of data for the series. See above data format reference. ##### fillDomain, fillRange, fillType Scale properties for the `fill` scale. If `fill` property is not passed in the data object, color scale is used instead. Please refer to [Scales and Data](scales-and-data.md) for more information about scales. ##### height (required, pixels) #### innerRadius Type: `number` in pixels If radius is not set on the data then this can be used to set the innerRadius for all of the rows. This can be useful for building donut charts. ##### width (required, pixels) ##### labelsAboveChildren Type: `boolean` Whether or not to position the labels on top of the children. This can be useful if you have circular gridline and you want your labels to be legible on top of your grids. ##### labelsRadiusMultiplier Type: `number` How far the labels should be from the center of the chart as a function of the radius of the chart. If not specified, the default value of 1.1 is used (slightly outside of the chart). Note that the property is labelsRadiusMultiplier (labels plural, not labelRadiusMultiplier) ##### labelsStyle Type: 'Object' A style object specifically for the labels. Note that the property is labelsStyle (labels plural, not labelStyle) ##### margin (optional, pixels) Type: `Object` Default: `{left: 40, right: 40, top: 10, bottom: 10}` #### radius Type: `number` in pixels If radius is not set on the data then this can be used to set the radius for all of the rows. ##### showLabels (optional) Type: `boolean` Whether or not to show the labels specified in the data ##### strokeDomain, strokeRange, strokeType Scale properties for the `stroke` scale. If `stroke` property is not passed in the data object, stroke is _not_ visualized. Please refer to [Scales and Data](scales-and-data.md) for more information about scales. ================================================ FILE: docs/rect-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Rect Series RectSeries is a generalization of [BarSeries](bar-series.md) which allows users to build a series of rectangles of arbitrary dimensions. Whereas in barSeries, one dimension of the bars is fixed (width for vertical bar series, height for horizontal bar series), in RectSeries, both dimensions can be controlled. RectSeries can be used to build histograms, icicle charts, or anything really where both height and width matter. Like BarSeries, RectSeries has two wrappers: HorizontalRectSeries and VerticalRectSeries. It also has a canvas version, RectSeriesCanvas (along with HorizontalRectSeriesCanvas and VerticalRectSeriesCanvas). RectSeries isn't meant to be used directly, however, it's provided as it's being used under the hood by HorizontalRectSeries and VerticalRectSeries. ## Data format Reference Like other series, it is required that the data be an array of objects, formatted like so: ```javascript const myData = [ {x: 1, x0: 0, y: 10, y0: 0}, {x: 2, x0: 1, y: 5, y0: 0}, {x: 4, x0: 2, y: 15, y0: 0} ] ``` The main difference with bar series is that it has x0 and y0 properties. ### For HorizontalRectSeries: #### x (optional) Type: `string|number|date` Default: `0` The value used to compute the x position of _the right_ side of the rectangle. #### x0 (optional) Type: `string|number|date` Default: `0` The value used to compute the x position of _the left_ side of the rectangle. #### y (optional) Type: `string|number|date` Default: `0` The value used to compute the y position of _the bottom_ of the rectangle. #### y0 (optional) Type: `string|number|date` Default: `0` The value used to compute the y position of _the top_ of the rectangle. ### For VerticalRectSeries: #### x (optional) Type: `string|number|date` Default: `0` The value used to compute the x position of _the left_ side of the rectangle. #### x0 (optional) Type: `string|number|date` Default: `0` The value used to compute the x position of _the right_ side of the rectangle. #### y (optional) Type: `string|number|date` Default: `0` The value used to compute the y position of _either_ side of the rectangle. #### y0 (optional) Type: `string|number|date` Default: `0` The value used to compute the y position of the other side of the rectangle. #### color (optional) Type: `string|number` The color of a bar in the series. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. See [colors](colors.md). #### opacity (optional) Type: `number|Object` Opacity of the individual box to be rendered. By default opacity is scaled by `literal`, so the exact value provided will be used. This property can also be defined on the series level. #### stroke (optional) Type: `number|Object` The color of the outline of the box to be rendered. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. See [colors](colors.md). #### fill (optional) Type: `number|Object` The color of the inside of the box to be rendered. When this value is not provided the color attribute is used instead. This property can also be defined on the series level. See [colors](colors.md). ## Series API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### color Type: `string|number` The color for all elements in the series, this property will be over-ridden by color specified in the data attribute. See [colors](colors.md). #### data Type: `Array` Array of data for the series. See above data format reference. #### fill Type: `string|number` The inner color for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### opacity Type: `string|number` The opacity for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### stroke Type: `string|number` The outer color for all elements in the series, this property will be over-ridden by color specified in the data attribute. #### style Type: `object` A list of CSS properties to style the series outside of the explicitly set properties. Note that it will override all other properties (ie fill, stroke, opacity, color). See [style](style.md) ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onValueClick (optional) Type: `function(d, {event})` `click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOut (optional) Type: `function(d, {event})` `mouseout` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueMouseOver (optional) Type: `function(d, {event})` `mouseover` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onValueRightClick (optional) Type: `function(d, {event})` `right-click` event handler for the elements corresponding separate data points. First argument received is, `d`, the relevant data point, and second an object with the only `event` property. #### onSeriesClick (optional) Type: `function({event})` `click` event handler for the entire series. Receives an object as argument with the `event` property. #### onSeriesMouseOut (optional) Type: `function({event})` `mouseout` event handler for the entire series. Receives an object as argument with the `event` property. #### onSeriesMouseOver (optional) Type: `function({event})` `mouseover` event handler for the entire series. Receives an object as argument with the `event` property. #### onSeriesRightClick (optional) Type: `function({event})` `right-click` event handler for the entire series. Receives an object as argument with the `event` property. ================================================ FILE: docs/sankey.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Sankey Sankey diagrams are a form of graph that allows for the easy communication of flows and other transferal processes. ### Usage ```jsx import {Sankey} from 'react-vis'; const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; const links = [ {source: 0, target: 1, value: 10}, {source: 0, target: 2, value: 20}, {source: 1, target: 2, value: 20} ]; ``` ### Api ##### width (required, pixels) ##### height (required, pixels) ##### nodes (required) Type: `Object` An array of objects matching the following shape: ``` { name: String, color: String, opacity: Number, key: String } ``` The name will be displayed as a label next to its node. All these fields are optional. ##### links (required) Type: `Object` An array of objects matching the following shape, where both `source` and `target` are the indexes of the nodes they intent to represent, and `value` that would match the height of the path link. ``` { // required source: Number, target: Number, value: Number, // optional color: String, opacity: Number, key: String } ``` ##### margin (pixels) Type: either number or {top: Number, left: Number, right: Number, bottom: Number} The margin that will applied around the edge of the diagram. ##### nodeWidth (optional) Type: `Number`(pixels) Defaults: `10`. Width of the nodes. ##### nodePadding (optional) Type: `Number`(pixels) Defaults: `10`. Padding between each node. ##### align (optional) Type: `String`, one of `justify`, `center`, `left`, `right` Defaults: `justify`. The alignment used for the sankey, see above for an example. ##### layout (optional) Type: `Number` Defaults: `50`. The number of passes the sankey algorithm will do in order to arrange positioning. ##### hasVoronoi (optional) Type: `Boolean` Defaults: `false` Determine if the node selection will be done using a voronoi or not. Although less precise, it can help providing a better interactive experience to the user. ##### hideLabels (optional) Type: `Boolean` Defaults: `false`. Hide the display of the node names if specified to true. #### labelRotation (optional) Type: `Number` Default: `0` Rotate the angle of the labels in the sankey ##### onValueClick (optional) Type: `function` Default: noop This handler is triggered either when the user clicks on a node. Callback when clicking a node, or the voronoi assigned to this node, pass the node. ```jsx { // does something on click // you can access the value of the event }} ``` ##### onValueMouseOver (optional) Type: `function` Default: noop This handler is triggered either when the user hovers over a node. Callback when clicking a node, or the voronoi assigned to this node, pass the node. ```jsx { // does something on click // you can access the value of the event }} ``` ##### onValueMouseOut (optional) Type: `function` Default: noop This handler is triggered either when the users mouse leaves a node. Callback when clicking a node, or the voronoi assigned to this node, pass the node. ```jsx { // does something on click // you can access the value of the event }} ``` #### onLinkClick (optional) Type: `function` Default noop This handler is triggered when the user clicks on a link. Callback accepts the data point associated with this link as well as the click event. ```jsx { // does something on click // you can access the value of the event }} /> ``` #### onLinkMouseOver (optional) Type: `function` Default noop This handler is triggered when the user's mouse hovers over a link. Callback accepts the data point associated with this link as well as the click event. ```jsx { // does something on mouseover // you can access the value of the event }} /> ``` #### onLinkMouseOut (optional) Type: `function` Default noop This handler is triggered when the user's exits a link. Callback accepts the data point associated with this link as well as the click event. ```jsx { // does something on mouseout // you can access the value of the event }} /> ``` #### style (optional) Type: `object` An object that contains CSS properties with which the axis component can be entirely re-styled. As the Sankey is composite of several composite elements, it is possible to provide style objects for any and all parts of the tree. See [style](style.md) Most generally, there are three top level components `labels`, `links`, and `rects`. These in turn lead to their corresponding to style objects. As an example, here is the default style object for the Sankey: ```jsx ``` ##### children (optional) Type: `Node` (Based on React.PropTypes.node: Anything that can be rendered: numbers, strings, elements or an array (or fragment) containing these types.) Allows to render additional children at the inner XYPlot used by the Sankey. See the [XYPlot](xy-plot.md)'s for more general information on children. This is especially useful for rendering of Hints within a Sankey (since the must be rendered inside the XYPlot). ```jsx ``` (See sample [Sankey - With hint (for links)](examples/showcases/sankeys-showcase.md) at showcase for more details.) ================================================ FILE: docs/scales-and-data.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Scales and data ### Data React-Vis charts are made of Series components - LineSeries, BarSeries and so on and so forth. Each of these Series components requires a `data` property, through which we pass an array of objects. These properties correspond to various visual characteristics of the corresponding marks. For example, x and y, which are required for most series types, affect the position of each mark. Each series type takes more properties, though, which are described in the series section. Here is how a simple dataset is transformed in some simple charts: ```jsx const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2}, {x: 9, y: 0} ]; ... ``` By the way: can you guess what this dataset is? Answer at the end of the document. ### Scales Scales are what actually transform the values of the properties in the data objects into visual attributes. In the example above, for instance, the third object is {x: 2, y: 4}. But the corresponding rectangle is 39.45px from the left and 105.56px from the top of the top left corner of the chart. How? Scales. React-vis scales are designed so that as often as possible, you shouldn't have to do anything; yet they give you control to override anything they do. The notion of scales and the corresponding vocabulary are directly taken from d3js. Scales have a type, a range and a domain. For a given chart, there are multiple scales: there's one scale per attribute. Attributes include x position, y position, color, size, angle etc. Again, not all attributes are applicable in all series. We are following the definition of scales which was given by Mike Bostock: _scales are functions that map from an input domain to an output range_. Under the hood, scales have a default type; a default domain can be inferred automatically from the data and, depending on the attribute, there's either a default range, or it is being generated depending on context. For example, for x and y: - the default type is a linear scale (the relationship between the value in the data object and the actual position of the mark is of the form: y = ax + b ), - the domain is defined by the smallest and highest values for found in the dataset - in the above example, both x and y vary from 0 to 9, so the domains are [0, 9] for both the x- and the y- scale, - and the range is the total width of the XYPlot minus the margin. All of this is sensible most of the time. Scales transform each datapoint into visual characteristics for a mark, so, for a given attribute, they only work if the corresponding data property exists. The property in the datapoint MUST have the same name as the attribute. You can have all the properties you want in your datapoint object, but to position the mark from left to right, you NEED a x property. ### Available scales by series type: Here is what attribute is available as a scale per series type, and what is the default scale type: | Series | angle | angle0 | color | fill | opacity | radius | radius0 | size | stroke | x | x0 | y | y0 | |---------------------|--------|--------|------------|------------|------------|--------|---------|--------|------------|--------|--------|--------|--------| | [ArcSeries](arc-series.md) | linear | linear | linear | linear | literal | linear | linear | | linear | linear | | linear | | | [AreaSeries](area-series.md) | | | / series | / series | / series | | | | / series | linear | | linear | linear | | [ContourSeries](contour-series.md) | | | linear* | | | | | | | linear | | linear | | | [HeatmapSeries](heatmap-series.md) | | | linear | | literal | | | | linear | linear | | linear | | | [HorizontalBarSeries](bar-series.md) | | | linear | linear | literal | | | | linear | linear | linear | linear | | | [LabelSeries](label-series.md) | | | | | | | | | | linear | | linear | | | [LineSeries](line-series.md) | | | / series | | / series | | | | / series | linear | | linear | | | [MarkSeries](mark-series.md) | | | linear | linear | literal | | | linear | linear | linear | | linear | | | [PolygonSeries](polygon-series.md) | | | / series | | | | | | | linear | | linear | | | [RectSeries](rect-series.md) | | | linear | linear | literal | | | | linear | linear | linear | linear | linear | | [VerticalBarSeries](bar-series.md) | | | linear | linear | literal | | | | linear | linear | | linear | linear | For the Heatmap and Hexbin series, while you can pass a colorDomain and a colorRange, you cannot override the type of scale for colors. "Per series" means that it's possible to pass a value to the series as a whole, but not per data point. If an attribute is not available as a scale for a given series, all values passed in the corresponding property will be ignored. For instance, if you use a dataset that has fill properties, it will be ignored for LineSeries. This table is also meant to be used for derived series. Canvas series have the same interface as SVG series. HorizontalRectSeries and VerticalRectSeries take the same attribute as RectSeries. And LineMarkSeries take the same attribute as Line and Mark series. ### Scale properties To redefine a scale, you must pass a prop to the series that uses that scale. The prop names are based on the name of the attribute: name + Domain, name + Range, name + Type, name + Padding (for instance: yDomain, colorType, xRange). * `get[name]` (optional) Type: `function` An accessor function that gets the value to be compute from the data. For instance if you were keeping your data as rows like {a: 1, b: 2, c: 3}, you could define an x accessor like `getX: d => d.a`. * `[name]Domain` (optional) Type: `Array` Array of values to visualize from. If domain is not passed, it will be calculated from the values which are passed to component. * `[name]Padding` (optional) Type: `Number` A percentage that will pad your `[name]Domain`. If the padding not passed `[name]Domain` will not be padded. Note: if you pass `[name]Domain` and it is not calculated from the values, padding will not be used. * `[name]Range` (optional) Type: `Array` Array of real-world values to visualize to. If range is not passed, the defaults (depend on visualization type) will be applied. * `[name]Type` (optional) Type: `('linear'|'ordinal'|'category'|'time'|'time-utc'|'log'|'literal')` Default: `'linear'` Type of the scale. Each scale type can be one of following values: * `'linear'` Continuous scale, that works with numbers. Similar to [d3.scaleLinear](https://github.com/d3/d3-scale/blob/master/README.md#scaleLinear). * `'ordinal'` Ordinal scale, works with numbers and strings. Similar to [d3.scaleOrdinal](https://github.com/d3/d3-scale/blob/master/README.md#ordinal-scales). * `'category'` Categorical scale, each new value gets the next value from the range. Similar to d3.scale.category\[Number\], but works with other values besides colors. * `'time'` Time scale. Similar to [d3.scaleTime](https://github.com/d3/d3-scale/blob/master/README.md#time-scales). * `'time-utc'` Time UTC scale. Similar to [d3.scaleUtc](https://github.com/d3/d3-scale/blob/master/README.md#scaleUtc) * `'log'` Log scale. Similar to [d3.scaleLog](https://github.com/d3/d3-scale/blob/master/README.md#log-scales). * `'literal'` Returns exactly the value that was given to it. Similar to [d3.scaleIdentity](https://github.com/d3/d3-scale#scaleIdentity), except that it does NOT coerce data into numbers. This is useful for precisely specifying properties in the data, eg color can be specified directly on the data. ### Overriding scales Scales can be defined either at the XYPlot level, in which case they apply to the whole chart, or at the series level. Scales defined at the series level override those defined at the XYPlot level. The scales provided to each individual series don't have to have the same parameters. For instance, if you wanted to do a dual-axis chart, you could provide a different yDomain and yRange to two data series (use at your own risk). ### A brief example Let's apply these ideas to a reasonably common use case: reversing the domain of a chart. Imagine we wanted to reverse the x display order of our mark series above. To do this we would make use of the xDomain and our prior knowledge of the domain of the x variable. ```javascript ``` This of course applies for all types of series. ### Other uses of scales Scales can also be used in [Axes](axes.md) and in [Gridlines](grids.md). You can pass an x-scale (so xDomain, xRange, xPadding, xType) to an XAxis or a VerticalGridLines component, and a y-scale (so yDomain, yRange, yPadding, yType) to a YAxis or HorizontalGridLines component. These scale parameters don't have to be the same as the ones passed to your series. Did you guess that this data set was the digits sorted alphabetically? eight, five, four... ### Time scale localization The locale used by d3 to format the time series tick labels is `en-US` by default. You can override it by providing a locale object to the `timeFormatDefaultLocale` function of [d3-time-format](https://github.com/d3/d3-time-format#timeFormatDefaultLocale) which you need to have as a dependency. [Various locale files](https://github.com/d3/d3-time-format/tree/master/locale) are available in the [d3-time-format git repository](https://github.com/d3/d3-time-format#timeFormatDefaultLocale). eg for including the french locale : ```javascript import {timeFormatDefaultLocale} from 'd3-time-format'; timeFormatDefaultLocale({ dateTime : '%a %b %e %X %Y', date : '%d/%m/%Y', time : '%H : %M : %S', periods : ['AM', 'PM'], days : ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'], shortDays : ['Di', 'Lu', 'Ma', 'Me', 'Je', 'Ve', 'Sa'], months : ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Decembre'], shortMonths : ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jui', 'Juil', 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'] }); ``` If you do not want to add d3-time-format as a dependency, you can still use the `tickFormat` prop that can be passed to [Axes](axes.md) to handle the localization yourself, although you will lose the benefits of d3-scale formatting. ================================================ FILE: docs/series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Series The library supports several types of series: * [ArcSeries](arc-series.md) for radial arcs such as might be found in pie charts. * [AreaSeries](area-series.md) for area charts; * [BarSeries](bar-series.md) for discrete bar charts, covers (covers HorizontalBarSeries and VerticalBarSeries); * [ContourSeries](contour-series.md) for making contour density plots; * [HeatmapSeries](heatmap-series.md) for heat maps. * [HexbinSeries](hexbin-series.md) for aggregate hexagonal binning heatmaps. * [LabelSeries](label-series.md) for adding annotations to charts * [LineMarkSeries](line-mark-series.md) is a shorthand to place marks (e.g. circles) on lines; * [LineSeries](line-series.md) for lines; * [MarkSeries](mark-series.md) for scatterplots; * [PolygonSeries](polygon-series.md) for arbitrary SVG shapes * [RectSeries](rect-series.md) for arbitrary histograms and other continuous variable boxes. (covers HorizontalRectSeries and VerticalRectSeries) Each series provides following API: #### data Type: `Array` Array of data for the series. #### x Type: `number|Object` Exact X position of all series points in pixels or a series object. #### y (optional) Type: `number|Object` Exact Y position of all series points in pixels or a series object. #### color (optional) Type: `string|Object` Exact color for all series points or a series object. #### size (optional) Type: `number|Object` Exact size for all series points in pixels or a series object. #### opacity (optional) Type: `number|Object` Exact opacity for all series points in pixels or a series object. #### className (optional) Type: `string` Provide an additional class name for the series. #### onNearestX (optional) Type: `function(value, info)` A callback function which is triggered each time when the mouse pointer gets close to some X value. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the value; - `index` is the index of the data point in the array of data; - `event` is the event object. #### onValueMouseOver (optional) Type: `function(d, info)` `mouseover` event handler for the elements corresponding separate data points `d` is a data point, `info` is an object with the only `event` property. **NOTE**: This event handler is *not* triggered for AreaSeries and LineSeries. #### onValueMouseOut (optional) Type: `function(d, info)` `mouseout` event handler for the elements corresponding separate data points. `d` is a data point, `info` is an object with the only `event` property. **NOTE**: This event handler is *not* triggered for AreaSeries and LineSeries. #### onValueClick (optional) Type: `function(d, info)` `click` event handler for the elements corresponding separate data points. `d` is a data point, `info` is an object with the only `event` property. **NOTE**: This event handler is *not* triggered for AreaSeries and LineSeries. #### onSeriesMouseOver (optional) Type: `function(info)` `mouseover` event handler for the entire series. Received `info` object as argument with the only `event` property. #### onSeriesMouseOut (optional) Type: `function(info)` `mouseout` event handler for the entire series. Received `info` object as argument with the only `event` property. #### onSeriesClick (optional) Type: `function(info)` `click` event handler for the entire series. Received `info` object as argument with the only `event` property. #### style (optional) Type: `object` An object which holds CSS properties that will be applied to the SVG element(s) rendered by the series. This allows you to style series beyond the other explicitly defined properties and without having to use CSS classnames and stylesheets. For instance, you can set the stroke-linejoin style of a line series to "round": ```jsx ``` LineMark series is a composite series, and as such, it's possible to separate style instructions for the line and the mark part by putting them under a "line" and a "mark" property respectively: ```jsx ``` Note that style information passed through the style property will override those passed through props. ```jsx ``` #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### stack (optional) Type: `Boolean` Default: `false` Opt-in for stacking series and mix stacked and non-stacked series in a single chart. If all series have the `stack` prop set to `false` (which is default behaviour), they will all be considered stackable. Otherwise if at least two of the series have the `stack` prop set to `true`, they will be stacked together and the other series will be considered non stackable. See the [XYPlot](xy-plot.md)'s `stackBy` section for more information. ================================================ FILE: docs/style.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Style In order to control the look and feel of your React-Vis components, you have four strategies. ### the React-Vis style sheet React-Vis comes with a default style sheet. You need to import it or otherwise link it to your app (as shown in the [getting started](tutorial/getting-started.md) page), or you may overwrite it. ### Class names You may also use the class names of the React-Vis component to style them through your own stylesheets, or your own style strategies. Furthermore, all series components accept a `className` property, which adds a class of your own choosing to the element. ### Component-specific properties Virtually every component accept several properties that affects its appearance. For instance, [line series](line-series.md) take a `color` property to control the stroke color of the line, but others as well such as strokeWidth that controls its thickness. Each of these is described in detail for each component. #### style property Finally, components can also accept a special property called `style`. This let you pass an object to the component. The keys of that object are CSS properties, camel-cased (ie `stroke-width` would be written `strokeWidth`) and values are what you'd want to set those properties to. These are the same conventions than when [passing style](https://facebook.github.io/react/docs/dom-elements.html) to a standard DOM element with React. ```javascript ``` Some React-Vis components are composite in the sense that they group several elements that you may want to style distinctly. For instance, the [line-mark series](line-mark-series.md) combines a [line series](line-series.md) and a [mark series](mark-series.md). While you could pass the same style object to both, you can also use special properties (in this case, `line` and `mark`) to send a specific style object to either or both sub-components. ```javascript ``` In that example, without the style property, both lines and marks would be red. Without specifying `mark` in the style property, the stroke color of both lines and marks would be white. Here, the line remains red, and the marks are going to be red (their fill color) but with a white outline. ================================================ FILE: docs/sunburst.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Sunbursts Sunbursts are a powerful way to demonstrate part to whole relationships. While they certainly have the many of easily criticized problems of pie charts, they allow for at a glance understanding of deeply nested systems. This could be useful for understanding for funnels problems or distributions of nested groups (eg how does my cities performance compare to other cities in my country?). The `Sunburst` is a thin data processing wrapper on an XYPlotted [ArcSeries](arc-series.md), it generates highly re-stylable SVG! Any props that are available in the arc series are also available here. Additionally it copies the data format of the treemap, so if you have data prepped to drop into the tree map, you can use that same data to get a sunburst. ## Usage Import the `Sunburst` component: ```jsx import {Sunburst} from 'react-vis'; ``` Add the following code to your render function: ```jsx ``` Like other systems that make use of d3's hierarchy layout system we ask that our data be presented to us in a tree like structure. Here's a slice of the famous d3-flare dataset: ```javascript const myData = { "title": "analytics", "color": "#12939A", "children": [ { "title": "cluster", "children": [ {"title": "AgglomerativeCluster", "color": "#12939A", "size": 3938}, {"title": "CommunityStructure", "color": "#12939A", "size": 3812}, {"title": "HierarchicalCluster", "color": "#12939A", "size": 6714}, {"title": "MergeEdge", "color": "#12939A", "size": 743} ] }, { "title": "graph", "children": [ {"title": "BetweennessCentrality", "color": "#12939A", "size": 3534}, {"title": "LinkDistance", "color": "#12939A", "size": 5731}, {"title": "MaxFlowMinCut", "color": "#12939A", "size": 7840}, {"title": "ShortestPaths", "color": "#12939A", "size": 5914}, {"title": "SpanningTree", "color": "#12939A", "size": 3416} ] }, { "title": "optimization", "children": [ {"title": "AspectRatioBanker", "color": "#12939A", "size": 7074} ] } ] } ``` First, note the recursive tree relationship: each node has a title, and an array of children. This pattern continues until we reach the leaves, where we declare the size of the leaves. This value is rolled up, so that the "cluster" node has 3938 + 3812 + 6714 + 743 = 15207 size units. ### Adding annotations Tooltips and other helpful annotations can be added to the sunburst diagram by providing those elements as children. For instance, if we wanted to add a tooltip to the above Sunburst, this could be done by adding a [Hint](hint.md) component as a child. ```jsx ``` Where `hoveredValue` is an appropriately curated coordinate value. See the [sunburst-with-tooltips](https://github.com/uber/react-vis/blob/master/packages/showcase/sunbursts/sunburst-with-tooltips.js) code for more details. ## API Reference #### width Type: `number` Width of the component. #### height Type: `number` Height of the component. #### data Type: `Object` The data for the component. The `data` property is a tree-like structure. Each point consists of following properties: * `title` Type: `string` The title to show inside the cell. Might be a string or a React component. * `size` Type: `number` The relative size of the cell. * `color` (optional) Type: `number` or `string` The value to visualize the color with. * `label` (optional) Type: `string` The label to be attached for the current node. * `labelStyle` (optional) Type: `object` The style of the attached label. Example `{labelStyle: {fontSize: 15}, ...}` * `dontRotateLabel` (optional) Type: `boolean` Don't rotate this label * `children` (optional) Type: `Array` The children for the leaf. #### hideRootNode (optional) Type: `boolean` Simple boolean on whether or not to show the root node of the tree. #### children (optional) Type: `react components` Sunburst can accept react components as children if you wish to annotate your diagram. #### animation (optional) Type: `boolean|Object` Please refer to [Animation](animation.md) doc for more information. #### onValueClick (optional) Type: `function` - Should accept arguments (arc node, domEvent) Pass in a function that will be called on click on a given arc. #### onValueRightClick (optional) Type: `function` - Should accept arguments (arc node, domEvent) Pass in a function that will be called on right click on a given arc. #### onValueMouseOver (optional) Type: `function` - Should accept arguments (arc node, domEvent) Pass in a function that will be called on mouseEnter on a given arc. #### onValueMouseOut (optional) Type: `function` - Should accept arguments (arc node, domEvent) Pass in a function that will be called on mouseOut on a given arc. #### padAngle (optional) Type: `number|function` The padding to be applied between arcs. ================================================ FILE: docs/treemap.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # Treemap Treemaps are a splendid way to represent data that has a nested aspect to it. They allow for the easy display of complicated relative information, such as nested-part-to-whole relationships in a easy to grok fashion. Checkout [the wikipedia page](https://en.wikipedia.org/wiki/Treemapping) or Ben Shneiderman's excellent [History of treemaps](http://www.cs.umd.edu/hcil/treemap-history/index.shtml) for more information. The `treemap` in react-vis builds a series of nested divs (allow for easy and highly restyleable trees). We offer ten different layout strategies, enabling the construction of standard treemaps, circle packed treemaps, and partition trees (also called icicle diagrams). Our treemap can target SVG or pure dom (via the renderMode prop)! You can play with the representation above. ## Usage Import the `treemap` component: ```jsx import {Treemap} from 'react-vis'; ``` Add the following code to your render function: ```jsx ``` Like other systems that make use of d3's hierarchy layout system we ask that our data be presented to us in a tree like structure. Here's a slice of the famous d3-flare dataset: ```javascript const myData = { "title": "analytics", "color": "#12939A", "children": [ { "title": "cluster", "children": [ {"title": "AgglomerativeCluster", "color": "#12939A", "size": 3938}, {"title": "CommunityStructure", "color": "#12939A", "size": 3812}, {"title": "HierarchicalCluster", "color": "#12939A", "size": 6714}, {"title": "MergeEdge", "color": "#12939A", "size": 743} ] }, { "title": "graph", "children": [ {"title": "BetweennessCentrality", "color": "#12939A", "size": 3534}, {"title": "LinkDistance", "color": "#12939A", "size": 5731}, {"title": "MaxFlowMinCut", "color": "#12939A", "size": 7840}, {"title": "ShortestPaths", "color": "#12939A", "size": 5914}, {"title": "SpanningTree", "color": "#12939A", "size": 3416} ] }, { "title": "optimization", "children": [ {"title": "AspectRatioBanker", "color": "#12939A", "size": 7074} ] } ] } ``` First, note the recursive tree relationship: each node has a title, and an array of children. This pattern continues until we reach the leaves, where we declare the size of the leaves. This value is rolled up, so that the "cluster" node has 3938 + 3812 + 6714 + 743 = 15207 size units. #### Hints - It is often quite effective to use the literal scale type for Treemap's color attribute, as this allows highly granular control over all of the nodes. - It can useful to encode opacity to indicate tree depth, however because each tree leaf is a nested div this gets a little tricky. One technique is to compute the effective RGBA to hex value, check out [this link](viget.com/articles/equating-color-and-transparency) for more details. - If your not sure when to use a treemap, remember they provide an easy drop in relationship for pie charts. ## API Reference #### width Type: `number` Width of the component. #### height Type: `number` Height of the component. #### padding Type: `number` The padding between cells the cells of the heatmap in pixels. #### data Type: `Object` The data for the component. The `data` property is a tree-like structure. Each point consists of following properties: * `title` Type: `string` The title to show inside the cell. Might be a string or a React component. * `size` Type: `number` The relative size of the cell. * `opacity` (optional) Type: `number` The value to visualize the opacity with. * `color` (optional) Type: `number` or `string` The value to visualize the color with. * `style` (optional) Type: `object` style object to be added to the inline styles of the array * `children` (optional) Type: `Array` The children for the leaf. #### animation (optional) Type: `boolean|Object` Please refer to [Animation](animation.md) doc for more information. #### hideRootNode (optional) Type: `boolean` Simple boolean on whether or not to show the root node of the tree. #### onLeafClick (optional) Type: `function` - Should accept arguments (leafNode, domEvent) Pass in a function that will be called on click on a given leaf. #### onLeafMouseOver (optional) Type: `function` - Should accept arguments (leafNode, domEvent) Pass in a function that will be called on mouseEnter on a given leaf. #### onLeafMouseOut (optional) Type: `function` - Should accept arguments (leafNode, domEvent) Pass in a function that will be called on mouseOut on a given leaf. #### mode (options) Type: `string` - One of squarify, resquarify, slice, dice, slicedice, binary, circlePack, partition, partition-pivot This modifies the tiling strategy for the treemap, for more information see the [d3 hierarchy docs](https://github.com/d3/d3-hierarchy). #### renderMode Type: `string` - One of 'SVG', or 'DOM' Determines which type of rendering to use for the treemap. #### sortFunction (optional) Type: `function` - Should accept arguments (a, b) Pass in a function that will be used to sort the nodes, for more information see the [d3 hierarchy docs on sorting](https://github.com/d3/d3-hierarchy#node_sort). ##### colorDomain, colorRange, colorType Scale properties for the `color` scale. If `color` property is not passed in the data object, each new section of the chart gets the next color (e. g. the `'category'` scale is applied). Please refer to [Scales and Data](scales-and-data.md) for more information about scales. ================================================ FILE: docs/voronoi.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## Voronoi Voronoi diagrams are useful for making a chart interactive by creating target areas for events like hover and click. ```jsx x(d.x)} y={d => y(d.y)} /> ``` ## API Reference #### extent Type: `Array` Sets the clip extent of the Voronoi layout to the specified bounds. The extent bounds are specified as an array [[x0, y0], [x1, y1]], where x0 is the left side of the extent, y0 is the top, x1 is the right and y1 is the bottom. Extent should take the dimensions of the accompanying XYPlot into account, so using the plot's width, height and margins: `[[marginLeft, marginTop], [width, height]]`, which coincidentally is the default extent. #### nodes (required) Type: `Array` The array must consist of `{x, y}` objects. These are often identical to the data passed to a series in the accompanying plot. Each item in the array will create a polygon cell in the resulting Voronoi diagram. Optional properties are: - style `Object` - className `String` Example: ```js [ { x: 0, y: 10 }, { x: 1, y: 5, style: { stroke: 'blue' } } ]; ``` #### x (optional) Type: `Function` Sets the x-coordinate accessor. Often you want to convert the coordinate-values to pixel values like `x={d => x(d.x)}`. If not provided defaults to wrapping XYPlot's xScale. #### y (optional) Type: `Function` Sets the y-coordinate accessor. Often you want to convert the coordinate-values to pixel values like `y={d => y(d.y)}`. If not provided defaults to wrapping XYPlot's yScale. #### onBlur (optional) Type: `Function` Add `blur`-event to Voronoi cells #### onClick (optional) Type: `Function` Add `click`-event to Voronoi cells #### onMouseUp (optional) Type: `Function` Add `mouseUp`-event to Voronoi cells #### onMouseDown (optional) Type: `Function` Add `mouseDown`-event to Voronoi cells #### onHover (optional) Type: `Function` Add `hover`-event to Voronoi cells #### className (optional) Type: `String` Add css class to Voronoi container ##### style (optional) Type: `Object` Add css styles to Voronoi container #### polygonStyle (optional) Type: `Object` Add css styles to Voronoi cells. For example: `polygonStyle={{stroke: 'red'}}` This will add a red border around cell which is very useful for debugging the Voronoi diagram. ================================================ FILE: docs/whisker-series.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. ## WhiskerSeries WhiskerSeries plots variance "whiskers" for each data point. Often this is used in combination with another series (e.g. a MarkSeries or LineSeries) to represent the variance (or alternately standard deviation) associated with each value. Variance lines can be in the Y dimension, X dimension, or both. Deploy a WhiskerSeries like this: ```javascript render() { return ( ); ``` Just like other series, WhiskerSeries expects its data to be formatted as an array of objects. These data points may include an `xVariance` property, a `yVariance` property, or both: ```javascript const myData = [ {x: 1, y: 10, xVariance: 4, yVariance: 4}, {x: 1.7, y: 12, xVariance: 7, yVariance: 7}, {x: 2, y: 5, xVariance: 3, yVariance: 3}, {x: 3, y: 15, xVariance: 10, yVariance: 10}, {x: 2.5, y: 7, xVariance: 4, yVariance: 4} ]; ``` WhiskerSeries also accepts a `size` value that specifies an empty "buffer" region. This is especially useful if you are combining the whiskers with another series, and the marks of that series include transparent regions. The buffer region prevents whisker lines from being drawn behind that other mark. ```javascript const myData = [ {x: 1, y: 10, size: 30, yVariance: 4}, {x: 1.7, y: 12, size: 10, yVariance: 7}, {x: 2, y: 5, size: 1, yVariance: 3}, {x: 3, y: 15, size: 12, yVariance: 10}, {x: 2.5, y: 7, size: 4, yVariance: 4} ]; ``` ## Data format reference #### x Type: `string|number|date` x will be used to determine the x position of each mark. The format of x depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### y Type: `string|number|date` y will be used to determine the y position of each mark. The format of y depends on what scale is being used - see [Scales and Data](scales-and-data.md) #### color (optional) Type: `string|number` The color of the marks. By default the color is interpreted as number to be scaled to a color range. This can be over-ridden by providing the prop colorType="literal" to the series itself. This property can also be defined on the series level. #### opacity (optional) Type: `string|number` Default: `1` Opacity of the individual marks, from 0 (transparent) to 1 (opaque). By default opacity is scaled by `literal`, so the exact value provided will be used. This property can also be defined on the series level. #### stroke (optional) Type: `string|number` The color of the outline of the marks. When this value is not provided, the color attribute is used instead. This property can also be defined on the series level. #### size (optional) Type: `string|number` Default: `0` The size of an empty "buffer" region at the center of each mark. #### xVariance (optional) Type: `string|number` The size of each of the lines in the X dimension. Either xVariance, yVariance, or both should be specified. #### yVariance (optional) Type: `string|number` The size of each of the lines in the Y dimension. Either xVariance, yVariance, or both should be specified. ## API Reference #### animation (optional) See the [XYPlot](xy-plot.md)'s `animation` section for more information. #### className (optional) Type: `string` Provide an additional class name for the series. #### color (optional) Type: `string|number` Exact color for all series points or a series object. #### data Type: `Array` Array of data for the series. #### opacity (optional) Type: `string|number` Exact opacity for all series points in pixels or a series object, from 0 (transparent) to 1 (opaque) #### size (optional) Type: `string|number` Exact size of an empty "buffer" region for all series points in pixels or a series object. #### stroke (optional) Type: `string|number` Default: see [colors](colors.md) A color for the outline of the marks. Will override color if both are provided. #### strokeWidth (optional) Type: `string|number` Default: `1` The width of the outline of the marks. ## Interaction handlers #### onNearestX (optional) Type: `function(value, {event, innerX, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose x position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onNearestXY (optional) Type: `function(value, {event, innerX, innerY, index})` A callback function which is triggered each time the mouse pointer moves. It can access the datapoint of the mark whose position is the closest to that of the cursor. Callback is triggered with two arguments. `value` is the data point, `info` object has following properties: - `innerX` is the left position of the mark; - `innerY` is the top position of the mark; - `index` is the index of the data point in the array of data; - `event` is the event object. See [interaction](interaction.md) #### onSeriesClick Type: `function` Default: none This handler fires when the user clicks somewhere on a series, and provides the corresponding event. Unlike onValueClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onSeriesMouseOut Type: `function` Default: none This handler fires when the user's mouse cursor leaves a series, and provides the corresponding event. Unlike onValueMouseOut, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesMouseOver Type: `function` Default: none This handler fires when the user mouses over a series, and provides the corresponding event. Unlike onValueMouseOver, it doesn't pass a specific datapoint. ```jsx { // does something on mouse over // you can access the value of the event }} ``` #### onSeriesRightClick Type: `function` Default: none This handler fires when the user right-clicks somewhere on a series, and provides the corresponding event. Unlike onValueRightClick, it doesn't pass a specific datapoint. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueClick Type: `function` Default: none This handler is triggered either when the user clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOut Type: `function` Default: none This handler is triggered either when the user's mouse leaves a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueMouseOver Type: `function` Default: none This handler is triggered either when the user's mouse enters a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on click // you can access the value of the event }} ``` #### onValueRightClick Type: `function` Default: none This handler is triggered either when the user right-clicks on a mark. The handler passes two arguments, the corresponding datapoint and the actual event. ```jsx { // does something on right click // you can access the value of the event }} ``` ================================================ FILE: docs/xy-plot.md ================================================ > This library is deprecated. Please see `DEPRECATED.md`. # XYPlot XYPlot allows you to make line charts, area charts, scatterplots, heat maps, etc with animations and different interactions between them. Currently following components are used for this purpose: * XYPlot to wrap all the items. * [Grids](grids.md) to show vertical and horizontal grids. * [Axes](axes.md) to show X and Y axis. * [Different kinds of series](series.md) for line/area/bar charts, scatterplots, heat maps, etc. * [Hint](hint.md) to show the selected hint. * [Crosshair](crosshair.md) for crosshairs. ## Usage Import the necessary components from the library… ```jsx import {XYPlot, XAxis, YAxis, HorizontalGridLines, LineSeries} from 'react-vis'; ``` … and add the following code to your `render` function: ```jsx ``` ## Common concepts XYPlot is a wrapper for series, hints, axes and other components. Most of these components do not require any properties by default, however it is expected that the user will pass the `data` property into each series. `data` is an array of objects. Each item is some point on the chart. Object may contain following properties: * `x` * `y` * `opacity` (optional) * `fill` (optional) * `stroke` (optional) * `strokeWidth` (optional), `strokeStyle` (optional) - to control the width of the line series and whether they are dashed or solid. * `color` (optional, used instead of `fill` and `stroke` if none of them is passed) * `size` (optional) * `style` (optional) - css properties as an object. Accessors can also be used to retreieve the properties above from the `data` object. For instance, the `getX` and `getY` accessors can be passed to the XYPlot object to access the `x` and `y` properties from `data` for each series. ```jsx d[0]} getY={d => d[1]}> ``` If the property is not passed in any of the objects, the property is not visualized. The user can override the way how properties are visualized by passing custom range, domain or type of scales to the series or the entire chart (please see [Series](series.md) for more info). Not all properties can be visualized in each series. Here's a short comparison of them: | | `x` | `y` | `color` | `opacity` | `size` | |----------------------|-----|-----|---------|-----------|--------| | [LineSeries](line-series.md) | + | + | + | | | | [AreaSeries](area-series.md) | + | + | + | | | | [LineMarkSeries](line-mark-series.md) | + | + | + | + | + | | [MarkSeries](mark-series.md) | + | + | + | + | + | | [VerticalBarSeries](bar-series.md) | + | + | + | + | | | [HorizontalBarSeries](bar-series.md)| + | + | + | + | | | [VerticalRectSeries](rect-series.md) | + | + | + | + | | | [HorizontalRectSeries](rect-series.md)| + | + | + | + | | | [HeatmapSeries](heatmap-series.md) | + | + | + | + | | | [HexbinSeries](hexbin-series.md) | + | + | + | + | | ### A note on ordering XYPlot is pretty flexible, and can accept most kinds of things DOM, SVG, really whatever react can build. As far as XYPlot is concerned there are two types of components in the world: those that can be rendered as part of an SVG tree and those that can't. It separates it's children into these two groups, and clusters the SVG elements under a root svg tag in order and then presents each of the remaining children in order. With a react configuration like: ```javascript ``` Would generate HTML something like: ```javascript
...
...
``` The TLDR here is that *ORDER MATTERS*! If you want the elements to appear in a different order, reorder them! ## API Reference ### XYPlot `XYPlot` is a component that wraps series, axis and grids, hints, etc and seamlessly provides necessary dimensions, sizes and scales into its children. `XYPlot` may or may not contain axes, grids, hints, crosshairs or series. #### width Type: `number` Width of the chart. The width should be passed. #### height Type: `number` Height of the component. The height should be passed. #### className (optional) Type: `string` DOM classNames to be added to the wrapper component. #### hasTreeStructure (optional) Type: `Boolean` Flag declaring whether or not react-vis should try to remove potential cyclic deps from tree structures created by d3. Specifically references to "parent" are removed. This is generally used as an internal prop, checkout the treemap or sunburst if you are curious. #### margin (optional) Type: `Object` Default: `{left: 40, right: 10, top: 10, bottom: 40}` Margin around the chart. #### stackBy (optional) Type: `string` Stack the chart by the given attribute. If the attribute is `y`, the chart is stacked vertically; if the attribute is `x` then it's stacked horizontally. See the [Series](series.md) API reference for series level stack opt-in. #### style (optional) Type: `object` CSS properties that will affect this wrapper component. Those will be applied to the SVG element in which other react-vis components will be created. ```jsx ``` *NOTE* in order to stack properly react-vis expects each x value in each series to be present (assuming stackBy: 'x', the same applies to stackBy 'y', just transposed). If our data looks like ``` const seriesOne = [ {x: 1, y: 10}, {x: 3, y: 15} ]; const seriesTwo = [ {x: 1, y: 10}, {x: 2, y: 5}, {x: 3, y: 15} ]; const seriesThree = [ {x: 3, y: 15} ]; ``` would render weirdly (eg boxes would not lump together at the bottom of the chart). To avoid this, simply provide zeroes for empty cells ``` const seriesOne = [ {x: 1, y: 10}, {x: 2, y: 0}, {x: 3, y: 15} ]; const seriesTwo = [ {x: 1, y: 10}, {x: 2, y: 5}, {x: 3, y: 15} ]; const seriesThree = [ {x: 1, y: 0}, {x: 2, y: 0}, {x: 3, y: 15} ]; ``` Will render beautifully! #### onClick (optional) Type: `function()` The function that is triggered each time the mouse clicks the component. #### onDoubleClick (optional) Type: `function()` The function that is triggered each time the mouse double-clicks the component. #### onMouseLeave (optional) Type: `function()` The function that is triggered each time the mouse leaves the component. #### onMouseMove (optional) Type: `function()` The function that is triggered each time mouse moves over at the component. #### onMouseEnter (optional) Type: `function()` The function that is triggered each time the mouse enters the component. #### onMouseDown (optional) Type: `function()` The function that is triggered each time the mouse button is pressed over the component. #### onMouseUp (optional) Type: `function()` The function that is triggered each time the mouse button is released over the component. #### onTouchStart (optional) Type: `function()` The function that is triggered each time the touch starts. #### onTouchMove (optional) Type: `function()` The function that is triggered each time the touch moves. #### onTouchEnd (optional) Type: `function()` The function that is triggered each time the touch ends. #### onTouchCancel (optional) Type: `function()` The function that is triggered each time the touch cancels. #### onWheel (optional) Type: `function()` The function that is triggered each time a wheel button is rotated on the component. #### animation (optional) Type: `{duration: number}|boolean` Default: `false` Animation config, which is automatically passed to all children, but can be overridden for the each child. If `false` is passed, then the child components *will not be* animated. If `true` is passed then the child components *will be* animated with the default settings. If an object is passed, then the child components *will be* animated with the given settings. #### dontCheckIfEmpty (optional) Type: `Boolean` Default: `false` If this prop is provided then the XYPlot with not check if the plot is empty before rendering. This can be useful if you have a variable amount of data, especially when that variable can be zero. ================================================ FILE: package.json ================================================ { "name": "react-vis-master", "version": "1.12.0", "license": "MIT", "author": "Visualization Team ", "description": "Data visualization library based on React and d3.", "private": true, "workspaces": [ "packages/showcase", "packages/react-vis", "packages/website" ], "repository": { "type": "git", "url": "https://github.com/uber-common/react-vis.git" }, "bugs": { "url": "https://github.com/uber-common/react-vis/issues/new", "email": "visualization@uber.com" }, "keywords": [ "d3", "react", "visualization", "chart", "es6", "babel" ], "scripts": { "lint": "eslint .", "remove-unpm-rfs": "./remove-refs-to-unpm.pl" }, "devDependencies": { "babel-eslint": "^10.1.0", "eslint": "6.8.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-babel": "^5.3.0", "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-react": "^7.20.0", "eslint-plugin-react-hooks": "^4.0.4", "husky": "^1.1.2" }, "husky": { "hooks": { "pre-commit": "npm run remove-unpm-rfs && git add yarn.lock" } }, "volta": { "node": "14.18.0", "yarn": "1.22.4" }, "resolutions": { "js-beautify": "1.10.3", "d3-color": "3.1.0" } } ================================================ FILE: packages/react-vis/.babelrc.json ================================================ { "plugins": [ ["module-resolver", { "root": ["src"] }] ] } ================================================ FILE: packages/react-vis/build-browser.sh ================================================ #!/bin/bash # Browserify doesn't seem to support defining custom entry point for modules in node_modules # This build script replaces the entry point (the main field of package.json) of d3 libraries to the files # defined in the unpkg field when building browser bundles. # The replacement are reverted after the build finishes. D3_LIB_PATHS=$(ls -d ../../node_modules/d3-*) get_key_from_d3_path() { local KEY=$(echo $1 | sed 's/..\/..\/node_modules\/d3-//') local KEY=$(echo $KEY | sed 's/-/_/') echo $KEY } for D3_LIB_PATH in $D3_LIB_PATHS do PACKAGE_JSON=$D3_LIB_PATH/package.json UNPKG=$(jq -r '.unpkg' $PACKAGE_JSON) if ! [ -z $UNPKG ] && [ $UNPKG != null ] then # save the main field MAIN=$(jq -r '.main' $PACKAGE_JSON) KEY=$(get_key_from_d3_path $D3_LIB_PATH) declare var_$KEY=$MAIN # modify the main field MOD=$(jq --arg unpkg $UNPKG '.main = $unpkg' $PACKAGE_JSON) cat <<< $MOD | jq . > $PACKAGE_JSON fi done BABEL_ENV=browser browserify src/index.js -t [ babelify --rootMode upward --global ] --standalone reactVis | uglifyjs > dist/dist.min.js # set the main fields of package.json back to original for D3_LIB_PATH in $D3_LIB_PATHS do PACKAGE_JSON=$D3_LIB_PATH/package.json KEY=$(get_key_from_d3_path $D3_LIB_PATH) MAIN="var_$KEY" if ! [ -z "${!MAIN}" ] then MOD=$(jq --arg var "${!MAIN}" '.main = $var' $PACKAGE_JSON) cat <<< $MOD | jq . > $PACKAGE_JSON fi done ================================================ FILE: packages/react-vis/jest.config.js ================================================ /*eslint-env node*/ const path = require('path'); module.exports = { transform: { '^.+\\.js$': path.resolve(__dirname, './jestBabelTransform.js') }, setupFilesAfterEnv: ['./jest.setup.js'], snapshotSerializers: ['enzyme-to-json/serializer'], transformIgnorePatterns: [ '/node_modules/(?!(d3-color|d3-scale|d3-interpolate|d3-hierarchy|d3-format|d3-shape|d3-array|d3-contour|d3-path|internmap|d3-time|d3-geo))' ] }; ================================================ FILE: packages/react-vis/jest.setup.js ================================================ /*eslint-env node*/ import 'regenerator-runtime/runtime'; import jsdom from 'jsdom'; import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; Enzyme.configure({adapter: new Adapter()}); global.document = jsdom.jsdom(''); global.window = document.defaultView; Object.keys(document.defaultView).forEach(function mapProperties(property) { if (typeof global[property] === 'undefined') { global[property] = document.defaultView[property]; } }); global.navigator = { userAgent: 'node.js' }; /* * Canvas mocks */ HTMLCanvasElement.prototype.getContext = () => {}; ================================================ FILE: packages/react-vis/jestBabelTransform.js ================================================ /*eslint-env node*/ const babelJest = require('babel-jest'); // see https://github.com/facebook/jest/issues/7359#issuecomment-471509996 module.exports = babelJest.createTransformer({ rootMode: 'upward' }); ================================================ FILE: packages/react-vis/package.json ================================================ { "name": "react-vis", "version": "1.12.1", "license": "MIT", "author": "Visualization Team ", "description": "Data visualization library based on React and d3.", "main": "dist/dist.min.js", "module": "es", "jsnext:main": "es", "files": [ "dist", "es" ], "repository": { "type": "git", "url": "https://github.com/uber-common/react-vis.git" }, "bugs": { "url": "https://github.com/uber-common/react-vis/issues/new", "email": "visualization@uber.com" }, "scripts": { "docs": "./publish-docs.sh", "clean": "rm -rf dist es bundle.* index.html && mkdir dist es", "start": "(cd ../showcase && command -v yarn >/dev/null && yarn start)", "build:browser": "./build-browser.sh", "build": "yarn run clean && babel --root-mode upward src -d dist --copy-files && BABEL_ENV=es babel --root-mode upward src -d es --copy-files && node-sass src/main.scss dist/style.css --output-style compressed && yarn run build:browser", "lint-styles": "stylelint src/styles/*.scss --syntax scss", "test:windows": "babel-node --inspect ./tests/index.js", "test": "NODE_ENV=development jest", "full-test": "npm run lint && npm run cover", "cover": "NODE_ENV=development jest --coverage", "prettier": "prettier --write $(git ls-files | grep '.js$')" }, "dependencies": { "d3-array": "^3.2.1", "d3-collection": "^1.0.7", "d3-color": "^3.1.0", "d3-contour": "^4.0.0", "d3-format": "^3.1.0", "d3-geo": "^3.1.0", "d3-hexbin": "^0.2.2", "d3-hierarchy": "^3.1.2", "d3-interpolate": "^3.0.1", "d3-sankey": "^0.12.3", "d3-scale": "^4.0.2", "d3-shape": "^3.2.0", "d3-voronoi": "^1.1.4", "deep-equal": "^1.0.1", "global": "^4.3.1", "prop-types": "^15.5.8", "react-motion": "^0.5.2" }, "devDependencies": { "@babel/cli": "^7.0.0", "@babel/core": "^7.0.0", "@babel/node": "^7.0.0", "@babel/plugin-proposal-class-properties": "^7.0.0", "@babel/plugin-proposal-export-default-from": "^7.0.0", "@babel/plugin-proposal-nullish-coalescing-operator": "^7.0.0", "@babel/plugin-proposal-optional-chaining": "^7.0.0", "@babel/plugin-transform-runtime": "^7.0.0", "@babel/preset-env": "^7.0.0", "@babel/preset-react": "^7.0.0", "@babel/register": "^7.0.0", "babel-eslint": "^10.1.0", "babel-jest": "^25.5.1", "babel-plugin-module-resolver": "^4.0.0", "babelify": "^10.0.0", "browserify": "^14.3.0", "canvas": "^2.11.0", "enzyme": "^3.11.0", "enzyme-adapter-react-16": "^1.15.2", "enzyme-to-json": "^3.5.0", "eslint-plugin-jest": "^23.13.2", "jest": "^25.5.4", "jsdom": "^9.9.1", "node-sass": "^4.9.3", "prettier": "^1.14.2", "react": "^17.0.2", "react-addons-test-utils": ">=15.4.2", "react-dom": "^17.0.2", "react-test-renderer": "^16.13.1", "react-vis-showcase": "^0.1.0", "regenerator-runtime": "^0.13.11", "stylelint": "^7.7.1", "stylelint-config-standard": "^15.0.1", "uglify-js": "^2.8.22" }, "peerDependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" }, "keywords": [ "d3", "react", "visualization", "chart", "es6", "babel" ], "nyc": { "exclude": [ "tests/**/*.js", "./utils/react-utils.js" ] }, "engines": { "node": ">=14.18.0", "npm": ">=6.13.0" }, "volta": { "node": "14.18.0", "yarn": "1.22.4" } } ================================================ FILE: packages/react-vis/src/animation.js ================================================ import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {interpolate} from 'd3-interpolate'; import {spring, Motion, presets} from 'react-motion'; const ANIMATION_PROPTYPES = PropTypes.oneOfType([ PropTypes.string, PropTypes.shape({ stiffness: PropTypes.number, nonAnimatedProps: PropTypes.arrayOf(PropTypes.string), damping: PropTypes.number }), PropTypes.bool ]); const propTypes = { animatedProps: PropTypes.arrayOf(PropTypes.string).isRequired, animation: ANIMATION_PROPTYPES, onStart: PropTypes.func, onEnd: PropTypes.func }; /** * Format the animation style object * @param {Object|String} animationStyle - The animation style property, either the name of a * presets are one of noWobble, gentle, wobbly, stiff */ function getAnimationStyle(animationStyle = presets.noWobble) { if (typeof animationStyle === 'string') { return presets[animationStyle] || presets.noWobble; } const {damping, stiffness} = animationStyle; return { ...animationStyle, damping: damping || presets.noWobble.damping, stiffness: stiffness || presets.noWobble.stiffness }; } /** * Extract the animated props from the entire props object. * @param {Object} props Props. * @returns {Object} Object of animated props. */ export function extractAnimatedPropValues(props) { const {animatedProps, ...otherProps} = props; return animatedProps.reduce((result, animatedPropName) => { if (Object.prototype.hasOwnProperty.call(otherProps, animatedPropName)) { result[animatedPropName] = otherProps[animatedPropName]; } return result; }, {}); } class Animation extends PureComponent { constructor(props) { super(props); this._updateInterpolator(props); } componentDidUpdate(props) { this._updateInterpolator(this.props, props); if (props.onStart) { props.onStart(); } } _motionEndHandler = () => { if (this.props.onEnd) { this.props.onEnd(); } }; /** * Render the child into the parent. * @param {Number} i Number generated by the spring. * @returns {React.Component} Rendered react element. * @private */ _renderChildren = ({i}) => { const {children} = this.props; const interpolator = this._interpolator; const child = React.Children.only(children); const interpolatedProps = interpolator ? interpolator(i) : interpolator; // interpolator doesnt play nice with deeply nested objected // so we expose an additional prop for situations like these, soit _data, // which stores the full tree and can be recombined with the sanitized version // after interpolation let data = (interpolatedProps && interpolatedProps.data) || null; if (data && child.props._data) { data = data.map((row, index) => { const correspondingCell = child.props._data[index]; return { ...row, parent: correspondingCell.parent, children: correspondingCell.children }; }); } return React.cloneElement(child, { ...child.props, ...interpolatedProps, data: data || child.props.data || null, // enforce re-rendering _animation: Math.random() }); }; /** * Update the interpolator function and assign it to this._interpolator. * @param {Object} oldProps Old props. * @param {Object} newProps New props. * @private */ _updateInterpolator(oldProps, newProps) { this._interpolator = interpolate( extractAnimatedPropValues(oldProps), newProps ? extractAnimatedPropValues(newProps) : null ); } render() { const animationStyle = getAnimationStyle(this.props.animation); const defaultStyle = {i: 0}; const style = {i: spring(1, animationStyle)}; // In order to make Motion re-run animations each time, the random key is // always passed. // TODO: find a better solution for the spring. const key = Math.random(); return ( {this._renderChildren} ); } } Animation.propTypes = propTypes; Animation.displayName = 'Animation'; export default Animation; export const AnimationPropType = ANIMATION_PROPTYPES; ================================================ FILE: packages/react-vis/src/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. export AbstractSeries from 'plot/series/abstract-series'; export ArcSeries from 'plot/series/arc-series'; export AreaSeries from 'plot/series/area-series'; export Borders from 'plot/borders'; export ChartLabel from 'plot/chart-label'; export CircularGridLines from 'plot/circular-grid-lines'; export ContourSeries from 'plot/series/contour-series'; export Crosshair from 'plot/crosshair'; export CustomSVGSeries from 'plot/series/custom-svg-series'; export DecorativeAxis from 'plot/axis/decorative-axis'; export GradientDefs from 'plot/gradient-defs'; export GridLines from 'plot/grid-lines'; export HeatmapSeries from 'plot/series/heatmap-series'; export HexbinSeries from 'plot/series/hexbin-series'; export Highlight from 'plot/highlight'; export Hint from 'plot/hint'; export HorizontalBarSeries from 'plot/series/horizontal-bar-series'; export HorizontalBarSeriesCanvas from 'plot/series/horizontal-bar-series-canvas'; export HorizontalGridLines from 'plot/horizontal-grid-lines'; export HorizontalRectSeries from 'plot/series/horizontal-rect-series'; export HorizontalRectSeriesCanvas from 'plot/series/horizontal-rect-series-canvas'; export LabelSeries from 'plot/series/label-series'; export LineMarkSeries from 'plot/series/line-mark-series'; export LineMarkSeriesCanvas from 'plot/series/line-mark-series-canvas'; export LineSeries from 'plot/series/line-series'; export LineSeriesCanvas from 'plot/series/line-series-canvas'; export MarkSeries from 'plot/series/mark-series'; export MarkSeriesCanvas from 'plot/series/mark-series-canvas'; export PolygonSeries from 'plot/series/polygon-series'; export VerticalBarSeries from 'plot/series/vertical-bar-series'; export VerticalBarSeriesCanvas from 'plot/series/vertical-bar-series-canvas'; export VerticalGridLines from 'plot/vertical-grid-lines'; export VerticalRectSeries from 'plot/series/vertical-rect-series'; export VerticalRectSeriesCanvas from 'plot/series/vertical-rect-series-canvas'; export Voronoi from 'plot/voronoi'; export RectSeries from 'plot/series/rect-series'; export RectSeriesCanvas from 'plot/series/rect-series-canvas'; export WhiskerSeries from 'plot/series/whisker-series'; export XYPlot from 'plot/xy-plot'; export XAxis from 'plot/axis/x-axis'; export YAxis from 'plot/axis/y-axis'; export ContinuousColorLegend from 'legends/continuous-color-legend'; export ContinuousSizeLegend from 'legends/continuous-size-legend'; export DiscreteColorLegend from 'legends/discrete-color-legend'; export SearchableDiscreteColorLegend from 'legends/searchable-discrete-color-legend'; export ParallelCoordinates from 'parallel-coordinates'; export RadarChart from 'radar-chart'; export RadialChart from 'radial-chart'; export Sankey from 'sankey'; export Sunburst from 'sunburst'; export Treemap from 'treemap'; export ContentClipPath from './plot/content-clip-path'; export { makeHeightFlexible, makeVisFlexible, makeWidthFlexible, FlexibleXYPlot, FlexibleWidthXYPlot, FlexibleHeightXYPlot } from './make-vis-flexible'; export AxisUtils from 'utils/axis-utils'; export ScaleUtils from 'utils/scales-utils'; ================================================ FILE: packages/react-vis/src/legends/continuous-color-legend.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {CONTINUOUS_COLOR_RANGE} from 'theme'; import {getCombinedClassName} from 'utils/styling-utils'; const propTypes = { className: PropTypes.string, height: PropTypes.number, endColor: PropTypes.string, endTitle: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, midColor: PropTypes.string, midTitle: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), startColor: PropTypes.string, startTitle: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, width: PropTypes.number }; const defaultProps = { className: '', startColor: CONTINUOUS_COLOR_RANGE[0], endColor: CONTINUOUS_COLOR_RANGE[1] }; function ContinuousColorLegend({ startColor, midColor, endColor, startTitle, midTitle, endTitle, height, width, className }) { const colors = [startColor]; if (midColor) { colors.push(midColor); } colors.push(endColor); return (
{startTitle} {endTitle} {midTitle ? ( {midTitle} ) : null}
); } ContinuousColorLegend.displayName = 'ContinuousColorLegend'; ContinuousColorLegend.propTypes = propTypes; ContinuousColorLegend.defaultProps = defaultProps; export default ContinuousColorLegend; ================================================ FILE: packages/react-vis/src/legends/continuous-size-legend.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {getCombinedClassName} from 'utils/styling-utils'; const propTypes = { className: PropTypes.string, circlesTotal: PropTypes.number, endSize: PropTypes.number, endTitle: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, height: PropTypes.number, startSize: PropTypes.number, startTitle: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) .isRequired, width: PropTypes.number }; const defaultProps = { circlesTotal: 10, className: '', endSize: 20, startSize: 2 }; function ContinuousSizeLegend({ startTitle, endTitle, startSize, endSize, circlesTotal, height, width, className }) { const circles = []; const step = (endSize - startSize) / (circlesTotal - 1); for (let i = 0; i < circlesTotal; i++) { const size = step * i + startSize; circles.push(
); // Add the separator in order to justify the content (otherwise the tags // will be stacked together without any margins around). circles.push(' '); } return (
{circles}
{startTitle} {endTitle}
); } ContinuousSizeLegend.displayName = 'ContinuousSizeLegend'; ContinuousSizeLegend.propTypes = propTypes; ContinuousSizeLegend.defaultProps = defaultProps; export default ContinuousSizeLegend; ================================================ FILE: packages/react-vis/src/legends/discrete-color-legend-item.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; const STROKE_STYLES = { dashed: '6, 2', solid: null }; function DiscreteColorLegendItem({ color, strokeDasharray, strokeStyle, strokeWidth, disabled, onClick, orientation, onMouseEnter, onMouseLeave, title }) { let className = `rv-discrete-color-legend-item ${orientation}`; if (disabled) { className += ' disabled'; } if (onClick) { className += ' clickable'; } const strokeDasharrayStyle = STROKE_STYLES[strokeStyle] || strokeDasharray; return (
{title}
); } DiscreteColorLegendItem.propTypes = { color: PropTypes.string.isRequired, disabled: PropTypes.bool, title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, onClick: PropTypes.func, onMouseEnter: PropTypes.func, onMouseLeave: PropTypes.func, orientation: PropTypes.oneOf(['vertical', 'horizontal']).isRequired, strokeDasharray: PropTypes.string, strokeWidth: PropTypes.number, strokeStyle: PropTypes.oneOf(Object.keys(STROKE_STYLES)) }; DiscreteColorLegendItem.defaultProps = { disabled: false, strokeStyle: 'solid' }; DiscreteColorLegendItem.displayName = 'DiscreteColorLegendItem'; export default DiscreteColorLegendItem; ================================================ FILE: packages/react-vis/src/legends/discrete-color-legend.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import DiscreteColorLegendItem from 'legends/discrete-color-legend-item'; import {DISCRETE_COLOR_RANGE} from 'theme'; import {getCombinedClassName} from 'utils/styling-utils'; function DiscreteColorLegend({ className, colors, height, items, onItemClick, onItemMouseEnter, onItemMouseLeave, orientation, style, width }) { return (
{items.map((item, i) => ( onItemClick(item, i, e) : null} onMouseEnter={ onItemMouseEnter ? e => onItemMouseEnter(item, i, e) : null } onMouseLeave={ onItemMouseEnter ? e => onItemMouseLeave(item, i, e) : null } /> ))}
); } DiscreteColorLegend.displayName = 'DiscreteColorLegendItem'; DiscreteColorLegend.propTypes = { className: PropTypes.string, items: PropTypes.arrayOf( PropTypes.oneOfType([ PropTypes.shape({ title: PropTypes.oneOfType([PropTypes.string, PropTypes.element]) .isRequired, color: PropTypes.string, disabled: PropTypes.bool }), PropTypes.string.isRequired, PropTypes.element ]) ).isRequired, onItemClick: PropTypes.func, onItemMouseEnter: PropTypes.func, onItemMouseLeave: PropTypes.func, height: PropTypes.number, width: PropTypes.number, orientation: PropTypes.oneOf(['vertical', 'horizontal']) }; DiscreteColorLegend.defaultProps = { className: '', colors: DISCRETE_COLOR_RANGE, orientation: 'vertical' }; export default DiscreteColorLegend; ================================================ FILE: packages/react-vis/src/legends/searchable-discrete-color-legend.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import DiscreteColorLegend from 'legends/discrete-color-legend'; import {getCombinedClassName} from 'utils/styling-utils'; const propTypes = { ...DiscreteColorLegend.propTypes, searchText: PropTypes.string, onSearchChange: PropTypes.func, searchPlaceholder: PropTypes.string, searchFn: PropTypes.func }; const defaultProps = { className: '', searchText: '', searchFn: (items, s) => items.filter( item => String(item.title || item) .toLowerCase() .indexOf(s) !== -1 ) }; function SearchableDiscreteColorLegend(props) { const { className, colors, height, items, onItemClick, onItemMouseEnter, onItemMouseLeave, onSearchChange, orientation, searchFn, searchPlaceholder, searchText, width } = props; const onChange = onSearchChange ? ({target: {value}}) => onSearchChange(value) : null; const filteredItems = searchFn(items, searchText); return (
); } SearchableDiscreteColorLegend.propTypes = propTypes; SearchableDiscreteColorLegend.defaultProps = defaultProps; SearchableDiscreteColorLegend.displayName = 'SearchableDiscreteColorLegend'; export default SearchableDiscreteColorLegend; ================================================ FILE: packages/react-vis/src/main.scss ================================================ // special tag for using to check if the style file has been imported .react-vis-magic-css-import-rule { display: inherit; } @import 'styles/treemap'; @import 'styles/plot'; @import 'styles/legends'; @import 'styles/radial-chart'; ================================================ FILE: packages/react-vis/src/make-vis-flexible.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import window from 'global/window'; import XYPlot from 'plot/xy-plot'; import {getDOMNode} from 'utils/react-utils'; const CONTAINER_REF = 'container'; // As a performance enhancement, we want to only listen once const resizeSubscribers = []; const DEBOUNCE_DURATION = 100; let timeoutId = null; /** * Calls each subscriber, debounced to the */ function debounceEmitResize() { window.clearTimeout(timeoutId); timeoutId = window.setTimeout(emitResize, DEBOUNCE_DURATION); } /** * Calls each subscriber once syncronously. */ function emitResize() { resizeSubscribers.forEach(cb => cb()); } /** * Add the given callback to the list of subscribers to be caled when the * window resizes. Returns a function that, when called, removes the given * callback from the list of subscribers. This function is also resposible for * adding and removing the resize listener on `window`. * * @param {Function} cb - Subscriber callback function * @returns {Function} Unsubscribe function */ function subscribeToDebouncedResize(cb) { resizeSubscribers.push(cb); // if we go from zero to one Flexible components instances, add the listener if (resizeSubscribers.length === 1) { window.addEventListener('resize', debounceEmitResize); } return function unsubscribe() { removeSubscriber(cb); // if we have no Flexible components, remove the listener if (resizeSubscribers.length === 0) { window.clearTimeout(timeoutId); window.removeEventListener('resize', debounceEmitResize); } }; } /** * Helper for removing the given callback from the list of subscribers. * * @param {Function} cb - Subscriber callback function */ function removeSubscriber(cb) { const index = resizeSubscribers.indexOf(cb); if (index > -1) { resizeSubscribers.splice(index, 1); } } /** * Helper for getting a display name for the child component * @param {*} Component React class for the child component. * @returns {String} The child components name */ function getDisplayName(Component) { return Component.displayName || Component.name || 'Component'; } /** * Add the ability to stretch the visualization on window resize. * @param {*} Component React class for the child component. * @returns {*} Flexible component. */ function makeFlexible(Component, isWidthFlexible, isHeightFlexible) { const ResultClass = class extends React.Component { static get propTypes() { const {height, width, ...otherPropTypes} = Component.propTypes; // eslint-disable-line no-unused-vars return otherPropTypes; } constructor(props) { super(props); this.state = { height: 0, width: 0 }; } /** * Get the width of the container and assign the width. * @private */ _onResize = () => { const containerElement = getDOMNode(this[CONTAINER_REF]); const {offsetHeight, offsetWidth} = containerElement; const newHeight = this.state.height === offsetHeight ? {} : {height: offsetHeight}; const newWidth = this.state.width === offsetWidth ? {} : {width: offsetWidth}; this.setState({ ...newHeight, ...newWidth }); }; componentDidMount() { this._onResize(); this.cancelSubscription = subscribeToDebouncedResize(this._onResize); } UNSAFE_componentWillReceiveProps() { this._onResize(); } componentWillUnmount() { this.cancelSubscription(); } render() { const {height, width} = this.state; const props = { ...this.props, animation: height === 0 && width === 0 ? null : this.props.animation }; const updatedDimensions = { ...(isHeightFlexible ? {height} : {}), ...(isWidthFlexible ? {width} : {}) }; return (
(this[CONTAINER_REF] = ref)} style={{width: '100%', height: '100%'}} >
); } }; ResultClass.displayName = `Flexible${getDisplayName(Component)}`; return ResultClass; } export function makeHeightFlexible(component) { return makeFlexible(component, false, true); } export function makeVisFlexible(component) { return makeFlexible(component, true, true); } export function makeWidthFlexible(component) { return makeFlexible(component, true, false); } export const FlexibleWidthXYPlot = makeWidthFlexible(XYPlot); export const FlexibleHeightXYPlot = makeHeightFlexible(XYPlot); export const FlexibleXYPlot = makeVisFlexible(XYPlot); ================================================ FILE: packages/react-vis/src/parallel-coordinates/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {scaleLinear} from 'd3-scale'; import {format} from 'd3-format'; import {AnimationPropType} from 'animation'; import XYPlot from 'plot/xy-plot'; import {DISCRETE_COLOR_RANGE} from 'theme'; import { MarginPropType, getInnerDimensions, DEFAULT_MARGINS } from 'utils/chart-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import LineSeries from 'plot/series/line-series'; import LineMarkSeries from 'plot/series/line-mark-series'; import LabelSeries from 'plot/series/label-series'; import DecorativeAxis from 'plot/axis/decorative-axis'; import Highlight from 'plot/highlight'; const predefinedClassName = 'rv-parallel-coordinates-chart'; const DEFAULT_FORMAT = format('.2r'); /** * Generate axes for each of the domains * @param {Object} props - props.animation {Boolean} - props.domains {Array} array of object specifying the way each axis is to be plotted - props.style {object} style object for the whole chart - props.tickFormat {Function} formatting function for axes * @return {Array} the plotted axis components */ function getAxes(props) { const {animation, domains, style, tickFormat} = props; return domains.map((domain, index) => { const sortedDomain = domain.domain; const domainTickFormat = t => { return domain.tickFormat ? domain.tickFormat(t) : tickFormat(t); }; return ( ); }); } /** * Generate labels for the ends of the axes * @param {Object} props - props.domains {Array} array of object specifying the way each axis is to be plotted - props.style {object} style object for just the labels * @return {Array} the prepped data for the labelSeries */ function getLabels(props) { const {domains, style} = props; return domains.map(domain => { return { x: domain.name, y: 1.1, label: domain.name, style }; }); } /** * Generate the actual lines to be plotted * @param {Object} props - props.animation {Boolean} - props.data {Array} array of object specifying what values are to be plotted - props.domains {Array} array of object specifying the way each axis is to be plotted - props.style {object} style object for the whole chart - props.showMarks {Bool} whether or not to use the line mark series * @return {Array} the plotted axis components */ function getLines(props) { const { animation, brushFilters, colorRange, domains, data, style, showMarks } = props; const scales = domains.reduce((acc, {domain, name}) => { acc[name] = scaleLinear() .domain(domain) .range([0, 1]); return acc; }, {}); // const return data.map((row, rowIndex) => { let withinFilteredRange = true; const mappedData = domains.map(domain => { const {getValue, name} = domain; // watch out! Gotcha afoot // yVal after being scale is in [0, 1] range const yVal = scales[name](getValue ? getValue(row) : row[name]); const filter = brushFilters[name]; // filter value after being scale back from pixel space is also in [0, 1] if (filter && (yVal < filter.min || yVal > filter.max)) { withinFilteredRange = false; } return {x: name, y: yVal}; }); const selectedName = `${predefinedClassName}-line`; const unselectedName = `${selectedName} ${predefinedClassName}-line-unselected`; const lineProps = { animation, className: withinFilteredRange ? selectedName : unselectedName, key: `${rowIndex}-polygon`, data: mappedData, color: row.color || colorRange[rowIndex % colorRange.length], style: {...style.lines, ...(row.style || {})} }; if (!withinFilteredRange) { lineProps.style = { ...lineProps.style, ...style.deselectedLineStyle }; } return showMarks ? ( ) : ( ); }); } class ParallelCoordinates extends Component { state = { brushFilters: {} }; render() { const {brushFilters} = this.state; const { animation, brushing, className, children, colorRange, data, domains, height, hideInnerMostValues, margin, onMouseLeave, onMouseEnter, showMarks, style, tickFormat, width } = this.props; const axes = getAxes({ domains, animation, hideInnerMostValues, style, tickFormat }); const lines = getLines({ animation, brushFilters, colorRange, domains, data, showMarks, style }); const labelSeries = ( ); const {marginLeft, marginRight} = getInnerDimensions( this.props, DEFAULT_MARGINS ); return ( {children} {axes.concat(lines).concat(labelSeries)} {brushing && domains.map(d => { const trigger = row => { this.setState({ brushFilters: { ...brushFilters, [d.name]: row ? {min: row.bottom, max: row.top} : null } }); }; return ( ); })} ); } } ParallelCoordinates.displayName = 'ParallelCoordinates'; ParallelCoordinates.propTypes = { animation: AnimationPropType, brushing: PropTypes.bool, className: PropTypes.string, colorType: PropTypes.string, colorRange: PropTypes.arrayOf(PropTypes.string), data: PropTypes.arrayOf(PropTypes.object).isRequired, domains: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string.isRequired, domain: PropTypes.arrayOf(PropTypes.number).isRequired, tickFormat: PropTypes.func }) ).isRequired, height: PropTypes.number.isRequired, margin: MarginPropType, style: PropTypes.shape({ axes: PropTypes.object, labels: PropTypes.object, lines: PropTypes.object }), showMarks: PropTypes.bool, tickFormat: PropTypes.func, width: PropTypes.number.isRequired }; ParallelCoordinates.defaultProps = { className: '', colorType: 'category', colorRange: DISCRETE_COLOR_RANGE, style: { axes: { line: {}, ticks: {}, text: {} }, labels: { fontSize: 10, textAnchor: 'middle' }, lines: { strokeWidth: 1, strokeOpacity: 1 }, deselectedLineStyle: { strokeOpacity: 0.1 } }, tickFormat: DEFAULT_FORMAT }; export default ParallelCoordinates; ================================================ FILE: packages/react-vis/src/plot/axis/axis-line.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {ORIENTATION} from 'utils/axis-utils'; const {LEFT, RIGHT, TOP, BOTTOM} = ORIENTATION; const propTypes = { height: PropTypes.number.isRequired, style: PropTypes.object, orientation: PropTypes.oneOf([LEFT, RIGHT, TOP, BOTTOM]).isRequired, width: PropTypes.number.isRequired }; const defaultProps = { style: {} }; function AxisLine({orientation, width, height, style}) { let lineProps; if (orientation === LEFT) { lineProps = { x1: width, x2: width, y1: 0, y2: height }; } else if (orientation === RIGHT) { lineProps = { x1: 0, x2: 0, y1: 0, y2: height }; } else if (orientation === TOP) { lineProps = { x1: 0, x2: width, y1: height, y2: height }; } else { lineProps = { x1: 0, x2: width, y1: 0, y2: 0 }; } return ( ); } AxisLine.defaultProps = defaultProps; AxisLine.displayName = 'AxisLine'; AxisLine.propTypes = propTypes; export default AxisLine; ================================================ FILE: packages/react-vis/src/plot/axis/axis-ticks.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {ORIENTATION, getTickValues} from 'utils/axis-utils'; import {getAttributeScale} from 'utils/scales-utils'; const {LEFT, RIGHT, TOP, BOTTOM} = ORIENTATION; const propTypes = { height: PropTypes.number.isRequired, orientation: PropTypes.oneOf([LEFT, RIGHT, TOP, BOTTOM]).isRequired, style: PropTypes.object, width: PropTypes.number.isRequired }; const defaultProps = { style: {} }; function _getTickFormatFn(scale, tickTotal, tickFormat) { return !tickFormat ? scale.tickFormat ? scale.tickFormat(tickTotal) : v => v : tickFormat; } class AxisTicks extends React.Component { /** * Check if axis ticks should be mirrored (for the right and top positions. * @returns {boolean} True if mirrored. * @private */ _areTicksWrapped() { const {orientation} = this.props; return orientation === LEFT || orientation === TOP; } _getTickContainerPropsGetterFn() { if (this._isAxisVertical()) { return pos => { return {transform: `translate(0, ${pos})`}; }; } return pos => { return {transform: `translate(${pos}, 0)`}; }; } /** * Get attributes for the label of the tick. * @returns {Object} Object with properties. * @private */ _getTickLabelProps() { const { orientation, tickLabelAngle, tickSize, tickSizeOuter = tickSize, tickPadding = tickSize } = this.props; // Assign the text orientation inside the label of the tick mark. let textAnchor; if (orientation === LEFT || (orientation === BOTTOM && tickLabelAngle)) { textAnchor = 'end'; } else if ( orientation === RIGHT || (orientation === TOP && tickLabelAngle) ) { textAnchor = 'start'; } else { textAnchor = 'middle'; } // The label's position is translated to the given padding and then the // label is rotated to the given angle. const isVertical = this._isAxisVertical(); const wrap = this._areTicksWrapped() ? -1 : 1; const labelOffset = wrap * (tickSizeOuter + tickPadding); const transform = (isVertical ? `translate(${labelOffset}, 0)` : `translate(0, ${labelOffset})`) + (tickLabelAngle ? ` rotate(${tickLabelAngle})` : ''); // Set the vertical offset of the label according to the position of // the axis. const dy = orientation === TOP || tickLabelAngle ? '0' : orientation === BOTTOM ? '0.72em' : '0.32em'; return { textAnchor, dy, transform }; } /** * Get the props of the tick line. * @returns {Object} Props. * @private */ _getTickLineProps() { const { tickSize, tickSizeOuter = tickSize, tickSizeInner = tickSize } = this.props; const isVertical = this._isAxisVertical(); const tickXAttr = isVertical ? 'y' : 'x'; const tickYAttr = isVertical ? 'x' : 'y'; const wrap = this._areTicksWrapped() ? -1 : 1; return { [`${tickXAttr}1`]: 0, [`${tickXAttr}2`]: 0, [`${tickYAttr}1`]: -wrap * tickSizeInner, [`${tickYAttr}2`]: wrap * tickSizeOuter }; } /** * Gets if the axis is vertical. * @returns {boolean} True if vertical. * @private */ _isAxisVertical() { const {orientation} = this.props; return orientation === LEFT || orientation === RIGHT; } render() { const { attr, orientation, width, height, style, tickFormat, tickTotal, tickValues } = this.props; const x = orientation === LEFT ? width : 0; const y = orientation === TOP ? height : 0; const scale = getAttributeScale(this.props, attr); const values = getTickValues(scale, tickTotal, tickValues); const tickFormatFn = _getTickFormatFn(scale, tickTotal, tickFormat); const translateFn = this._getTickContainerPropsGetterFn(); const pathProps = this._getTickLineProps(); const textProps = this._getTickLabelProps(); const ticks = values.map((v, i) => { const pos = scale(v); const labelNode = tickFormatFn(v, i, scale, tickTotal); const shouldRenderAsOwnNode = React.isValidElement(labelNode) && !['tspan', 'textPath'].includes(labelNode.type); const shouldAddProps = labelNode && typeof labelNode.type !== 'string'; return ( {shouldRenderAsOwnNode ? ( React.cloneElement( labelNode, shouldAddProps ? { ...textProps, containerWidth: width, tickCount: values.length } : undefined ) ) : ( {labelNode} )} ); }); return ( {ticks} ); } } AxisTicks.defaultProps = defaultProps; AxisTicks.displayName = 'AxisTicks'; AxisTicks.propTypes = propTypes; AxisTicks.requiresSVG = true; export default AxisTicks; ================================================ FILE: packages/react-vis/src/plot/axis/axis-title.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {ORIENTATION} from 'utils/axis-utils'; // Assuming that 16px = 1em const ADJUSTMENT_FOR_TEXT_SIZE = 16; const MARGIN = 6; const {LEFT, RIGHT, TOP, BOTTOM} = ORIENTATION; const defaultProps = { position: 'end' }; /** * Compute transformations, keyed by orientation * @param {number} width - width of axis * @param {number} height - height of axis * @returns {Object} Object of transformations, keyed by orientation */ const transformation = (width, height) => ({ [LEFT]: { end: { x: ADJUSTMENT_FOR_TEXT_SIZE, y: MARGIN, rotation: -90, textAnchor: 'end' }, middle: { x: ADJUSTMENT_FOR_TEXT_SIZE, y: height / 2 - MARGIN, rotation: -90, textAnchor: 'middle' }, start: { x: ADJUSTMENT_FOR_TEXT_SIZE, y: height - MARGIN, rotation: -90, textAnchor: 'start' } }, [RIGHT]: { end: { x: ADJUSTMENT_FOR_TEXT_SIZE * -0.5, y: MARGIN, rotation: -90, textAnchor: 'end' }, middle: { x: ADJUSTMENT_FOR_TEXT_SIZE * -0.5, y: height / 2 - MARGIN, rotation: -90, textAnchor: 'middle' }, start: { x: ADJUSTMENT_FOR_TEXT_SIZE * -0.5, y: height - MARGIN, rotation: -90, textAnchor: 'start' } }, [TOP]: { start: { x: MARGIN, y: ADJUSTMENT_FOR_TEXT_SIZE, rotation: 0, textAnchor: 'start' }, middle: { x: width / 2 - MARGIN, y: ADJUSTMENT_FOR_TEXT_SIZE, rotation: 0, textAnchor: 'middle' }, end: { x: width - MARGIN, y: ADJUSTMENT_FOR_TEXT_SIZE, rotation: 0, textAnchor: 'end' } }, [BOTTOM]: { start: { x: MARGIN, y: -MARGIN, rotation: 0, textAnchor: 'start' }, middle: { x: width / 2 - MARGIN, y: -MARGIN, rotation: 0, textAnchor: 'middle' }, end: { x: width - MARGIN, y: -MARGIN, rotation: 0, textAnchor: 'end' } } }); const propTypes = { width: PropTypes.number.isRequired, height: PropTypes.number.isRequired, orientation: PropTypes.oneOf([LEFT, RIGHT, TOP, BOTTOM]).isRequired, style: PropTypes.object, title: PropTypes.string.isRequired }; function AxisTitle({orientation, position, width, height, style, title}) { const outerGroupTranslateX = orientation === LEFT ? width : 0; const outerGroupTranslateY = orientation === TOP ? height : 0; const outerGroupTransform = `translate(${outerGroupTranslateX}, ${outerGroupTranslateY})`; const {x, y, rotation, textAnchor} = transformation(width, height)[ orientation ][position]; const innerGroupTransform = `translate(${x}, ${y}) rotate(${rotation})`; return ( {title} ); } AxisTitle.displayName = 'AxisTitle'; AxisTitle.propTypes = propTypes; AxisTitle.defaultProps = defaultProps; export default AxisTitle; ================================================ FILE: packages/react-vis/src/plot/axis/axis.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {ORIENTATION, getTicksTotalFromSize} from 'utils/axis-utils'; import {getAttributeScale} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AxisLine from './axis-line'; import AxisTicks from './axis-ticks'; import AxisTitle from './axis-title'; const defaultAnimatedProps = [ 'xRange', 'yRange', 'xDomain', 'yDomain', 'width', 'height', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'tickSize', 'tickTotal', 'tickSizeInner', 'tickSizeOuter' ]; const {LEFT, RIGHT, TOP, BOTTOM} = ORIENTATION; const propTypes = { orientation: PropTypes.oneOf([LEFT, RIGHT, TOP, BOTTOM]), attr: PropTypes.string.isRequired, attrAxis: PropTypes.string, width: PropTypes.number, height: PropTypes.number, top: PropTypes.number, left: PropTypes.number, title: PropTypes.string, style: PropTypes.object, className: PropTypes.string, hideTicks: PropTypes.bool, hideLine: PropTypes.bool, on0: PropTypes.bool, tickLabelAngle: PropTypes.number, tickSize: PropTypes.number, tickSizeInner: PropTypes.number, tickSizeOuter: PropTypes.number, tickPadding: PropTypes.number, tickValues: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]) ), tickFormat: PropTypes.func, tickTotal: PropTypes.number, // Not expected to be used by the users. // TODO: Add underscore to these properties later. marginTop: PropTypes.number, marginBottom: PropTypes.number, marginLeft: PropTypes.number, marginRight: PropTypes.number, innerWidth: PropTypes.number, innerHeight: PropTypes.number }; const defaultProps = { className: '', on0: false, style: {}, tickSize: 6, tickPadding: 8, orientation: BOTTOM }; const predefinedClassName = 'rv-xy-plot__axis'; const VERTICAL_CLASS_NAME = 'rv-xy-plot__axis--vertical'; const HORIZONTAL_CLASS_NAME = 'rv-xy-plot__axis--horizontal'; class Axis extends PureComponent { /** * Define the default values depending on the data passed from the outside. * @returns {*} Object of default properties. * @private */ _getDefaultAxisProps() { const { innerWidth, innerHeight, marginTop, marginBottom, marginLeft, marginRight, orientation } = this.props; if (orientation === BOTTOM) { return { tickTotal: getTicksTotalFromSize(innerWidth), top: innerHeight + marginTop, left: marginLeft, width: innerWidth, height: marginBottom }; } else if (orientation === TOP) { return { tickTotal: getTicksTotalFromSize(innerWidth), top: 0, left: marginLeft, width: innerWidth, height: marginTop }; } else if (orientation === LEFT) { return { tickTotal: getTicksTotalFromSize(innerHeight), top: marginTop, left: 0, width: marginLeft, height: innerHeight }; } return { tickTotal: getTicksTotalFromSize(innerHeight), top: marginTop, left: marginLeft + innerWidth, width: marginRight, height: innerHeight }; } render() { const {animation} = this.props; if (animation) { const animatedProps = animation.nonAnimatedProps ? defaultAnimatedProps.filter( prop => animation.nonAnimatedProps.indexOf(prop) < 0 ) : defaultAnimatedProps; return ( ); } const props = { ...this._getDefaultAxisProps(), ...this.props }; const { attrAxis, className, height, hideLine, hideTicks, left, marginTop, on0, orientation, position, style, title, top, width } = props; const isVertical = [LEFT, RIGHT].indexOf(orientation) > -1; const axisClassName = isVertical ? VERTICAL_CLASS_NAME : HORIZONTAL_CLASS_NAME; let leftPos = left; let topPos = top; if (on0) { const scale = getAttributeScale(props, attrAxis); if (isVertical) { leftPos = scale(0); } else { topPos = marginTop + scale(0); } } return ( {!hideLine && ( )} {!hideTicks && ( )} {title ? ( ) : null} ); } } Axis.displayName = 'Axis'; Axis.propTypes = propTypes; Axis.defaultProps = defaultProps; Axis.requiresSVG = true; export default Axis; ================================================ FILE: packages/react-vis/src/plot/axis/decorative-axis-ticks.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {generatePoints, getAxisAngle} from 'utils/axis-utils'; /** * Generate the actual polygons to be plotted * @param {Object} props - props.animation {Boolean} - props.axisDomain {Array} a pair of values specifying the domain of the axis - props.numberOfTicks{Number} the number of ticks on the axis - props.axisStart {Object} a object specify in cartesian space the start of the axis example: {x: 0, y: 0} - props.axisEnd {Object} a object specify in cartesian space the start of the axis - props.tickValue {Func} a formatting function for the tick values - props.tickSize {Number} a pixel size of the axis - props.style {Object} The style object for the axis * @return {Component} the plotted axis */ export default function decorativeAxisTick(props) { const { axisDomain, numberOfTicks, axisStart, axisEnd, tickValue, tickSize, style } = props; const {points} = generatePoints({ axisStart, axisEnd, numberOfTicks, axisDomain }); // add a quarter rotation to make ticks orthogonal to axis const tickAngle = getAxisAngle(axisStart, axisEnd) + Math.PI / 2; return points.map((point, index) => { const tickProps = { x1: 0, y1: 0, x2: tickSize * Math.cos(tickAngle), y2: tickSize * Math.sin(tickAngle), ...style.ticks }; const textProps = { x: tickSize * Math.cos(tickAngle), y: tickSize * Math.sin(tickAngle), textAnchor: 'start', ...style.text }; return ( {tickValue(point.text)} ); }); } ================================================ FILE: packages/react-vis/src/plot/axis/decorative-axis.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {format} from 'd3-format'; import PropTypes from 'prop-types'; import AbstractSeries from 'plot/series/abstract-series'; import DecorativeAxisTicks from './decorative-axis-ticks'; import Animation from 'animation'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-xy-manipulable-axis rv-xy-plot__axis'; const animatedProps = [ 'xRange', 'yRange', 'xDomain', 'yDomain', 'width', 'height', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'tickSize', 'tickTotal', 'tickSizeInner', 'tickSizeOuter' ]; class DecorativeAxis extends AbstractSeries { render() { const { animation, className, marginLeft, marginTop, axisStart, axisEnd, axisDomain, numberOfTicks, tickValue, tickSize, style } = this.props; if (animation) { return ( ); } const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); return ( {DecorativeAxisTicks({ axisDomain, axisEnd: {x: x(axisEnd), y: y(axisEnd)}, axisStart: {x: x(axisStart), y: y(axisStart)}, numberOfTicks, tickValue, tickSize, style })} ); } } const DEFAULT_FORMAT = format('.2r'); DecorativeAxis.defaultProps = { className: '', numberOfTicks: 10, tickValue: d => DEFAULT_FORMAT(d), tickSize: 5, style: { line: { strokeWidth: 1 }, ticks: { strokeWidth: 2 }, text: {} } }; DecorativeAxis.propTypes = { ...AbstractSeries.propTypes, axisDomain: PropTypes.arrayOf(PropTypes.number).isRequired, axisEnd: PropTypes.shape({ x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }).isRequired, axisStart: PropTypes.shape({ x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]) }).isRequired, className: PropTypes.string, numberOfTicks: PropTypes.number, tickValue: PropTypes.func, tickSize: PropTypes.number, style: PropTypes.shape({ line: PropTypes.object, ticks: PropTypes.object, text: PropTypes.object }) }; DecorativeAxis.displayName = 'DecorativeAxis'; export default DecorativeAxis; ================================================ FILE: packages/react-vis/src/plot/axis/x-axis.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {ORIENTATION} from 'utils/axis-utils'; import Axis from './axis'; const {TOP, BOTTOM} = ORIENTATION; const propTypes = { ...Axis.propTypes, orientation: PropTypes.oneOf([TOP, BOTTOM]) }; const defaultProps = { orientation: BOTTOM, attr: 'x', attrAxis: 'y' }; function XAxis(props) { return ; } XAxis.displayName = 'XAxis'; XAxis.propTypes = propTypes; XAxis.defaultProps = defaultProps; XAxis.requiresSVG = true; export default XAxis; ================================================ FILE: packages/react-vis/src/plot/axis/y-axis.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {ORIENTATION} from 'utils/axis-utils'; import Axis from './axis'; const {LEFT, RIGHT} = ORIENTATION; const propTypes = { ...Axis.propTypes, orientation: PropTypes.oneOf([LEFT, RIGHT]) }; const defaultProps = { orientation: LEFT, attr: 'y', attrAxis: 'x' }; function YAxis(props) { return ; } YAxis.displayName = 'YAxis'; YAxis.propTypes = propTypes; YAxis.defaultProps = defaultProps; YAxis.requiresSVG = true; export default YAxis; ================================================ FILE: packages/react-vis/src/plot/borders.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {getCombinedClassName} from 'utils/styling-utils'; const propTypes = { style: PropTypes.shape({ bottom: PropTypes.object, left: PropTypes.object, right: PropTypes.object, top: PropTypes.object }), // supplied by xyplot marginTop: PropTypes.number, marginBottom: PropTypes.number, marginLeft: PropTypes.number, marginRight: PropTypes.number, innerWidth: PropTypes.number, innerHeight: PropTypes.number }; const CLASSES = { bottom: 'rv-xy-plot__borders-bottom', container: 'rv-xy-plot__borders', left: 'rv-xy-plot__borders-left', right: 'rv-xy-plot__borders-right', top: 'rv-xy-plot__borders-top' }; function Borders(props) { const { marginTop, marginBottom, marginLeft, marginRight, innerWidth, innerHeight, style, className } = props; const height = innerHeight + marginTop + marginBottom; const width = innerWidth + marginLeft + marginRight; return ( ); } Borders.displayName = 'Borders'; Borders.defaultProps = { className: '', style: { all: {}, bottom: {}, left: {}, right: {}, top: {} } }; Borders.propTypes = propTypes; Borders.requiresSVG = true; export default Borders; ================================================ FILE: packages/react-vis/src/plot/chart-label.js ================================================ // Copyright (c) 2018 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {getCombinedClassName} from 'utils/styling-utils'; class ChartLabel extends React.PureComponent { static get requiresSVG() { return true; } render() { const { // rv defined innerHeight, innerWidth, marginBottom, marginLeft, marginRight, marginTop, // user defined className, includeMargin, style, text, xPercent, yPercent } = this.props; const width = innerWidth + (includeMargin ? marginLeft + marginRight : 0); const height = innerHeight + (includeMargin ? marginTop + marginBottom : 0); const xPos = width * xPercent + (includeMargin ? 0 : marginLeft); const yPos = height * yPercent + (includeMargin ? marginTop : 0); return ( {text} ); } } ChartLabel.displayName = 'ChartLabel'; ChartLabel.propTypes = { className: PropTypes.string, includeMargin: PropTypes.bool, style: PropTypes.object, text: PropTypes.string.isRequired, xPercent: PropTypes.number.isRequired, yPercent: PropTypes.number.isRequired }; ChartLabel.defaultProps = { className: '', includeMargin: true, text: '', xPercent: 0, yPercent: 0, style: {} }; export default ChartLabel; ================================================ FILE: packages/react-vis/src/plot/circular-grid-lines.js ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // 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. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {getAttributeScale} from 'utils/scales-utils'; import Animation, {AnimationPropType} from 'animation'; import {getTicksTotalFromSize, getTickValues} from '../utils/axis-utils'; const animatedProps = [ 'xRange', 'yRange', 'xDomain', 'yDomain', 'width', 'height', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'tickTotal' ]; class CircularGridLines extends PureComponent { _getDefaultProps() { const {innerWidth, innerHeight, marginTop, marginLeft} = this.props; return { left: marginLeft, top: marginTop, width: innerWidth, height: innerHeight, style: {}, tickTotal: getTicksTotalFromSize(Math.min(innerWidth, innerHeight)) }; } render() { const {animation, centerX, centerY} = this.props; if (animation) { return ( ); } const props = { ...this._getDefaultProps(), ...this.props }; const {tickTotal, tickValues, marginLeft, marginTop, rRange, style} = props; const xScale = getAttributeScale(props, 'x'); const yScale = getAttributeScale(props, 'y'); const values = getTickValues(xScale, tickTotal, tickValues); return ( {values.reduce((res, value, index) => { const radius = xScale(value); if (rRange && (radius < rRange[0] || radius > rRange[1])) { return res; } return res.concat([ ]); }, [])} ); } } CircularGridLines.displayName = 'CircularGridLines'; CircularGridLines.propTypes = { centerX: PropTypes.number, centerY: PropTypes.number, width: PropTypes.number, height: PropTypes.number, top: PropTypes.number, left: PropTypes.number, rRange: PropTypes.arrayOf(PropTypes.number), style: PropTypes.object, tickValues: PropTypes.arrayOf(PropTypes.number), tickTotal: PropTypes.number, animation: AnimationPropType, // generally supplied by xyplot marginTop: PropTypes.number, marginBottom: PropTypes.number, marginLeft: PropTypes.number, marginRight: PropTypes.number, innerWidth: PropTypes.number, innerHeight: PropTypes.number }; CircularGridLines.defaultProps = { centerX: 0, centerY: 0 }; CircularGridLines.requiresSVG = true; export default CircularGridLines; ================================================ FILE: packages/react-vis/src/plot/content-clip-path.js ================================================ import React from 'react'; export default function ContentClipPath(props) { const {id = 'content-area', innerWidth, innerHeight} = props; return ( ); } ContentClipPath.requiresSVG = true; ContentClipPath.displayName = 'ContentClipPath'; ================================================ FILE: packages/react-vis/src/plot/crosshair.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {transformValueToString} from 'utils/data-utils'; import {getAttributeFunctor} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; /** * Format title by detault. * @param {Array} values List of values. * @returns {*} Formatted value or undefined. */ function defaultTitleFormat(values) { const value = getFirstNonEmptyValue(values); if (value) { return { title: 'x', value: transformValueToString(value.x) }; } } /** * Format items by default. * @param {Array} values Array of values. * @returns {*} Formatted list of items. */ function defaultItemsFormat(values) { return values.map((v, i) => { if (v) { return {value: v.y, title: i}; } }); } /** * Get the first non-empty item from an array. * @param {Array} values Array of values. * @returns {*} First non-empty value or undefined. */ function getFirstNonEmptyValue(values) { return (values || []).find(v => Boolean(v)); } class Crosshair extends PureComponent { static get defaultProps() { return { titleFormat: defaultTitleFormat, itemsFormat: defaultItemsFormat, style: { line: {}, title: {}, box: {} } }; } static get propTypes() { return { className: PropTypes.string, values: PropTypes.arrayOf( PropTypes.oneOfType([ PropTypes.number, PropTypes.string, PropTypes.object, PropTypes.bool ]) ), series: PropTypes.object, innerWidth: PropTypes.number, innerHeight: PropTypes.number, marginLeft: PropTypes.number, marginTop: PropTypes.number, orientation: PropTypes.oneOf(['left', 'right']), itemsFormat: PropTypes.func, titleFormat: PropTypes.func, style: PropTypes.shape({ line: PropTypes.object, title: PropTypes.object, box: PropTypes.object }) }; } /** * Render crosshair items (title + value for each series). * @returns {*} Array of React classes with the crosshair values. * @private */ _renderCrosshairItems() { const {values, itemsFormat} = this.props; const items = itemsFormat(values); if (!items) { return null; } return items .filter(i => i) .map(function renderValue(item, i) { return (
{item.title} {': '} {item.value}
); }); } /** * Render crosshair title. * @returns {*} Container with the crosshair title. * @private */ _renderCrosshairTitle() { const {values, titleFormat, style} = this.props; const titleItem = titleFormat(values); if (!titleItem) { return null; } return (
{titleItem.title} {': '} {titleItem.value}
); } render() { const { children, className, values, marginTop, marginLeft, innerWidth, innerHeight, style } = this.props; const value = getFirstNonEmptyValue(values); if (!value) { return null; } const x = getAttributeFunctor(this.props, 'x'); const innerLeft = x(value); const { orientation = innerLeft > innerWidth / 2 ? 'left' : 'right' } = this.props; const left = marginLeft + innerLeft; const top = marginTop; const innerClassName = `rv-crosshair__inner rv-crosshair__inner--${orientation}`; return (
{children ? ( children ) : (
{this._renderCrosshairTitle()} {this._renderCrosshairItems()}
)}
); } } Crosshair.displayName = 'Crosshair'; export default Crosshair; ================================================ FILE: packages/react-vis/src/plot/gradient-defs.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-gradient-defs'; function GradientDefs(props) { const {className} = props; return ( {props.children} ); } GradientDefs.displayName = 'GradientDefs'; GradientDefs.requiresSVG = true; GradientDefs.propTypes = { className: PropTypes.string }; GradientDefs.defaultProps = { className: '' }; export default GradientDefs; ================================================ FILE: packages/react-vis/src/plot/grid-lines.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {getAttributeScale} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import Animation, {AnimationPropType} from 'animation'; import { getTicksTotalFromSize, getTickValues, DIRECTION } from '../utils/axis-utils'; const {VERTICAL, HORIZONTAL} = DIRECTION; const propTypes = { direction: PropTypes.oneOf([VERTICAL, HORIZONTAL]), attr: PropTypes.string.isRequired, width: PropTypes.number, height: PropTypes.number, top: PropTypes.number, left: PropTypes.number, style: PropTypes.object, tickValues: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.number, PropTypes.string]) ), tickTotal: PropTypes.number, animation: AnimationPropType, // generally supplied by xyplot marginTop: PropTypes.number, marginBottom: PropTypes.number, marginLeft: PropTypes.number, marginRight: PropTypes.number, innerWidth: PropTypes.number, innerHeight: PropTypes.number }; const defaultProps = { direction: VERTICAL }; const animatedProps = [ 'xRange', 'yRange', 'xDomain', 'yDomain', 'width', 'height', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'tickTotal' ]; class GridLines extends PureComponent { _getDefaultProps() { const { innerWidth, innerHeight, marginTop, marginLeft, direction } = this.props; return { left: marginLeft, top: marginTop, width: innerWidth, height: innerHeight, tickTotal: getTicksTotalFromSize( direction === VERTICAL ? innerWidth : innerHeight ) }; } render() { const {animation, className} = this.props; if (animation) { return ( ); } const props = { ...this._getDefaultProps(), ...this.props }; const { attr, direction, width, height, style, tickTotal, tickValues, top, left } = props; const isVertical = direction === VERTICAL; const tickXAttr = isVertical ? 'y' : 'x'; const tickYAttr = isVertical ? 'x' : 'y'; const length = isVertical ? height : width; const scale = getAttributeScale(props, attr); const values = getTickValues(scale, tickTotal, tickValues); return ( {values.map((v, i) => { const pos = scale(v); const pathProps = { [`${tickYAttr}1`]: pos, [`${tickYAttr}2`]: pos, [`${tickXAttr}1`]: 0, [`${tickXAttr}2`]: length }; return ( ); })} ); } } GridLines.displayName = 'GridLines'; GridLines.defaultProps = defaultProps; GridLines.propTypes = propTypes; GridLines.requiresSVG = true; export default GridLines; ================================================ FILE: packages/react-vis/src/plot/highlight.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import AbstractSeries from './series/abstract-series'; import {getAttributeScale} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; function getLocs(evt) { const xLoc = evt.type === 'touchstart' ? evt.pageX : evt.offsetX; const yLoc = evt.type === 'touchstart' ? evt.pageY : evt.offsetY; return {xLoc, yLoc}; } class Highlight extends AbstractSeries { state = { dragging: false, brushArea: {top: 0, right: 0, bottom: 0, left: 0}, brushing: false, startLocX: 0, startLocY: 0, dragArea: null }; _getDrawArea(xLoc, yLoc) { const {startLocX, startLocY} = this.state; const { enableX, enableY, highlightWidth, highlightHeight, innerWidth, innerHeight, marginLeft, marginRight, marginBottom, marginTop } = this.props; const plotHeight = innerHeight + marginTop + marginBottom; const plotWidth = innerWidth + marginLeft + marginRight; const touchWidth = highlightWidth || plotWidth; const touchHeight = highlightHeight || plotHeight; return { bottom: enableY ? Math.max(startLocY, yLoc) : touchHeight, right: enableX ? Math.max(startLocX, xLoc) : touchWidth, left: enableX ? Math.min(xLoc, startLocX) : 0, top: enableY ? Math.min(yLoc, startLocY) : 0 }; } _getDragArea(xLoc, yLoc) { const {enableX, enableY} = this.props; const {startLocX, startLocY, dragArea} = this.state; return { bottom: dragArea.bottom + (enableY ? yLoc - startLocY : 0), left: dragArea.left + (enableX ? xLoc - startLocX : 0), right: dragArea.right + (enableX ? xLoc - startLocX : 0), top: dragArea.top + (enableY ? yLoc - startLocY : 0) }; } _clickedOutsideDrag(xLoc, yLoc) { const {enableX, enableY} = this.props; const { dragArea, brushArea: {left, right, top, bottom} } = this.state; const clickedOutsideDragX = dragArea && (xLoc < left || xLoc > right); const clickedOutsideDragY = dragArea && (yLoc < top || yLoc > bottom); if (enableX && enableY) { return clickedOutsideDragX || clickedOutsideDragY; } if (enableX) { return clickedOutsideDragX; } if (enableY) { return clickedOutsideDragY; } return true; } _convertAreaToCoordinates(brushArea) { // NOTE only continuous scales are supported for brushing/getting coordinates back const {enableX, enableY, marginLeft, marginTop} = this.props; const xScale = getAttributeScale(this.props, 'x'); const yScale = getAttributeScale(this.props, 'y'); // Ensure that users wishes are being respected about which scales are evaluated // this is specifically enabled to ensure brushing on mixed categorical and linear // charts will run as expected if (enableX && enableY) { return { bottom: yScale.invert(brushArea.bottom), left: xScale.invert(brushArea.left - marginLeft), right: xScale.invert(brushArea.right - marginLeft), top: yScale.invert(brushArea.top) }; } if (enableY) { return { bottom: yScale.invert(brushArea.bottom - marginTop), top: yScale.invert(brushArea.top - marginTop) }; } if (enableX) { return { left: xScale.invert(brushArea.left - marginLeft), right: xScale.invert(brushArea.right - marginLeft) }; } return {}; } startBrushing(e) { const {onBrushStart, onDragStart, drag} = this.props; const {dragArea} = this.state; const {xLoc, yLoc} = getLocs(e.nativeEvent); const startArea = (dragging, resetDrag) => { const emptyBrush = { bottom: yLoc, left: xLoc, right: xLoc, top: yLoc }; this.setState({ dragging, brushArea: dragArea && !resetDrag ? dragArea : emptyBrush, brushing: !dragging, startLocX: xLoc, startLocY: yLoc }); }; const clickedOutsideDrag = this._clickedOutsideDrag(xLoc, yLoc); if ((drag && !dragArea) || !drag || clickedOutsideDrag) { startArea(false, clickedOutsideDrag); if (onBrushStart) { onBrushStart(e); } return; } if (drag && dragArea) { startArea(true, clickedOutsideDrag); if (onDragStart) { onDragStart(e); } } } stopBrushing() { const {brushing, dragging, brushArea} = this.state; // Quickly short-circuit if the user isn't brushing in our component if (!brushing && !dragging) { return; } const {onBrushEnd, onDragEnd, drag} = this.props; const noHorizontal = Math.abs(brushArea.right - brushArea.left) < 5; const noVertical = Math.abs(brushArea.top - brushArea.bottom) < 5; // Invoke the callback with null if the selected area was < 5px const isNulled = noVertical || noHorizontal; // Clear the draw area this.setState({ brushing: false, dragging: false, brushArea: drag ? brushArea : {top: 0, right: 0, bottom: 0, left: 0}, startLocX: 0, startLocY: 0, dragArea: drag && !isNulled && brushArea }); if (brushing && onBrushEnd) { onBrushEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null); } if (drag && onDragEnd) { onDragEnd(!isNulled ? this._convertAreaToCoordinates(brushArea) : null); } } onBrush(e) { const {onBrush, onDrag, drag} = this.props; const {brushing, dragging} = this.state; const {xLoc, yLoc} = getLocs(e.nativeEvent); if (brushing) { const brushArea = this._getDrawArea(xLoc, yLoc); this.setState({brushArea}); if (onBrush) { onBrush(this._convertAreaToCoordinates(brushArea)); } } if (drag && dragging) { const brushArea = this._getDragArea(xLoc, yLoc); this.setState({brushArea}); if (onDrag) { onDrag(this._convertAreaToCoordinates(brushArea)); } } } render() { const { color, className, highlightHeight, highlightWidth, highlightX, highlightY, innerWidth, innerHeight, marginLeft, marginRight, marginTop, marginBottom, opacity } = this.props; const { brushArea: {left, right, top, bottom} } = this.state; let leftPos = 0; if (highlightX) { const xScale = getAttributeScale(this.props, 'x'); leftPos = xScale(highlightX); } let topPos = 0; if (highlightY) { const yScale = getAttributeScale(this.props, 'y'); topPos = yScale(highlightY); } const plotWidth = marginLeft + marginRight + innerWidth; const plotHeight = marginTop + marginBottom + innerHeight; const touchWidth = highlightWidth || plotWidth; const touchHeight = highlightHeight || plotHeight; return ( this.startBrushing(e)} onMouseMove={e => this.onBrush(e)} onMouseUp={e => this.stopBrushing(e)} onMouseLeave={e => this.stopBrushing(e)} // preventDefault() so that mouse event emulation does not happen onTouchEnd={e => { e.preventDefault(); this.stopBrushing(e); }} onTouchCancel={e => { e.preventDefault(); this.stopBrushing(e); }} onContextMenu={e => e.preventDefault()} onContextMenuCapture={e => e.preventDefault()} /> ); } } Highlight.displayName = 'HighlightOverlay'; Highlight.defaultProps = { color: 'rgb(77, 182, 172)', className: '', enableX: true, enableY: true, opacity: 0.3 }; Highlight.propTypes = { ...AbstractSeries.propTypes, enableX: PropTypes.bool, enableY: PropTypes.bool, highlightHeight: PropTypes.number, highlightWidth: PropTypes.number, highlightX: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), highlightY: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), onBrushStart: PropTypes.func, onDragStart: PropTypes.func, onBrush: PropTypes.func, onDrag: PropTypes.func, onBrushEnd: PropTypes.func, onDragEnd: PropTypes.func }; export default Highlight; ================================================ FILE: packages/react-vis/src/plot/hint.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {PureComponent} from 'react'; import PropTypes from 'prop-types'; import {transformValueToString} from 'utils/data-utils'; import {getAttributeFunctor} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; /* * Hint provides two options for placement of hint: * a) around a data point in one of four quadrants (imagine the point bisected * by two axes -vertical, horizontal- creating 4 quadrants around a data * point). * b) **New** pin to an edge of chart/plot area and position along that edge * using data point's other dimension value. * * To support these two options, deprecate one Hint props (orientation) with two * new Hint align prop object (horizontal, vertical) with following values: * * horizontal: auto, left, right, leftEdge, rightEdge * vertical: auto, bottom, top, bottomEdge, topEdge * * Thus, the following ALIGN constants are the values for horizontal * and vertical */ const ALIGN = { AUTO: 'auto', LEFT: 'left', RIGHT: 'right', LEFT_EDGE: 'leftEdge', RIGHT_EDGE: 'rightEdge', BOTTOM: 'bottom', TOP: 'top', BOTTOM_EDGE: 'bottomEdge', TOP_EDGE: 'topEdge' }; /** * For backwards support, retain the ORIENTATION prop constants */ const ORIENTATION = { BOTTOM_LEFT: 'bottomleft', BOTTOM_RIGHT: 'bottomright', TOP_LEFT: 'topleft', TOP_RIGHT: 'topright' }; /** * Default format function for the value. * @param {Object} value Value. * @returns {Array} title-value pairs. */ function defaultFormat(value) { return Object.keys(value).map(function getProp(key) { return {title: key, value: transformValueToString(value[key])}; }); } class Hint extends PureComponent { static get defaultProps() { return { format: defaultFormat, align: { horizontal: ALIGN.AUTO, vertical: ALIGN.AUTO }, style: {} }; } static get propTypes() { return { marginTop: PropTypes.number, marginLeft: PropTypes.number, innerWidth: PropTypes.number, innerHeight: PropTypes.number, scales: PropTypes.object, value: PropTypes.object, format: PropTypes.func, style: PropTypes.object, className: PropTypes.string, align: PropTypes.shape({ horizontal: PropTypes.oneOf([ ALIGN.AUTO, ALIGN.LEFT, ALIGN.RIGHT, ALIGN.LEFT_EDGE, ALIGN.RIGHT_EDGE ]), vertical: PropTypes.oneOf([ ALIGN.AUTO, ALIGN.BOTTOM, ALIGN.TOP, ALIGN.BOTTOM_EDGE, ALIGN.TOP_EDGE ]) }), getAlignStyle: PropTypes.func, orientation: PropTypes.oneOf([ ORIENTATION.BOTTOM_LEFT, ORIENTATION.BOTTOM_RIGHT, ORIENTATION.TOP_LEFT, ORIENTATION.TOP_RIGHT ]) }; } /** * Obtain align object with horizontal and vertical settings * but convert any AUTO values to the non-auto ALIGN depending on the * values of x and y. * @param {number} x X value. * @param {number} y Y value. * @returns {Object} Align object w/ horizontal, vertical prop strings. * @private */ _getAlign(x, y) { const { innerWidth, innerHeight, orientation, align: {horizontal, vertical} } = this.props; const align = orientation ? this._mapOrientationToAlign(orientation) : {horizontal, vertical}; if (horizontal === ALIGN.AUTO) { align.horizontal = x > innerWidth / 2 ? ALIGN.LEFT : ALIGN.RIGHT; } if (vertical === ALIGN.AUTO) { align.vertical = y > innerHeight / 2 ? ALIGN.TOP : ALIGN.BOTTOM; } return align; } /** * Get the class names from align values. * @param {Object} align object with horizontal and vertical prop strings. * @returns {string} Class names. * @private */ _getAlignClassNames(align) { const {orientation} = this.props; const orientationClass = orientation ? `rv-hint--orientation-${orientation}` : ''; return `${orientationClass} rv-hint--horizontalAlign-${align.horizontal} rv-hint--verticalAlign-${align.vertical}`; } /** * Get a CSS mixin for a proper positioning of the element. * @param {Object} align object with horizontal and vertical prop strings. * @param {number} x X position. * @param {number} y Y position. * @returns {Object} Object, that may contain `left` or `right, `top` or * `bottom` properties. * @private */ _getAlignStyle(align, x, y) { return { ...this._getXCSS(align.horizontal, x), ...this._getYCSS(align.vertical, y) }; } /** * Get the bottom coordinate of the hint. * When y undefined or null, edge case, pin bottom. * @param {number} y Y. * @returns {{bottom: *}} Mixin. * @private */ _getCSSBottom(y) { if (y === undefined || y === null) { return { bottom: 0 }; } const {innerHeight, marginBottom} = this.props; return { bottom: marginBottom + innerHeight - y }; } /** * Get the left coordinate of the hint. * When x undefined or null, edge case, pin left. * @param {number} x X. * @returns {{left: *}} Mixin. * @private */ _getCSSLeft(x) { if (x === undefined || x === null) { return { left: 0 }; } const {marginLeft} = this.props; return { left: marginLeft + x }; } /** * Get the right coordinate of the hint. * When x undefined or null, edge case, pin right. * @param {number} x X. * @returns {{right: *}} Mixin. * @private */ _getCSSRight(x) { if (x === undefined || x === null) { return { right: 0 }; } const {innerWidth, marginRight} = this.props; return { right: marginRight + innerWidth - x }; } /** * Get the top coordinate of the hint. * When y undefined or null, edge case, pin top. * @param {number} y Y. * @returns {{top: *}} Mixin. * @private */ _getCSSTop(y) { if (y === undefined || y === null) { return { top: 0 }; } const {marginTop} = this.props; return { top: marginTop + y }; } /** * Get the position for the hint and the appropriate class name. * @returns {{style: Object, positionClassName: string}} Style and className for the * hint. * @private */ _getPositionInfo() { const {value, getAlignStyle} = this.props; const x = getAttributeFunctor(this.props, 'x')(value); const y = getAttributeFunctor(this.props, 'y')(value); const align = this._getAlign(x, y); return { position: getAlignStyle ? getAlignStyle(align, x, y) : this._getAlignStyle(align, x, y), positionClassName: this._getAlignClassNames(align) }; } _getXCSS(horizontal, x) { // obtain xCSS switch (horizontal) { case ALIGN.LEFT_EDGE: // this pins x to left edge return this._getCSSLeft(null); case ALIGN.RIGHT_EDGE: // this pins x to left edge return this._getCSSRight(null); case ALIGN.LEFT: // this places hint text to the left of center, so set its right edge return this._getCSSRight(x); case ALIGN.RIGHT: default: // this places hint text to the right of center, so set its left edge // default case should not be possible but if it happens set to RIGHT return this._getCSSLeft(x); } } _getYCSS(verticalAlign, y) { // obtain yCSS switch (verticalAlign) { case ALIGN.TOP_EDGE: // this pins x to top edge return this._getCSSTop(null); case ALIGN.BOTTOM_EDGE: // this pins x to bottom edge return this._getCSSBottom(null); case ALIGN.BOTTOM: // this places hint text to the bottom of center, so set its top edge return this._getCSSTop(y); case ALIGN.TOP: default: // this places hint text to the top of center, so set its bottom edge // default case should not be possible but if it happens set to BOTTOM return this._getCSSBottom(y); } } _mapOrientationToAlign(orientation) { // TODO: print warning that this feature is deprecated and support will be // removed in next major release. switch (orientation) { case ORIENTATION.BOTTOM_LEFT: return { horizontal: ALIGN.LEFT, vertical: ALIGN.BOTTOM }; case ORIENTATION.BOTTOM_RIGHT: return { horizontal: ALIGN.RIGHT, vertical: ALIGN.BOTTOM }; case ORIENTATION.TOP_LEFT: return { horizontal: ALIGN.LEFT, vertical: ALIGN.TOP }; case ORIENTATION.TOP_RIGHT: return { horizontal: ALIGN.RIGHT, vertical: ALIGN.TOP }; default: // fall back to horizontalAlign, verticalAlign that are either // provided or defaulted to AUTO. So, don't change things break; } } render() { const {value, format, children, style, className} = this.props; const {position, positionClassName} = this._getPositionInfo(); return (
{children ? ( children ) : (
{format(value).map((formattedProp, i) => (
{formattedProp.title} {': '} {formattedProp.value}
))}
)}
); } } Hint.displayName = 'Hint'; Hint.ORIENTATION = ORIENTATION; Hint.ALIGN = ALIGN; export default Hint; ================================================ FILE: packages/react-vis/src/plot/horizontal-grid-lines.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {DIRECTION} from 'utils/axis-utils'; import GridLines from 'plot/grid-lines'; const {HORIZONTAL} = DIRECTION; const propTypes = { ...GridLines.propTypes, direction: PropTypes.oneOf([HORIZONTAL]) }; const defaultProps = { direction: HORIZONTAL, attr: 'y' }; function HorizontalGridLines(props) { return ; } HorizontalGridLines.displayName = 'HorizontalGridLines'; HorizontalGridLines.propTypes = propTypes; HorizontalGridLines.defaultProps = defaultProps; HorizontalGridLines.requiresSVG = true; export default HorizontalGridLines; ================================================ FILE: packages/react-vis/src/plot/series/abstract-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import PropTypes from 'prop-types'; import {voronoi} from 'd3-voronoi'; import {PureComponent} from 'react'; import {AnimationPropType} from 'animation'; import { getAttributeFunctor, getAttr0Functor, getAttributeValue, getScaleObjectFromProps, getScalePropTypesByAttribute } from 'utils/scales-utils'; const propTypes = { ...getScalePropTypesByAttribute('x'), ...getScalePropTypesByAttribute('y'), ...getScalePropTypesByAttribute('size'), ...getScalePropTypesByAttribute('opacity'), ...getScalePropTypesByAttribute('color'), width: PropTypes.number, height: PropTypes.number, data: PropTypes.arrayOf( PropTypes.oneOfType([PropTypes.object, PropTypes.array]) ), onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func, onValueClick: PropTypes.func, onValueRightClick: PropTypes.func, onSeriesMouseOver: PropTypes.func, onSeriesMouseOut: PropTypes.func, onSeriesClick: PropTypes.func, onSeriesRightClick: PropTypes.func, onNearestX: PropTypes.func, onNearestXY: PropTypes.func, style: PropTypes.object, animation: AnimationPropType, stack: PropTypes.bool }; const defaultProps = { className: '', stack: false, style: {} }; class AbstractSeries extends PureComponent { /** * Get a default config for the parent. * @returns {Object} Empty config. */ static getParentConfig() { return {}; } /** * Tells the rest of the world that it requires SVG to work. * @returns {boolean} Result. */ static get requiresSVG() { return true; } onParentMouseMove(event) { const {onNearestX, onNearestXY, data} = this.props; if ((!onNearestX && !onNearestXY) || !data) { return; } if (onNearestXY) { this._handleNearestXY(event); } else { this._handleNearestX(event); } } onParentTouchMove(e) { e.preventDefault(); this.onParentMouseMove(e); } onParentTouchStart(e) { // prevent mouse event emulation e.preventDefault(); } /** * Get the attr0 functor. * @param {string} attr Attribute name. * @returns {*} Functor. * @private */ _getAttr0Functor(attr) { return getAttr0Functor(this.props, attr); } /** * Get attribute functor. * @param {string} attr Attribute name * @returns {*} Functor. * @protected */ _getAttributeFunctor(attr) { return getAttributeFunctor(this.props, attr); } /** * Get the attribute value if it is available. * @param {string} attr Attribute name. * @returns {*} Attribute value if available, fallback value or undefined * otherwise. * @protected */ _getAttributeValue(attr) { return getAttributeValue(this.props, attr); } /** * Get the scale object distance by the attribute from the list of properties. * @param {string} attr Attribute name. * @returns {number} Scale distance. * @protected */ _getScaleDistance(attr) { const scaleObject = getScaleObjectFromProps(this.props, attr); return scaleObject ? scaleObject.distance : 0; } _getXYCoordinateInContainer(event) { const {marginTop = 0, marginLeft = 0} = this.props; const {nativeEvent: evt, currentTarget} = event; const rect = currentTarget.getBoundingClientRect(); let x = evt.clientX; let y = evt.clientY; if (evt.type === 'touchmove') { x = evt.touches[0].pageX; y = evt.touches[0].pageY; } return { x: x - rect.left - currentTarget.clientLeft - marginLeft, y: y - rect.top - currentTarget.clientTop - marginTop }; } _handleNearestX(event) { const {onNearestX, data} = this.props; let minDistance = Number.POSITIVE_INFINITY; let value = null; let valueIndex = null; const coordinate = this._getXYCoordinateInContainer(event); const xScaleFn = this._getAttributeFunctor('x'); data.forEach((item, i) => { const currentCoordinate = xScaleFn(item); const newDistance = Math.abs(coordinate.x - currentCoordinate); if (newDistance < minDistance) { minDistance = newDistance; value = item; valueIndex = i; } }); if (!value) { return; } onNearestX(value, { innerX: xScaleFn(value), index: valueIndex, event: event.nativeEvent }); } _handleNearestXY(event) { const {onNearestXY, data} = this.props; const coordinate = this._getXYCoordinateInContainer(event); const xScaleFn = this._getAttributeFunctor('x'); const yScaleFn = this._getAttributeFunctor('y'); // Create a voronoi with each node center points const voronoiInstance = voronoi() .x(xScaleFn) .y(yScaleFn); const foundPoint = voronoiInstance(data).find(coordinate.x, coordinate.y); const value = foundPoint.data; if (!value) { return; } onNearestXY(value, { innerX: foundPoint[0], innerY: foundPoint[1], index: foundPoint.index, event: event.nativeEvent }); } /** * Click handler for the entire series. * @param {Object} event Event. * @protected */ _seriesClickHandler = event => { const {onSeriesClick} = this.props; if (onSeriesClick) { onSeriesClick({event}); } }; /** * Mouse out handler for the entire series. * @param {Object} event Event. * @protected */ _seriesMouseOutHandler = event => { const {onSeriesMouseOut} = this.props; if (onSeriesMouseOut) { onSeriesMouseOut({event}); } }; /** * Mouse over handler for the entire series. * @param {Object} event Event. * @protected */ _seriesMouseOverHandler = event => { const {onSeriesMouseOver} = this.props; if (onSeriesMouseOver) { onSeriesMouseOver({event}); } }; /** * Right Click handler for the entire series. * @param {Object} event Event. * @protected */ _seriesRightClickHandler = event => { const {onSeriesRightClick} = this.props; if (onSeriesRightClick) { onSeriesRightClick({event}); } }; /** * Click handler for the specific series' value. * @param {Object} d Value object * @param {Object} event Event. * @protected */ _valueClickHandler = (d, event) => { const {onValueClick, onSeriesClick} = this.props; if (onValueClick) { onValueClick(d, {event}); } if (onSeriesClick) { onSeriesClick({event}); } }; /** * Mouse out handler for the specific series' value. * @param {Object} d Value object * @param {Object} event Event. * @protected */ _valueMouseOutHandler = (d, event) => { const {onValueMouseOut, onSeriesMouseOut} = this.props; if (onValueMouseOut) { onValueMouseOut(d, {event}); } if (onSeriesMouseOut) { onSeriesMouseOut({event}); } }; /** * Mouse over handler for the specific series' value. * @param {Object} d Value object * @param {Object} event Event. * @protected */ _valueMouseOverHandler = (d, event) => { const {onValueMouseOver, onSeriesMouseOver} = this.props; if (onValueMouseOver) { onValueMouseOver(d, {event}); } if (onSeriesMouseOver) { onSeriesMouseOver({event}); } }; /** * Right Click handler for the specific series' value. * @param {Object} d Value object * @param {Object} event Event. * @protected */ _valueRightClickHandler = (d, event) => { const {onValueRightClick, onSeriesRightClick} = this.props; if (onValueRightClick) { onValueRightClick(d, {event}); } if (onSeriesRightClick) { onSeriesRightClick({event}); } }; } AbstractSeries.displayName = 'AbstractSeries'; AbstractSeries.propTypes = propTypes; AbstractSeries.defaultProps = defaultProps; export default AbstractSeries; ================================================ FILE: packages/react-vis/src/plot/series/arc-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {arc as arcBuilder} from 'd3-shape'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import AbstractSeries from './abstract-series'; import { getAttributeFunctor, getAttr0Functor, extractScalePropsFromProps, getMissingScaleProps, getScalePropTypesByAttribute } from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--arc'; const ATTRIBUTES = ['radius', 'angle']; const defaultProps = { ...AbstractSeries.defaultProps, center: {x: 0, y: 0}, arcClassName: '', className: '', style: {}, padAngle: 0 }; /** * Prepare the internal representation of row for real use. * This is necessary because d3 insists on starting at 12 oclock and moving * clockwise, rather than starting at 3 oclock and moving counter clockwise * as one might expect from polar * @param {Object} row - coordinate object to be modifed * @return {Object} angle corrected object */ function modifyRow(row) { const {radius, angle, angle0} = row; const truedAngle = -1 * angle + Math.PI / 2; const truedAngle0 = -1 * angle0 + Math.PI / 2; return { ...row, x: radius * Math.cos(truedAngle), y: radius * Math.sin(truedAngle), angle: truedAngle, angle0: truedAngle0 }; } class ArcSeries extends AbstractSeries { constructor(props) { super(props); const scaleProps = this._getAllScaleProps(props); this.state = {scaleProps}; } UNSAFE_componentWillReceiveProps(nextProps) { this.setState({scaleProps: this._getAllScaleProps(nextProps)}); } /** * Get the map of scales from the props. * @param {Object} props Props. * @param {Array} data Array of all data. * @returns {Object} Map of scales. * @private */ _getAllScaleProps(props) { const defaultScaleProps = this._getDefaultScaleProps(props); const userScaleProps = extractScalePropsFromProps(props, ATTRIBUTES); const missingScaleProps = getMissingScaleProps( { ...defaultScaleProps, ...userScaleProps }, props.data, ATTRIBUTES ); return { ...defaultScaleProps, ...userScaleProps, ...missingScaleProps }; } /** * Get the list of scale-related settings that should be applied by default. * @param {Object} props Object of props. * @returns {Object} Defaults. * @private */ _getDefaultScaleProps(props) { const {innerWidth, innerHeight} = props; const radius = Math.min(innerWidth / 2, innerHeight / 2); return { radiusRange: [0, radius], _radiusValue: radius, angleType: 'literal' }; } render() { const { arcClassName, animation, className, center, data, disableSeries, hideSeries, marginLeft, marginTop, padAngle, style } = this.props; if (!data) { return null; } if (animation) { const cloneData = data.map(d => ({...d})); return ( ); } const {scaleProps} = this.state; const {radiusDomain} = scaleProps; // need to generate our own functors as abstract series doesnt have anythign for us const radius = getAttributeFunctor(scaleProps, 'radius'); const radius0 = getAttr0Functor(scaleProps, 'radius'); const angle = getAttributeFunctor(scaleProps, 'angle'); const angle0 = getAttr0Functor(scaleProps, 'angle'); // but it does have good color support! const fill = this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'); const stroke = this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'); const opacity = this._getAttributeFunctor('opacity'); const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); return ( {data.map((row, i) => { const noRadius = radiusDomain[1] === radiusDomain[0]; const arcArg = { innerRadius: noRadius ? 0 : radius0(row), outerRadius: radius(row), startAngle: angle0(row) || 0, endAngle: angle(row) }; const arcedData = arcBuilder().padAngle(padAngle); const rowStyle = row.style || {}; const rowClassName = row.className || ''; return ( this._valueClickHandler(modifyRow(row), e)} onContextMenu={e => this._valueRightClickHandler(modifyRow(row), e) } onMouseOver={e => this._valueMouseOverHandler(modifyRow(row), e)} onMouseOut={e => this._valueMouseOutHandler(modifyRow(row), e)} className={`${predefinedClassName}-path ${arcClassName} ${rowClassName}`} d={arcedData(arcArg)} /> ); })} ); } } ArcSeries.propTypes = { ...AbstractSeries.propTypes, ...getScalePropTypesByAttribute('radius'), ...getScalePropTypesByAttribute('angle'), center: PropTypes.shape({ x: PropTypes.number, y: PropTypes.number }), arcClassName: PropTypes.string, padAngle: PropTypes.oneOfType([PropTypes.func, PropTypes.number]) }; ArcSeries.defaultProps = defaultProps; ArcSeries.displayName = 'ArcSeries'; export default ArcSeries; ================================================ FILE: packages/react-vis/src/plot/series/area-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import * as d3Shape from 'd3-shape'; import Animation from 'animation'; import {DEFAULT_OPACITY} from 'theme'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {warning} from 'utils/react-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--line'; class AreaSeries extends AbstractSeries { _renderArea(data, x, y0, y, curve, getNull) { let area = d3Shape.area(); if (curve !== null) { if (typeof curve === 'string' && d3Shape[curve]) { area = area.curve(d3Shape[curve]); } else if (typeof curve === 'function') { area = area.curve(curve); } } area = area.defined(getNull); area = area .x(x) .y0(y0) .y1(y); return area(data); } render() { const { animation, className, curve, data, marginLeft, marginTop, style } = this.props; if (this.props.nullAccessor) { warning('nullAccessor has been renamed to getNull', true); } if (!data) { return null; } if (animation) { return ( ); } const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const y0 = this._getAttr0Functor('y'); const stroke = this._getAttributeValue('stroke') || this._getAttributeValue('color'); const fill = this._getAttributeValue('fill') || this._getAttributeValue('color'); const newOpacity = this._getAttributeValue('opacity'); const opacity = Number.isFinite(newOpacity) ? newOpacity : DEFAULT_OPACITY; const getNull = this.props.nullAccessor || this.props.getNull; const d = this._renderArea(data, x, y0, y, curve, getNull); return ( ); } } AreaSeries.displayName = 'AreaSeries'; AreaSeries.propTypes = { ...AbstractSeries.propTypes, getNull: PropTypes.func }; AreaSeries.defaultProps = { ...AbstractSeries.defaultProps, getNull: () => true }; export default AreaSeries; ================================================ FILE: packages/react-vis/src/plot/series/bar-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import PropTypes from 'prop-types'; import {rgb} from 'd3-color'; import {DEFAULT_OPACITY} from 'theme'; import { getAttributeFunctor, getScaleObjectFromProps, getAttr0Functor } from 'utils/scales-utils'; import {getStackParams} from 'utils/series-utils'; import AbstractSeries from './abstract-series'; function getScaleDistance(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); return scaleObject ? scaleObject.distance : 0; } class BarSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static renderLayer(props, ctx) { const { data, linePosAttr, lineSizeAttr, valuePosAttr, marginTop, marginBottom } = props; if (!data || data.length === 0) { return; } const distance = getScaleDistance(props, linePosAttr); const line = getAttributeFunctor(props, linePosAttr); const value = getAttributeFunctor(props, valuePosAttr); const value0 = getAttr0Functor(props, valuePosAttr); const fill = getAttributeFunctor(props, 'fill') || getAttributeFunctor(props, 'color'); const stroke = getAttributeFunctor(props, 'stroke') || getAttributeFunctor(props, 'color'); const opacity = getAttributeFunctor(props, 'opacity'); const halfSpace = (distance / 2) * 0.85; // totalSpaceAvailable is the space we have available to draw all the // bars of a same 'linePosAttr' value (a.k.a. sameTypeTotal) const totalSpaceAvailable = halfSpace * 2; const {sameTypeTotal, sameTypeIndex} = getStackParams(props); data.forEach(row => { const totalSpaceCenter = line(row); // totalSpaceStartingPoint is the first pixel were we can start drawing const totalSpaceStartingPoint = totalSpaceCenter - halfSpace; // spaceTakenByInterBarsPixels has the overhead space consumed by each bar of sameTypeTotal const spaceTakenByInterBarsPixels = (sameTypeTotal - 1) / sameTypeTotal; // lineSize is the space we have available to draw sameTypeIndex bar const lineSize = totalSpaceAvailable / sameTypeTotal - spaceTakenByInterBarsPixels; const fillColor = rgb(fill(row)); const strokeColor = rgb(stroke(row)); const rowOpacity = opacity(row) || DEFAULT_OPACITY; // linePos is the first pixel were we can start drawing sameTypeIndex bar const linePos = totalSpaceStartingPoint + lineSize * sameTypeIndex + sameTypeIndex; const valuePos = Math.min(value0(row), value(row)); const x = valuePosAttr === 'x' ? valuePos : linePos; const y = valuePosAttr === 'y' ? valuePos : linePos; const valueSize = Math.abs(-value0(row) + value(row)); const height = lineSizeAttr === 'height' ? lineSize : valueSize; const width = lineSizeAttr === 'width' ? lineSize : valueSize; ctx.beginPath(); ctx.rect(x + marginBottom, y + marginTop, width, height); ctx.fillStyle = `rgba(${fillColor.r}, ${fillColor.g}, ${fillColor.b}, ${rowOpacity})`; ctx.fill(); ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${rowOpacity})`; ctx.stroke(); }); } render() { return null; } } BarSeriesCanvas.displayName = 'BarSeriesCanvas'; BarSeriesCanvas.defaultProps = { ...AbstractSeries.defaultProps, linePosAttr: PropTypes.string.isRequired, valuePosAttr: PropTypes.string.isRequired, lineSizeAttr: PropTypes.string.isRequired, valueSizeAttr: PropTypes.string.isRequired }; BarSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default BarSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/bar-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS, getStackParams} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--bar'; class BarSeries extends AbstractSeries { static get propTypes() { return { ...AbstractSeries.propTypes, linePosAttr: PropTypes.string, valuePosAttr: PropTypes.string, lineSizeAttr: PropTypes.string, valueSizeAttr: PropTypes.string, cluster: PropTypes.string, barWidth: PropTypes.number }; } static get defaultProps() { return { barWidth: 0.85 }; } render() { const { animation, className, data, linePosAttr, lineSizeAttr, marginLeft, marginTop, style, valuePosAttr, valueSizeAttr, barWidth } = this.props; if (!data) { return null; } if (animation) { return ( ); } const {sameTypeTotal, sameTypeIndex} = getStackParams(this.props); const distance = this._getScaleDistance(linePosAttr); const lineFunctor = this._getAttributeFunctor(linePosAttr); const valueFunctor = this._getAttributeFunctor(valuePosAttr); const value0Functor = this._getAttr0Functor(valuePosAttr); const fillFunctor = this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'); const strokeFunctor = this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'); const opacityFunctor = this._getAttributeFunctor('opacity'); const halfSpace = (distance / 2) * barWidth; return ( {data.map((d, i) => { // totalSpaceAvailable is the space we have available to draw all the // bars of a same 'linePosAttr' value (a.k.a. sameTypeTotal) const totalSpaceAvailable = halfSpace * 2; const totalSpaceCenter = lineFunctor(d); // totalSpaceStartingPoint is the first pixel were we can start drawing const totalSpaceStartingPoint = totalSpaceCenter - halfSpace; // spaceTakenByInterBarsPixels has the overhead space consumed by each bar of sameTypeTotal const spaceTakenByInterBarsPixels = (sameTypeTotal - 1) / sameTypeTotal; // spacePerBar is the space we have available to draw sameTypeIndex bar const spacePerBar = totalSpaceAvailable / sameTypeTotal - spaceTakenByInterBarsPixels; // barStartingPoint is the first pixel were we can start drawing sameTypeIndex bar const barStartingPoint = totalSpaceStartingPoint + spacePerBar * sameTypeIndex + sameTypeIndex; const attrs = { style: { opacity: opacityFunctor && opacityFunctor(d), stroke: strokeFunctor && strokeFunctor(d), fill: fillFunctor && fillFunctor(d), ...style }, [linePosAttr]: barStartingPoint, [lineSizeAttr]: spacePerBar, [valuePosAttr]: Math.min(value0Functor(d), valueFunctor(d)), [valueSizeAttr]: Math.abs(-value0Functor(d) + valueFunctor(d)), onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e) }; return ; })} ); } } BarSeries.displayName = 'BarSeries'; export default BarSeries; ================================================ FILE: packages/react-vis/src/plot/series/canvas-wrapper.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {interpolate} from 'd3-interpolate'; import {extractAnimatedPropValues} from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; const MAX_DRAWS = 30; /** * Draw loop draws each of the layers until it should draw more * @param {CanvasContext} ctx - the context where the drawing will take place * @param {Number} height - height of the canvas * @param {Number} width - width of the canvas * @param {Array} layers - the layer objects to render */ function engageDrawLoop(ctx, height, width, layers) { let drawIteration = 0; // using setInterval because request animation frame goes too fast const drawCycle = setInterval(() => { if (!ctx) { clearInterval(drawCycle); return; } drawLayers(ctx, height, width, layers, drawIteration); if (drawIteration > MAX_DRAWS) { clearInterval(drawCycle); } drawIteration += 1; }, 1); } /** * Loops across each of the layers to be drawn and draws them * @param {CanvasContext} ctx - the context where the drawing will take place * @param {Number} height - height of the canvas * @param {Number} width - width of the canvas * @param {Array} layers - the layer objects to render * @param {Number} drawIteration - width of the canvas */ function drawLayers(ctx, height, width, layers, drawIteration) { ctx.clearRect(0, 0, width, height); layers.forEach(layer => { const {interpolator, newProps, animation} = layer; // return an empty object if dont need to be animating const interpolatedProps = animation ? interpolator ? interpolator(drawIteration / MAX_DRAWS) : interpolator : () => ({}); layer.renderLayer( { ...newProps, ...interpolatedProps }, ctx ); }); } /** * Build an array of layer of objects the contain the method for drawing each series * as well as an interpolar (specifically a d3-interpolate interpolator) * @param {Object} newChildren the new children to be rendered. * @param {Object} oldChildren the old children to be rendered. * @returns {Array} Object for rendering */ function buildLayers(newChildren, oldChildren) { return newChildren.map((child, index) => { const oldProps = oldChildren[index] ? oldChildren[index].props : {}; const newProps = child.props; const oldAnimatedProps = extractAnimatedPropValues({ ...oldProps, animatedProps: ANIMATED_SERIES_PROPS }); const newAnimatedProps = newProps ? extractAnimatedPropValues({ ...newProps, animatedProps: ANIMATED_SERIES_PROPS }) : null; const interpolator = interpolate(oldAnimatedProps, newAnimatedProps); return { renderLayer: child.type.renderLayer, newProps: child.props, animation: child.props.animation, interpolator }; }); } class CanvasWrapper extends Component { static get defaultProps() { return { pixelRatio: (window && window.devicePixelRatio) || 1 }; } componentDidMount() { const ctx = this.canvas.getContext('2d'); if (!ctx) { return; } const {pixelRatio} = this.props; if (!ctx) { return; } ctx.scale(pixelRatio, pixelRatio); this.drawChildren(null, this.props, ctx); } componentDidUpdate(oldProps) { this.drawChildren(oldProps, this.props, this.canvas.getContext('2d')); } /** * Check that we can and should be animating, then kick off animations as apporpriate * @param {Object} newProps the new props to be interpolated to * @param {Object} oldProps the old props to be interpolated against * @param {DomRef} ctx the canvas context to be drawn on. * @returns {Array} Object for rendering */ drawChildren(oldProps, newProps, ctx) { const { children, innerHeight, innerWidth, marginBottom, marginLeft, marginRight, marginTop } = newProps; if (!ctx) { return; } const childrenShouldAnimate = children.find(child => child.props.animation); const height = innerHeight + marginTop + marginBottom; const width = innerWidth + marginLeft + marginRight; const layers = buildLayers( newProps.children, oldProps ? oldProps.children : [] ); // if we don't need to be animating, dont! cut short if (!childrenShouldAnimate) { drawLayers(ctx, height, width, layers); return; } engageDrawLoop(ctx, height, width, layers); } render() { const { innerHeight, innerWidth, marginBottom, marginLeft, marginRight, marginTop, pixelRatio } = this.props; const height = innerHeight + marginTop + marginBottom; const width = innerWidth + marginLeft + marginRight; return (
(this.canvas = ref)} /> {this.props.children}
); } } CanvasWrapper.displayName = 'CanvasWrapper'; CanvasWrapper.propTypes = { marginBottom: PropTypes.number.isRequired, marginLeft: PropTypes.number.isRequired, marginRight: PropTypes.number.isRequired, marginTop: PropTypes.number.isRequired, innerHeight: PropTypes.number.isRequired, innerWidth: PropTypes.number.isRequired, pixelRatio: PropTypes.number.isRequired }; export default CanvasWrapper; ================================================ FILE: packages/react-vis/src/plot/series/contour-series.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {contourDensity} from 'd3-contour'; import {geoPath} from 'd3-geo'; import {scaleLinear} from 'd3-scale'; import AbstractSeries from './abstract-series'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import {CONTINUOUS_COLOR_RANGE} from 'theme'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--contour'; function getDomain(data) { return data.reduce( (acc, row) => { return { min: Math.min(acc.min, row.value), max: Math.max(acc.max, row.value) }; }, {min: Infinity, max: -Infinity} ); } class ContourSeries extends AbstractSeries { render() { const { animation, bandwidth, className, colorRange, data, innerHeight, innerWidth, marginLeft, marginTop, style } = this.props; if (!data || !innerWidth || !innerHeight) { return null; } if (animation) { return ( ); } const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const contouredData = contourDensity() .x(d => x(d)) .y(d => y(d)) .size([innerWidth, innerHeight]) .bandwidth(bandwidth)(data); const geo = geoPath(); const {min, max} = getDomain(contouredData); const colorScale = scaleLinear() .domain([min, max]) .range(colorRange || CONTINUOUS_COLOR_RANGE); return ( {contouredData.map((polygon, index) => { return ( ); })} ); } } ContourSeries.propTypes = { ...AbstractSeries.propTypes, animation: PropTypes.bool, bandwidth: PropTypes.number, className: PropTypes.string, marginLeft: PropTypes.number, marginTop: PropTypes.number, style: PropTypes.object }; ContourSeries.defaultProps = { ...AbstractSeries.defaultProps, bandwidth: 40, style: {} }; export default ContourSeries; ================================================ FILE: packages/react-vis/src/plot/series/custom-svg-series.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import AbstractSeries from './abstract-series'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--custom-svg-wrapper'; const DEFAULT_STYLE = { stroke: 'blue', fill: 'blue' }; function predefinedComponents(type, size = 2, style = DEFAULT_STYLE) { switch (type) { case 'diamond': return ( ); case 'star': { const starPoints = [...new Array(5)] .map((c, index) => { const angle = (index / 5) * Math.PI * 2; const innerAngle = angle + Math.PI / 10; const outerAngle = angle - Math.PI / 10; // ratio of inner polygon to outer polgyon const innerRadius = size / 2.61; return ` ${Math.cos(outerAngle) * size} ${Math.sin(outerAngle) * size} ${Math.cos(innerAngle) * innerRadius} ${Math.sin(innerAngle) * innerRadius} `; }) .join(' '); return ( ); } case 'square': return ( ); default: case 'circle': return ; } } function getInnerComponent({ customComponent, defaultType, positionInPixels, positionFunctions, style, propsSize }) { const {size} = customComponent; const aggStyle = {...style, ...(customComponent.style || {})}; const innerComponent = customComponent.customComponent; if (!innerComponent && typeof defaultType === 'string') { return predefinedComponents(defaultType, size || propsSize, aggStyle); } // if default component is a function if (!innerComponent) { return defaultType( customComponent, positionInPixels, aggStyle, positionFunctions ); } if (typeof innerComponent === 'string') { return predefinedComponents(innerComponent || defaultType, size, aggStyle); } // if inner component is a function return innerComponent( customComponent, positionInPixels, aggStyle, positionFunctions ); } class CustomSVGSeries extends AbstractSeries { render() { const { animation, className, customComponent, data, innerHeight, innerWidth, marginLeft, marginTop, style, size } = this.props; if (!data || !innerWidth || !innerHeight) { return null; } if (animation) { return ( ); } const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const contents = data.map((seriesComponent, index) => { const positionInPixels = { x: x(seriesComponent), y: y(seriesComponent) }; const innerComponent = getInnerComponent({ customComponent: seriesComponent, positionInPixels, defaultType: customComponent, positionFunctions: {x, y}, style, propsSize: size }); return ( this._valueMouseOverHandler(seriesComponent, e)} onMouseLeave={e => this._valueMouseOutHandler(seriesComponent, e)} > {innerComponent} ); }); return ( {contents} ); } } CustomSVGSeries.propTypes = { animation: PropTypes.bool, className: PropTypes.string, customComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), data: PropTypes.arrayOf( PropTypes.shape({ x: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired, y: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired }) ).isRequired, marginLeft: PropTypes.number, marginTop: PropTypes.number, style: PropTypes.object, size: PropTypes.number, onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func }; CustomSVGSeries.defaultProps = { ...AbstractSeries.defaultProps, animation: false, customComponent: 'circle', style: {}, size: 2 }; export default CustomSVGSeries; ================================================ FILE: packages/react-vis/src/plot/series/heatmap-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--heatmap'; class HeatmapSeries extends AbstractSeries { static getParentConfig(attr) { const isDomainAdjustmentNeeded = attr === 'x' || attr === 'y'; return {isDomainAdjustmentNeeded}; } render() { const { animation, className, data, marginLeft, marginTop, style } = this.props; if (!data) { return null; } if (animation) { return ( ); } const {rectStyle} = {rectStyle: {}, ...style}; const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const opacity = this._getAttributeFunctor('opacity'); const fill = this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'); const stroke = this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'); const xDistance = this._getScaleDistance('x'); const yDistance = this._getScaleDistance('y'); return ( {data.map((d, i) => { const attrs = { style: { stroke: stroke && stroke(d), fill: fill && fill(d), opacity: opacity && opacity(d), ...style }, ...rectStyle, x: x(d) - xDistance / 2, y: y(d) - yDistance / 2, width: xDistance, height: yDistance, onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e) }; return ; })} ); } } HeatmapSeries.propTypes = { ...AbstractSeries.propTypes }; HeatmapSeries.displayName = 'HeatmapSeries'; export default HeatmapSeries; ================================================ FILE: packages/react-vis/src/plot/series/hexbin-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {hexbin} from 'd3-hexbin'; import {scaleLinear} from 'd3-scale'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import {CONTINUOUS_COLOR_RANGE} from 'theme'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--hexbin'; function getColorDomain({countDomain}, hexes) { if (countDomain) { return countDomain; } return [0, Math.max(...hexes.map(row => row.length))]; } class HexbinSeries extends AbstractSeries { render() { const { animation, className, colorRange, data, innerHeight, innerWidth, marginLeft, marginTop, radius, sizeHexagonsWithCount, style, xOffset, yOffset } = this.props; if (!data) { return null; } if (animation) { return ( ); } const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const hex = hexbin() .x(d => x(d) + xOffset) .y(d => y(d) + yOffset) .radius(radius) .size([innerWidth, innerHeight]); const hexagonPath = hex.hexagon(); const hexes = hex(data); const countDomain = getColorDomain(this.props, hexes); const color = scaleLinear() .domain(countDomain) .range(colorRange); const size = scaleLinear() .domain(countDomain) .range([0, radius]); return ( {hexes.map((d, i) => { const attrs = { style, d: sizeHexagonsWithCount ? hex.hexagon(size(d.length)) : hexagonPath, fill: color(d.length), transform: `translate(${d.x}, ${d.y})`, onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e) }; return ; })} ); } } HexbinSeries.propTypes = { ...AbstractSeries.propTypes, radius: PropTypes.number }; HexbinSeries.defaultProps = { radius: 20, colorRange: CONTINUOUS_COLOR_RANGE, xOffset: 0, yOffset: 0 }; HexbinSeries.displayName = 'HexbinSeries'; export default HexbinSeries; ================================================ FILE: packages/react-vis/src/plot/series/horizontal-bar-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import AbstractSeries from './abstract-series'; import BarSeries from './bar-series-canvas'; class HorizontalBarSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static getParentConfig(attr) { const isDomainAdjustmentNeeded = attr === 'y'; const zeroBaseValue = attr === 'x'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } static renderLayer(props, ctx) { BarSeries.renderLayer( { ...props, linePosAttr: 'y', valuePosAttr: 'x', lineSizeAttr: 'height', valueSizeAttr: 'width' }, ctx ); } render() { return null; } } HorizontalBarSeriesCanvas.displayName = 'HorizontalBarSeriesCanvas'; HorizontalBarSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default HorizontalBarSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/horizontal-bar-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import AbstractSeries from './abstract-series'; import BarSeries from './bar-series'; class HorizontalBarSeries extends AbstractSeries { static getParentConfig(attr) { const isDomainAdjustmentNeeded = attr === 'y'; const zeroBaseValue = attr === 'x'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } render() { return ( ); } } HorizontalBarSeries.displayName = 'HorizontalBarSeries'; export default HorizontalBarSeries; ================================================ FILE: packages/react-vis/src/plot/series/horizontal-rect-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import AbstractSeries from './abstract-series'; import RectSeries from './rect-series-canvas'; class HorizontalRectSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static getParentConfig(attr) { const isDomainAdjustmentNeeded = false; const zeroBaseValue = attr === 'x'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } static renderLayer(props, ctx) { RectSeries.renderLayer( { ...props, linePosAttr: 'y', valuePosAttr: 'x', lineSizeAttr: 'height', valueSizeAttr: 'width' }, ctx ); } render() { return null; } } HorizontalRectSeriesCanvas.displayName = 'HorizontalRectSeriesCanvas'; HorizontalRectSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default HorizontalRectSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/horizontal-rect-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import AbstractSeries from './abstract-series'; import RectSeries from './rect-series'; class HorizontalRectSeries extends AbstractSeries { static getParentConfig(attr) { const isDomainAdjustmentNeeded = false; const zeroBaseValue = attr === 'x'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } render() { return ( ); } } HorizontalRectSeries.displayName = 'HorizontalRectSeries'; export default HorizontalRectSeries; ================================================ FILE: packages/react-vis/src/plot/series/label-series.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import AbstractSeries from './abstract-series'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--label'; const getTextAnchor = (labelAnchorX, leftOfMiddle) => { return labelAnchorX ? labelAnchorX : leftOfMiddle ? 'start' : 'end'; }; const getDominantBaseline = (labelAnchorY, aboveMiddle) => { return labelAnchorY ? labelAnchorY : aboveMiddle ? 'text-before-edge' : 'text-after-edge'; }; class LabelSeries extends AbstractSeries { render() { const { animation, allowOffsetToBeReversed, className, data, _data, getLabel, marginLeft, marginTop, rotation, style, xRange, yRange, labelAnchorX, labelAnchorY } = this.props; if (!data) { return null; } if (animation) { return ( ); } const xFunctor = this._getAttributeFunctor('x'); const yFunctor = this._getAttributeFunctor('y'); return ( {data.reduce((res, d, i) => { const {style: markStyle, xOffset, yOffset} = d; if (!getLabel(d)) { return res; } const xVal = xFunctor(d); const yVal = yFunctor(d); const leftOfMiddle = xVal < (xRange[1] - xRange[0]) / 2; const aboveMiddle = yVal < Math.abs(yRange[1] - yRange[0]) / 2; const x = xVal + (allowOffsetToBeReversed && leftOfMiddle ? -1 : 1) * (xOffset || 0); const y = yVal + (allowOffsetToBeReversed && aboveMiddle ? -1 : 1) * (yOffset || 0); const hasRotationValueSet = d.rotation === 0 || d.rotation; const labelRotation = hasRotationValueSet ? d.rotation : rotation; const attrs = { dominantBaseline: getDominantBaseline(labelAnchorY, aboveMiddle), className: 'rv-xy-plot__series--label-text', onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e), textAnchor: getTextAnchor(labelAnchorX, leftOfMiddle), x, y, transform: `rotate(${labelRotation},${x},${y})`, ...markStyle }; const textContent = getLabel(_data ? _data[i] : d); return res.concat([ {textContent} ]); }, [])} ); } } LabelSeries.propTypes = { animation: PropTypes.bool, allowOffsetToBeReversed: PropTypes.bool, className: PropTypes.string, data: PropTypes.arrayOf( PropTypes.shape({ x: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), y: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), angle: PropTypes.number, radius: PropTypes.number, label: PropTypes.string, xOffset: PropTypes.number, yOffset: PropTypes.number, style: PropTypes.object }) ).isRequired, marginLeft: PropTypes.number, marginTop: PropTypes.number, rotation: PropTypes.number, style: PropTypes.object, xRange: PropTypes.arrayOf(PropTypes.number), yRange: PropTypes.arrayOf(PropTypes.number), labelAnchorX: PropTypes.string, labelAnchorY: PropTypes.string }; LabelSeries.defaultProps = { ...AbstractSeries.defaultProps, animation: false, rotation: 0, getLabel: d => d.label }; LabelSeries.displayName = 'LabelSeries'; export default LabelSeries; ================================================ FILE: packages/react-vis/src/plot/series/line-mark-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import AbstractSeries from './abstract-series'; import MarkSeriesCanvas from './mark-series-canvas'; import LineSeriesCanvas from './line-series-canvas'; class LineMarkSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static renderLayer(props, ctx) { LineSeriesCanvas.renderLayer(props, ctx); MarkSeriesCanvas.renderLayer(props, ctx); } render() { return null; } } LineMarkSeriesCanvas.displayName = 'LineMarkSeriesCanvas'; LineMarkSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default LineMarkSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/line-mark-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import AbstractSeries from './abstract-series'; import LineSeries from './line-series'; import MarkSeries from './mark-series'; const propTypes = { ...LineSeries.propTypes, lineStyle: PropTypes.object, markStyle: PropTypes.object }; class LineMarkSeries extends AbstractSeries { static get defaultProps() { return { ...LineSeries.defaultProps, lineStyle: {}, markStyle: {} }; } render() { const {lineStyle, markStyle, style} = this.props; return ( ); } } LineMarkSeries.displayName = 'LineMarkSeries'; LineMarkSeries.propTypes = propTypes; export default LineMarkSeries; ================================================ FILE: packages/react-vis/src/plot/series/line-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import PropTypes from 'prop-types'; import {rgb} from 'd3-color'; import * as d3Shape from 'd3-shape'; import React from 'react'; import {DEFAULT_OPACITY} from 'theme'; import {getAttributeFunctor, getAttributeValue} from 'utils/scales-utils'; import AbstractSeries from './abstract-series'; class LineSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static renderLayer(props, ctx) { const { curve, data, marginLeft, marginTop, strokeWidth, strokeDasharray } = props; if (!data || data.length === 0) { return; } const x = getAttributeFunctor(props, 'x'); const y = getAttributeFunctor(props, 'y'); const stroke = getAttributeValue(props, 'stroke') || getAttributeValue(props, 'color'); const strokeColor = rgb(stroke); const newOpacity = getAttributeValue(props, 'opacity'); const opacity = Number.isFinite(newOpacity) ? newOpacity : DEFAULT_OPACITY; let line = d3Shape .line() .x(row => x(row) + marginLeft) .y(row => y(row) + marginTop); if (typeof curve === 'string' && d3Shape[curve]) { line = line.curve(d3Shape[curve]); } else if (typeof curve === 'function') { line = line.curve(curve); } ctx.beginPath(); ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${opacity})`; ctx.lineWidth = strokeWidth; if (strokeDasharray) { ctx.setLineDash(strokeDasharray); } line.context(ctx)(data); ctx.stroke(); ctx.closePath(); // set back to default ctx.lineWidth = 1; ctx.setLineDash([]); } render() { return
; } } LineSeriesCanvas.displayName = 'LineSeriesCanvas'; LineSeriesCanvas.defaultProps = { ...AbstractSeries.defaultProps, strokeWidth: 2 }; LineSeriesCanvas.propTypes = { ...AbstractSeries.propTypes, strokeWidth: PropTypes.number }; export default LineSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/line-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import * as d3Shape from 'd3-shape'; import Animation from 'animation'; import {DEFAULT_OPACITY} from 'theme'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {warning} from 'utils/react-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--line'; const STROKE_STYLES = { dashed: '6, 2', solid: null }; class LineSeries extends AbstractSeries { _renderLine(data, x, y, curve, getNull) { let line = d3Shape.line(); if (curve !== null) { if (typeof curve === 'string' && d3Shape[curve]) { line = line.curve(d3Shape[curve]); } else if (typeof curve === 'function') { line = line.curve(curve); } } line = line.defined(getNull); line = line.x(x).y(y); return line(data); } render() { const {animation, className, data} = this.props; if (this.props.nullAccessor) { warning('nullAccessor has been renamed to getNull', true); } if (!data) { return null; } if (animation) { return ( ); } const { curve, marginLeft, marginTop, strokeDasharray, strokeStyle, strokeWidth, style } = this.props; const x = this._getAttributeFunctor('x'); const y = this._getAttributeFunctor('y'); const stroke = this._getAttributeValue('stroke') || this._getAttributeValue('color'); const newOpacity = this._getAttributeValue('opacity'); const opacity = Number.isFinite(newOpacity) ? newOpacity : DEFAULT_OPACITY; const getNull = this.props.nullAccessor || this.props.getNull; const d = this._renderLine(data, x, y, curve, getNull); return ( ); } } LineSeries.displayName = 'LineSeries'; LineSeries.propTypes = { ...AbstractSeries.propTypes, strokeStyle: PropTypes.oneOf(Object.keys(STROKE_STYLES)), curve: PropTypes.oneOfType([PropTypes.string, PropTypes.func]), getNull: PropTypes.func }; LineSeries.defaultProps = { ...AbstractSeries.defaultProps, strokeStyle: 'solid', style: {}, opacity: 1, curve: null, className: '', getNull: () => true }; export default LineSeries; ================================================ FILE: packages/react-vis/src/plot/series/mark-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import {rgb} from 'd3-color'; import {DEFAULT_SIZE, DEFAULT_OPACITY} from 'theme'; import {getAttributeFunctor} from 'utils/scales-utils'; import AbstractSeries from './abstract-series'; class MarkSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static renderLayer(props, ctx) { const {data, marginLeft, marginTop} = props; const x = getAttributeFunctor(props, 'x'); const y = getAttributeFunctor(props, 'y'); const size = getAttributeFunctor(props, 'size') || (() => DEFAULT_SIZE); const fill = getAttributeFunctor(props, 'fill') || getAttributeFunctor(props, 'color'); const stroke = getAttributeFunctor(props, 'stroke') || getAttributeFunctor(props, 'color'); const opacity = getAttributeFunctor(props, 'opacity'); data.forEach(row => { const fillColor = rgb(fill(row)); const strokeColor = rgb(stroke(row)); const rowOpacity = opacity(row) || DEFAULT_OPACITY; ctx.beginPath(); ctx.arc( x(row) + marginLeft, y(row) + marginTop, size(row), 0, 2 * Math.PI ); ctx.fillStyle = `rgba(${fillColor.r}, ${fillColor.g}, ${fillColor.b}, ${rowOpacity})`; ctx.fill(); ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${rowOpacity})`; ctx.stroke(); }); } render() { return null; } } MarkSeriesCanvas.displayName = 'MarkSeriesCanvas'; MarkSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default MarkSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/mark-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {warning} from 'utils/react-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import {DEFAULT_SIZE, DEFAULT_OPACITY} from 'theme'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--mark'; const DEFAULT_STROKE_WIDTH = 1; class MarkSeries extends AbstractSeries { _renderCircle(d, i, strokeWidth, style, scalingFunctions) { const {fill, opacity, size, stroke, x, y} = scalingFunctions; const attrs = { r: size ? size(d) : DEFAULT_SIZE, cx: x(d), cy: y(d), style: { opacity: opacity ? opacity(d) : DEFAULT_OPACITY, stroke: stroke && stroke(d), fill: fill && fill(d), strokeWidth: strokeWidth || DEFAULT_STROKE_WIDTH, ...style }, key: i, onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e) }; return ; } render() { const { animation, className, data, marginLeft, marginTop, strokeWidth, style } = this.props; if (this.props.nullAccessor) { warning('nullAccessor has been renamed to getNull', true); } const getNull = this.props.nullAccessor || this.props.getNull; if (!data) { return null; } if (animation) { return ( ); } const scalingFunctions = { fill: this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'), opacity: this._getAttributeFunctor('opacity'), size: this._getAttributeFunctor('size'), stroke: this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'), x: this._getAttributeFunctor('x'), y: this._getAttributeFunctor('y') }; return ( {data.map((d, i) => { return ( getNull(d) && this._renderCircle(d, i, strokeWidth, style, scalingFunctions) ); })} ); } } MarkSeries.displayName = 'MarkSeries'; MarkSeries.propTypes = { ...AbstractSeries.propTypes, getNull: PropTypes.func, strokeWidth: PropTypes.number }; MarkSeries.defaultProps = { getNull: () => true }; export default MarkSeries; ================================================ FILE: packages/react-vis/src/plot/series/polygon-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--polygon'; const DEFAULT_COLOR = '#12939A'; const generatePath = (data, xFunctor, yFunctor) => `${data.reduce( (res, row, i) => `${res} ${i ? 'L' : 'M'}${xFunctor(row)} ${yFunctor(row)}`, '' )} Z`; class PolygonSeries extends AbstractSeries { static get propTypes() { return { ...AbstractSeries.propTypes }; } render() { const { animation, color, className, data, marginLeft, marginTop, style } = this.props; if (!data) { return null; } if (animation) { return ( ); } const xFunctor = this._getAttributeFunctor('x'); const yFunctor = this._getAttributeFunctor('y'); return ( this._seriesMouseOverHandler(data, e), onMouseOut: e => this._seriesMouseOutHandler(data, e), onClick: this._seriesClickHandler, onContextMenu: this._seriesRightClickHandler, fill: color || DEFAULT_COLOR, style, d: generatePath(data, xFunctor, yFunctor), transform: `translate(${marginLeft},${marginTop})` }} /> ); } } PolygonSeries.displayName = 'PolygonSeries'; export default PolygonSeries; ================================================ FILE: packages/react-vis/src/plot/series/rect-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import PropTypes from 'prop-types'; import {rgb} from 'd3-color'; import {DEFAULT_OPACITY} from 'theme'; import {getAttributeFunctor, getAttr0Functor} from 'utils/scales-utils'; import AbstractSeries from './abstract-series'; class RectSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static renderLayer(props, ctx) { const { data, linePosAttr, lineSizeAttr, marginLeft, marginTop, valuePosAttr } = props; if (!data || data.length === 0) { return; } const line = getAttributeFunctor(props, linePosAttr); const line0 = getAttr0Functor(props, linePosAttr); const value = getAttributeFunctor(props, valuePosAttr); const value0 = getAttr0Functor(props, valuePosAttr); const fill = getAttributeFunctor(props, 'fill') || getAttributeFunctor(props, 'color'); const stroke = getAttributeFunctor(props, 'stroke') || getAttributeFunctor(props, 'color'); const opacity = getAttributeFunctor(props, 'opacity'); data.forEach(row => { const fillColor = rgb(fill(row)); const strokeColor = rgb(stroke(row)); const rowOpacity = opacity(row) || DEFAULT_OPACITY; const linePos = line0(row); const valuePos = Math.min(value0(row), value(row)); const x = valuePosAttr === 'x' ? valuePos : linePos; const y = valuePosAttr === 'y' ? valuePos : linePos; const lineSize = Math.abs(line(row) - line0(row)); const valueSize = Math.abs(-value0(row) + value(row)); const height = lineSizeAttr === 'height' ? lineSize : valueSize; const width = lineSizeAttr === 'width' ? lineSize : valueSize; ctx.beginPath(); ctx.rect(x + marginLeft, y + marginTop, width, height); ctx.fillStyle = `rgba(${fillColor.r}, ${fillColor.g}, ${fillColor.b}, ${rowOpacity})`; ctx.fill(); ctx.strokeStyle = `rgba(${strokeColor.r}, ${strokeColor.g}, ${strokeColor.b}, ${rowOpacity})`; ctx.stroke(); }); } render() { return null; } } RectSeriesCanvas.displayName = 'RectSeriesCanvas'; RectSeriesCanvas.defaultProps = { ...AbstractSeries.defaultProps, linePosAttr: PropTypes.string.isRequired, valuePosAttr: PropTypes.string.isRequired, lineSizeAttr: PropTypes.string.isRequired, valueSizeAttr: PropTypes.string.isRequired }; RectSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default RectSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/rect-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--rect'; class RectSeries extends AbstractSeries { static get propTypes() { return { ...AbstractSeries.propTypes, linePosAttr: PropTypes.string, valuePosAttr: PropTypes.string, lineSizeAttr: PropTypes.string, valueSizeAttr: PropTypes.string }; } render() { const { animation, className, data, linePosAttr, lineSizeAttr, marginLeft, marginTop, style, valuePosAttr, valueSizeAttr } = this.props; if (!data) { return null; } if (animation) { return ( ); } const lineFunctor = this._getAttributeFunctor(linePosAttr); const line0Functor = this._getAttr0Functor(linePosAttr); const valueFunctor = this._getAttributeFunctor(valuePosAttr); const value0Functor = this._getAttr0Functor(valuePosAttr); const fillFunctor = this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'); const strokeFunctor = this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'); const opacityFunctor = this._getAttributeFunctor('opacity'); return ( {data.map((d, i) => { const attrs = { style: { opacity: opacityFunctor && opacityFunctor(d), stroke: strokeFunctor && strokeFunctor(d), fill: fillFunctor && fillFunctor(d), ...style }, [linePosAttr]: line0Functor(d), [lineSizeAttr]: Math.abs(lineFunctor(d) - line0Functor(d)), [valuePosAttr]: Math.min(value0Functor(d), valueFunctor(d)), [valueSizeAttr]: Math.abs(-value0Functor(d) + valueFunctor(d)), onClick: e => this._valueClickHandler(d, e), onContextMenu: e => this._valueRightClickHandler(d, e), onMouseOver: e => this._valueMouseOverHandler(d, e), onMouseOut: e => this._valueMouseOutHandler(d, e) }; return ; })} ); } } RectSeries.displayName = 'RectSeries'; export default RectSeries; ================================================ FILE: packages/react-vis/src/plot/series/vertical-bar-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import AbstractSeries from './abstract-series'; import BarSeries from './bar-series-canvas'; class HorizontalBarSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static getParentConfig(attr) { const isDomainAdjustmentNeeded = attr === 'x'; const zeroBaseValue = attr === 'y'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } static renderLayer(props, ctx) { BarSeries.renderLayer( { ...props, linePosAttr: 'x', valuePosAttr: 'y', lineSizeAttr: 'width', valueSizeAttr: 'height' }, ctx ); } render() { return null; } } HorizontalBarSeriesCanvas.displayName = 'HorizontalBarSeriesCanvas'; HorizontalBarSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default HorizontalBarSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/vertical-bar-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import AbstractSeries from './abstract-series'; import BarSeries from './bar-series'; class VerticalBarSeries extends AbstractSeries { static getParentConfig(attr) { const isDomainAdjustmentNeeded = attr === 'x'; const zeroBaseValue = attr === 'y'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } render() { return ( ); } } VerticalBarSeries.displayName = 'VerticalBarSeries'; export default VerticalBarSeries; ================================================ FILE: packages/react-vis/src/plot/series/vertical-rect-series-canvas.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import AbstractSeries from './abstract-series'; import RectSeries from './rect-series-canvas'; class HorizontalRectSeriesCanvas extends AbstractSeries { static get requiresSVG() { return false; } static get isCanvas() { return true; } static getParentConfig(attr) { const isDomainAdjustmentNeeded = false; const zeroBaseValue = attr === 'y'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } static renderLayer(props, ctx) { RectSeries.renderLayer( { ...props, linePosAttr: 'x', valuePosAttr: 'y', lineSizeAttr: 'width', valueSizeAttr: 'height' }, ctx ); } render() { return null; } } HorizontalRectSeriesCanvas.displayName = 'HorizontalRectSeriesCanvas'; HorizontalRectSeriesCanvas.propTypes = { ...AbstractSeries.propTypes }; export default HorizontalRectSeriesCanvas; ================================================ FILE: packages/react-vis/src/plot/series/vertical-rect-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import AbstractSeries from './abstract-series'; import RectSeries from './rect-series'; class VerticalRectSeries extends AbstractSeries { static getParentConfig(attr) { const isDomainAdjustmentNeeded = false; const zeroBaseValue = attr === 'y'; return { isDomainAdjustmentNeeded, zeroBaseValue }; } render() { return ( ); } } VerticalRectSeries.displayName = 'VerticalRectSeries'; export default VerticalRectSeries; ================================================ FILE: packages/react-vis/src/plot/series/whisker-series.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import {DEFAULT_OPACITY} from 'theme'; import AbstractSeries from './abstract-series'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--whisker'; const DEFAULT_STROKE_WIDTH = 1; const DEFAULT_CROSS_BAR_WIDTH = 6; /** * Render whisker lines for a data point. * @param {Object} whiskerMarkProps All the properties of the whisker mark. * @private */ const renderWhiskerMark = whiskerMarkProps => (d, i) => { const { crossBarWidth, opacityFunctor, sizeFunctor, strokeFunctor, strokeWidth, style, valueClickHandler, valueMouseOutHandler, valueMouseOverHandler, valueRightClickHandler, xFunctor, yFunctor } = whiskerMarkProps; const r = sizeFunctor ? sizeFunctor(d) : 0; const cx = xFunctor(d); const cy = yFunctor(d); const positiveXVariance = xFunctor({x: d.x + d.xVariance / 2}); const negativeXVariance = xFunctor({x: d.x - d.xVariance / 2}); const positiveYVariance = yFunctor({y: d.y + d.yVariance / 2}); const negativeYVariance = yFunctor({y: d.y - d.yVariance / 2}); /** * Determine whether on not we should draw whiskers in each direction. * We need to see an actual variance value, and also have that value extend past the * radius "buffer" region in which we won't be drawing (if any). */ const hasXWhiskers = positiveXVariance && cx + r < positiveXVariance; const hasYWhiskers = positiveYVariance && cy - r > positiveYVariance; if (!hasXWhiskers && !hasYWhiskers) { return null; } const styleAttr = { opacity: opacityFunctor ? opacityFunctor(d) : DEFAULT_OPACITY, stroke: strokeFunctor && strokeFunctor(d), strokeWidth: strokeWidth || DEFAULT_STROKE_WIDTH, ...style }; const crossBarExtension = crossBarWidth / 2; const rightLineAttrs = { x1: cx + r, y1: cy, x2: positiveXVariance, y2: cy, style: styleAttr }; const leftLineAttrs = { x1: cx - r, y1: cy, x2: negativeXVariance, y2: cy, style: styleAttr }; const rightCrossBarAttrs = { x1: positiveXVariance, y1: cy - crossBarExtension, x2: positiveXVariance, y2: cy + crossBarExtension, style: styleAttr }; const leftCrossBarAttrs = { x1: negativeXVariance, y1: cy - crossBarExtension, x2: negativeXVariance, y2: cy + crossBarExtension, style: styleAttr }; const upperLineAttrs = { x1: cx, y1: cy - r, x2: cx, y2: positiveYVariance, style: styleAttr }; const lowerLineAttrs = { x1: cx, y1: cy + r, x2: cx, y2: negativeYVariance, style: styleAttr }; const upperCrossBarAttrs = { x1: cx - crossBarExtension, y1: positiveYVariance, x2: cx + crossBarExtension, y2: positiveYVariance, style: styleAttr }; const lowerCrossBarAttrs = { x1: cx - crossBarExtension, y1: negativeYVariance, x2: cx + crossBarExtension, y2: negativeYVariance, style: styleAttr }; return ( valueClickHandler(d, e)} onContextMenu={e => valueRightClickHandler(d, e)} onMouseOver={e => valueMouseOverHandler(d, e)} onMouseOut={e => valueMouseOutHandler(d, e)} > {hasXWhiskers ? ( ) : null} {hasYWhiskers ? ( ) : null} ); }; class WhiskerSeries extends AbstractSeries { render() { const { animation, className, crossBarWidth, data, marginLeft, marginTop, strokeWidth, style } = this.props; if (!data) { return null; } if (animation) { return ( ); } const whiskerMarkProps = { crossBarWidth, opacityFunctor: this._getAttributeFunctor('opacity'), sizeFunctor: this._getAttributeFunctor('size'), strokeFunctor: this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'), strokeWidth, style, xFunctor: this._getAttributeFunctor('x'), yFunctor: this._getAttributeFunctor('y'), valueClickHandler: this._valueClickHandler, valueRightClickHandler: this._valueRightClickHandler, valueMouseOverHandler: this._valueMouseOverHandler, valueMouseOutHandler: this._valueMouseOutHandler }; return ( {data.map(renderWhiskerMark(whiskerMarkProps))} ); } } WhiskerSeries.displayName = 'WhiskerSeries'; WhiskerSeries.propTypes = { ...AbstractSeries.propTypes, strokeWidth: PropTypes.number }; WhiskerSeries.defaultProps = { ...AbstractSeries.defaultProps, crossBarWidth: DEFAULT_CROSS_BAR_WIDTH, size: 0, strokeWidth: DEFAULT_STROKE_WIDTH }; export default WhiskerSeries; ================================================ FILE: packages/react-vis/src/plot/vertical-grid-lines.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import GridLines from 'plot/grid-lines'; import {DIRECTION} from 'utils/axis-utils'; const {VERTICAL} = DIRECTION; const propTypes = { ...GridLines.propTypes, direction: PropTypes.oneOf([VERTICAL]) }; const defaultProps = { direction: VERTICAL, attr: 'x' }; function VerticalGridLines(props) { return ; } VerticalGridLines.displayName = 'VerticalGridLines'; VerticalGridLines.propTypes = propTypes; VerticalGridLines.defaultProps = defaultProps; VerticalGridLines.requiresSVG = true; export default VerticalGridLines; ================================================ FILE: packages/react-vis/src/plot/voronoi.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import {voronoi} from 'd3-voronoi'; import {getAttributeFunctor} from 'utils/scales-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const NOOP = f => f; // Find the index of the node at coordinates of a touch point function getNodeIndex(evt) { const { nativeEvent: {pageX, pageY} } = evt; const target = document.elementFromPoint(pageX, pageY); if (!target) { return -1; } const {parentNode} = target; return Array.prototype.indexOf.call(parentNode.childNodes, target); } function getExtent({innerWidth, innerHeight, marginLeft, marginTop}) { return [ [marginLeft, marginTop], [innerWidth + marginLeft, innerHeight + marginTop] ]; } function Voronoi(props) { const { className, extent, nodes, onBlur, onClick, onMouseUp, onMouseDown, onHover, polygonStyle, style, x, y } = props; // Create a voronoi with each node center points const voronoiInstance = voronoi() .x(x || getAttributeFunctor(props, 'x')) .y(y || getAttributeFunctor(props, 'y')) .extent(extent || getExtent(props)); // Create an array of polygons corresponding to the cells in voronoi const polygons = voronoiInstance.polygons(nodes); // Create helper function to handle special logic for touch events const handleTouchEvent = handler => evt => { evt.preventDefault(); const index = getNodeIndex(evt); if (index > -1 && index < polygons.length) { const d = polygons[index]; handler(d.data); } }; return ( {polygons.map((d, i) => ( onClick(d.data)} onMouseUp={() => onMouseUp(d.data)} onMouseDown={() => onMouseDown(d.data)} onMouseOver={() => onHover(d.data)} onMouseOut={() => onBlur(d.data)} fill="none" style={{ pointerEvents: 'all', ...polygonStyle, ...(d.data && d.data.style) }} key={i} /> ))} ); } Voronoi.requiresSVG = true; Voronoi.displayName = 'Voronoi'; Voronoi.defaultProps = { className: '', onBlur: NOOP, onClick: NOOP, onHover: NOOP, onMouseDown: NOOP, onMouseUp: NOOP }; Voronoi.propTypes = { className: PropTypes.string, extent: PropTypes.arrayOf(PropTypes.arrayOf(PropTypes.number)), nodes: PropTypes.arrayOf(PropTypes.object).isRequired, onBlur: PropTypes.func, onClick: PropTypes.func, onHover: PropTypes.func, onMouseDown: PropTypes.func, onMouseUp: PropTypes.func, x: PropTypes.func, y: PropTypes.func }; export default Voronoi; ================================================ FILE: packages/react-vis/src/plot/xy-plot.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import equal from 'deep-equal'; import {getCombinedClassName} from 'utils/styling-utils'; import { extractScalePropsFromProps, getMissingScaleProps, getOptionalScaleProps, getXYPlotValues } from 'utils/scales-utils'; import { getStackedData, getSeriesChildren, getSeriesPropsFromChildren } from 'utils/series-utils'; import { getInnerDimensions, MarginPropType, DEFAULT_MARGINS } from 'utils/chart-utils'; import {AnimationPropType} from 'animation'; import { CONTINUOUS_COLOR_RANGE, EXTENDED_DISCRETE_COLOR_RANGE, SIZE_RANGE, OPACITY_TYPE } from 'theme'; import CanvasWrapper from './series/canvas-wrapper'; const ATTRIBUTES = [ 'x', 'y', 'radius', 'angle', 'color', 'fill', 'stroke', 'opacity', 'size' ]; /** * Remove parents from tree formatted data. deep-equal doesnt play nice with data * that has circular structures, so we make every node single directional by pruning the parents. * @param {Array} data - the data object to have circular deps resolved in * @returns {Array} the sanitized data */ function cleanseData(data) { return data.map(series => { if (!Array.isArray(series)) { return series; } return series.map(row => ({...row, parent: null})); }); } /** * Wrapper on the deep-equal method for checking equality of next props vs current props * @param {Object} scaleMixins - Scale object. * @param {Object} nextScaleMixins - Scale object. * @param {Boolean} hasTreeStructure - Whether or not to cleanse the data of possible cyclic structures * @returns {Boolean} whether or not the two mixins objects are equal */ function checkIfMixinsAreEqual(nextScaleMixins, scaleMixins, hasTreeStructure) { const newMixins = { ...nextScaleMixins, _allData: hasTreeStructure ? cleanseData(nextScaleMixins._allData) : nextScaleMixins._allData }; const oldMixins = { ...scaleMixins, _allData: hasTreeStructure ? cleanseData(scaleMixins._allData) : scaleMixins._allData }; // it's hard to say if this function is reasonable? return equal(newMixins, oldMixins); } class XYPlot extends React.Component { static get defaultProps() { return { className: '' }; } static get propTypes() { return { animation: AnimationPropType, className: PropTypes.string, dontCheckIfEmpty: PropTypes.bool, height: PropTypes.number.isRequired, margin: MarginPropType, onClick: PropTypes.func, onDoubleClick: PropTypes.func, onMouseDown: PropTypes.func, onMouseUp: PropTypes.func, onMouseEnter: PropTypes.func, onMouseLeave: PropTypes.func, onMouseMove: PropTypes.func, onTouchStart: PropTypes.func, onTouchMove: PropTypes.func, onTouchEnd: PropTypes.func, onTouchCancel: PropTypes.func, onWheel: PropTypes.func, stackBy: PropTypes.oneOf(ATTRIBUTES), style: PropTypes.object, width: PropTypes.number.isRequired }; } constructor(props) { super(props); const {stackBy} = props; const children = getSeriesChildren(props.children); const data = getStackedData(children, stackBy); this.state = { scaleMixins: XYPlot._getScaleMixins(data, props), data }; } static getDerivedStateFromProps(nextProps) { const children = getSeriesChildren(nextProps.children); const nextData = getStackedData(children, nextProps.stackBy); const {scaleMixins} = this.state; const nextScaleMixins = XYPlot._getScaleMixins(nextData, nextProps); if ( !checkIfMixinsAreEqual( nextScaleMixins, scaleMixins, nextProps.hasTreeStructure ) ) { return { scaleMixins: nextScaleMixins, data: nextData }; } return null; } /** * Trigger click related callbacks if they are available. * @param {React.SyntheticEvent} event Click event. * @private */ _clickHandler = event => { const {onClick} = this.props; if (onClick) { onClick(event); } }; /** * Trigger doule-click related callbacks if they are available. * @param {React.SyntheticEvent} event Double-click event. * @private */ _doubleClickHandler = event => { const {onDoubleClick} = this.props; if (onDoubleClick) { onDoubleClick(event); } }; /** * Prepare the child components (including series) for rendering. * @returns {Array} Array of child components. * @private */ _getClonedChildComponents() { const props = this.props; const {animation} = this.props; const {scaleMixins, data} = this.state; const dimensions = getInnerDimensions(this.props, DEFAULT_MARGINS); const children = React.Children.toArray(this.props.children); const seriesProps = getSeriesPropsFromChildren(children); const XYPlotValues = getXYPlotValues(props, children); return children.map((child, index) => { let dataProps = null; if (seriesProps[index]) { // Get the index of the series in the list of props and retrieve // the data property from it. const {seriesIndex} = seriesProps[index]; dataProps = {data: data[seriesIndex]}; } return React.cloneElement(child, { ...dimensions, animation, ...(dataProps && child.type.prototype && child.type.prototype.render ? { ref: ref => (this[`series${seriesProps[index].seriesIndex}`] = ref) } : {}), ...seriesProps[index], ...scaleMixins, ...child.props, ...XYPlotValues[index], ...dataProps }); }); } /** * Get the list of scale-related settings that should be applied by default. * @param {Object} props Object of props. * @returns {Object} Defaults. * @private */ static _getDefaultScaleProps(props) { const {innerWidth, innerHeight} = getInnerDimensions( props, DEFAULT_MARGINS ); const colorRanges = ['color', 'fill', 'stroke'].reduce((acc, attr) => { const range = props[`${attr}Type`] === 'category' ? EXTENDED_DISCRETE_COLOR_RANGE : CONTINUOUS_COLOR_RANGE; return {...acc, [`${attr}Range`]: range}; }, {}); return { xRange: [0, innerWidth], yRange: [innerHeight, 0], ...colorRanges, opacityType: OPACITY_TYPE, sizeRange: SIZE_RANGE }; } /** * Get the map of scales from the props, apply defaults to them and then pass * them further. * @param {Object} data Array of all data. * @param {Object} props Props of the component. * @returns {Object} Map of scale-related props. * @private */ static _getScaleMixins(data, props) { const filteredData = data.filter(d => d); const allData = [].concat(...filteredData); const defaultScaleProps = XYPlot._getDefaultScaleProps(props); const optionalScaleProps = getOptionalScaleProps(props); const userScaleProps = extractScalePropsFromProps(props, ATTRIBUTES); const missingScaleProps = getMissingScaleProps( { ...defaultScaleProps, ...optionalScaleProps, ...userScaleProps }, allData, ATTRIBUTES ); const children = getSeriesChildren(props.children); const zeroBaseProps = {}; const adjustBy = new Set(); const adjustWhat = new Set(); children.forEach((child, index) => { if (!child || !data[index]) { return; } ATTRIBUTES.forEach(attr => { const { isDomainAdjustmentNeeded, zeroBaseValue } = child.type.getParentConfig(attr, child.props); if (isDomainAdjustmentNeeded) { adjustBy.add(attr); adjustWhat.add(index); } if (zeroBaseValue) { const specifiedDomain = props[`${attr}Domain`]; zeroBaseProps[`${attr}BaseValue`] = specifiedDomain ? specifiedDomain[0] : 0; } }); }); return { ...defaultScaleProps, ...zeroBaseProps, ...userScaleProps, ...missingScaleProps, _allData: data, _adjustBy: Array.from(adjustBy), _adjustWhat: Array.from(adjustWhat), _stackBy: props.stackBy }; } /** * Checks if the plot is empty or not. * Currently checks the data only. * @returns {boolean} True for empty. * @private */ _isPlotEmpty() { const {data} = this.state; return ( !data || !data.length || !data.some(series => series && series.some(d => d)) ); } /** * Trigger mouse-down related callbacks if they are available. * @param {React.SyntheticEvent} event Mouse down event. * @private */ _mouseDownHandler = event => { const {onMouseDown, children} = this.props; if (onMouseDown) { onMouseDown(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentMouseDown) { component.onParentMouseDown(event); } }); }; /** * Trigger onMouseEnter handler if it was passed in props. * @param {React.SyntheticEvent} event Mouse enter event. * @private */ _mouseEnterHandler = event => { const {onMouseEnter, children} = this.props; if (onMouseEnter) { onMouseEnter(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentMouseEnter) { component.onParentMouseEnter(event); } }); }; /** * Trigger onMouseLeave handler if it was passed in props. * @param {React.SyntheticEvent} event Mouse leave event. * @private */ _mouseLeaveHandler = event => { const {onMouseLeave, children} = this.props; if (onMouseLeave) { onMouseLeave(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentMouseLeave) { component.onParentMouseLeave(event); } }); }; /** * Trigger movement-related callbacks if they are available. * @param {React.SyntheticEvent} event Mouse move event. * @private */ _mouseMoveHandler = event => { const {onMouseMove, children} = this.props; if (onMouseMove) { onMouseMove(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentMouseMove) { component.onParentMouseMove(event); } }); }; /** * Trigger mouse-up related callbacks if they are available. * @param {React.SyntheticEvent} event Mouse up event. * @private */ _mouseUpHandler = event => { const {onMouseUp, children} = this.props; if (onMouseUp) { onMouseUp(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentMouseUp) { component.onParentMouseUp(event); } }); }; /** * Trigger onTouchCancel handler if it was passed in props. * @param {React.SyntheticEvent} event Touch Cancel event. * @private */ _touchCancelHandler = event => { const {onTouchCancel} = this.props; if (onTouchCancel) { onTouchCancel(event); } }; /** * Trigger onTouchEnd handler if it was passed in props. * @param {React.SyntheticEvent} event Touch End event. * @private */ _touchEndHandler = event => { const {onTouchEnd} = this.props; if (onTouchEnd) { onTouchEnd(event); } }; /** * Trigger touch movement-related callbacks if they are available. * @param {React.SyntheticEvent} event Touch move event. * @private */ _touchMoveHandler = event => { const {onTouchMove, children} = this.props; if (onTouchMove) { onTouchMove(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentTouchMove) { component.onParentTouchMove(event); } }); }; /** * Trigger touch-start related callbacks if they are available. * @param {React.SyntheticEvent} event Touch start event. * @private */ _touchStartHandler = event => { const {onTouchStart, children} = this.props; if (onTouchStart) { onTouchStart(event); } const seriesChildren = getSeriesChildren(children); seriesChildren.forEach((child, index) => { const component = this[`series${index}`]; if (component && component.onParentTouchStart) { component.onParentTouchStart(event); } }); }; renderCanvasComponents(components) { const componentsToRender = components.filter( c => c && !c.type.requiresSVG && c.type.isCanvas ); if (componentsToRender.length === 0) { return null; } const { marginLeft, marginTop, marginBottom, marginRight, innerHeight, innerWidth } = componentsToRender[0].props; return ( {componentsToRender} ); } render() { const { className, dontCheckIfEmpty, style, width, height, onWheel } = this.props; if (!dontCheckIfEmpty && this._isPlotEmpty()) { return (
); } const components = this._getClonedChildComponents(); return (
{components.filter(c => c && c.type.requiresSVG)} {this.renderCanvasComponents(components)} {components.filter(c => c && !c.type.requiresSVG && !c.type.isCanvas)}
); } } XYPlot.displayName = 'XYPlot'; export default XYPlot; ================================================ FILE: packages/react-vis/src/radar-chart/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {scaleLinear} from 'd3-scale'; import {format} from 'd3-format'; import {AnimationPropType} from 'animation'; import XYPlot from 'plot/xy-plot'; import {DISCRETE_COLOR_RANGE} from 'theme'; import {MarginPropType} from 'utils/chart-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import MarkSeries from 'plot/series/mark-series'; import PolygonSeries from 'plot/series/polygon-series'; import LabelSeries from 'plot/series/label-series'; import DecorativeAxis from 'plot/axis/decorative-axis'; const predefinedClassName = 'rv-radar-chart'; const DEFAULT_FORMAT = format('.2r'); /** * Generate axes for each of the domains * @param {Object} props - props.animation {Boolean} - props.domains {Array} array of object specifying the way each axis is to be plotted - props.style {object} style object for the whole chart - props.tickFormat {Function} formatting function for axes - props.startingAngle {number} the initial angle offset * @return {Array} the plotted axis components */ function getAxes(props) { const { animation, domains, startingAngle, style, tickFormat, hideInnerMostValues } = props; return domains.map((domain, index) => { const angle = (index / domains.length) * Math.PI * 2 + startingAngle; const sortedDomain = domain.domain; const domainTickFormat = t => { if (hideInnerMostValues && t === sortedDomain[0]) { return ''; } return domain.tickFormat ? domain.tickFormat(t) : tickFormat(t); }; return ( ); }); } /** * Generate x or y coordinate for axisEnd * @param {Number} axisEndPoint - epsilon is an arbitrarily chosen small number to approximate axisEndPoints - to true values resulting from trigonometry functions (sin, cos) on angles * @return {Number} the x or y coordinate accounting for exact trig values */ function getCoordinate(axisEndPoint) { const epsilon = 10e-13; if (Math.abs(axisEndPoint) <= epsilon) { axisEndPoint = 0; } else if (axisEndPoint > 0) { if (Math.abs(axisEndPoint - 0.5) <= epsilon) { axisEndPoint = 0.5; } } else if (axisEndPoint < 0) { if (Math.abs(axisEndPoint + 0.5) <= epsilon) { axisEndPoint = -0.5; } } return axisEndPoint; } /** * Generate labels for the ends of the axes * @param {Object} props - props.domains {Array} array of object specifying the way each axis is to be plotted - props.startingAngle {number} the initial angle offset - props.style {object} style object for just the labels * @return {Array} the prepped data for the labelSeries */ function getLabels(props) { const {domains, startingAngle, style} = props; return domains.map(({name}, index) => { const angle = (index / domains.length) * Math.PI * 2 + startingAngle; const radius = 1.2; return { x: radius * Math.cos(angle), y: radius * Math.sin(angle), label: name, style }; }); } /** * Generate the actual polygons to be plotted * @param {Object} props - props.animation {Boolean} - props.data {Array} array of object specifying what values are to be plotted - props.domains {Array} array of object specifying the way each axis is to be plotted - props.startingAngle {number} the initial angle offset - props.style {object} style object for the whole chart * @return {Array} the plotted axis components */ function getPolygons(props) { const { animation, colorRange, domains, data, style, startingAngle, onSeriesMouseOver, onSeriesMouseOut } = props; const scales = domains.reduce((acc, {domain, name}) => { acc[name] = scaleLinear() .domain(domain) .range([0, 1]); return acc; }, {}); return data.map((row, rowIndex) => { const mappedData = domains.map(({name, getValue}, index) => { const dataPoint = getValue ? getValue(row) : row[name]; // error handling if point doesn't exist const angle = (index / domains.length) * Math.PI * 2 + startingAngle; // dont let the radius become negative const radius = Math.max(scales[name](dataPoint), 0); return { x: radius * Math.cos(angle), y: radius * Math.sin(angle), name: row.name }; }); return ( ); }); } /** * Generate circles at the polygon points for Hover functionality * @param {Object} props - props.animation {Boolean} - props.data {Array} array of object specifying what values are to be plotted - props.domains {Array} array of object specifying the way each axis is to be plotted - props.startingAngle {number} the initial angle offset - props.style {object} style object for the whole chart - props.onValueMouseOver {function} function to call on mouse over a polygon point - props.onValueMouseOver {function} function to call when mouse leaves a polygon point * @return {Array} the plotted axis components */ function getPolygonPoints(props) { const { animation, domains, data, startingAngle, style, onValueMouseOver, onValueMouseOut } = props; if (!onValueMouseOver) { return; } const scales = domains.reduce((acc, {domain, name}) => { acc[name] = scaleLinear() .domain(domain) .range([0, 1]); return acc; }, {}); return data.map((row, rowIndex) => { const mappedData = domains.map(({name, getValue}, index) => { const dataPoint = getValue ? getValue(row) : row[name]; // error handling if point doesn't exist const angle = (index / domains.length) * Math.PI * 2 + startingAngle; // dont let the radius become negative const radius = Math.max(scales[name](dataPoint), 0); return { x: radius * Math.cos(angle), y: radius * Math.sin(angle), domain: name, value: dataPoint, dataName: row.name }; }); return ( ); }); } function RadarChart(props) { const { animation, className, children, colorRange, data, domains, height, hideInnerMostValues, margin, onMouseLeave, onMouseEnter, startingAngle, style, tickFormat, width, renderAxesOverPolygons, onValueMouseOver, onValueMouseOut, onSeriesMouseOver, onSeriesMouseOut } = props; const axes = getAxes({ domains, animation, hideInnerMostValues, startingAngle, style, tickFormat }); const polygons = getPolygons({ animation, colorRange, domains, data, startingAngle, style, onSeriesMouseOver, onSeriesMouseOut }); const polygonPoints = getPolygonPoints({ animation, colorRange, domains, data, startingAngle, style, onValueMouseOver, onValueMouseOut }); const labelSeries = ( ); return ( {children} {!renderAxesOverPolygons && axes .concat(polygons) .concat(labelSeries) .concat(polygonPoints)} {renderAxesOverPolygons && polygons .concat(labelSeries) .concat(axes) .concat(polygonPoints)} ); } RadarChart.displayName = 'RadarChart'; RadarChart.propTypes = { animation: AnimationPropType, className: PropTypes.string, colorType: PropTypes.string, colorRange: PropTypes.arrayOf(PropTypes.string), data: PropTypes.arrayOf(PropTypes.object).isRequired, domains: PropTypes.arrayOf( PropTypes.shape({ name: PropTypes.string.isRequired, domain: PropTypes.arrayOf(PropTypes.number).isRequired, tickFormat: PropTypes.func }) ).isRequired, height: PropTypes.number.isRequired, hideInnerMostValues: PropTypes.bool, margin: MarginPropType, startingAngle: PropTypes.number, style: PropTypes.shape({ axes: PropTypes.object, labels: PropTypes.object, polygons: PropTypes.object }), tickFormat: PropTypes.func, width: PropTypes.number.isRequired, renderAxesOverPolygons: PropTypes.bool, onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func, onSeriesMouseOver: PropTypes.func, onSeriesMouseOut: PropTypes.func }; RadarChart.defaultProps = { className: '', colorType: 'category', colorRange: DISCRETE_COLOR_RANGE, hideInnerMostValues: true, startingAngle: Math.PI / 2, style: { axes: { line: {}, ticks: {}, text: {} }, labels: { fontSize: 10, textAnchor: 'middle' }, polygons: { strokeWidth: 0.5, strokeOpacity: 1, fillOpacity: 0.1 } }, tickFormat: DEFAULT_FORMAT, renderAxesOverPolygons: false }; export default RadarChart; ================================================ FILE: packages/react-vis/src/radial-chart/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {pie as pieBuilder} from 'd3-shape'; import {AnimationPropType} from 'animation'; import ArcSeries from 'plot/series/arc-series'; import LabelSeries from 'plot/series/label-series'; import XYPlot from 'plot/xy-plot'; import {DISCRETE_COLOR_RANGE} from 'theme'; import {MarginPropType, getRadialLayoutMargin} from 'utils/chart-utils'; import {getRadialDomain} from 'utils/series-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-radial-chart'; const DEFAULT_RADIUS_MARGIN = 15; /** * Create the list of wedges to render. * @param {Object} props props.data {Object} - tree structured data (each node has a name anc an array of children) * @returns {Array} Array of nodes. */ function getWedgesToRender({data, getAngle}) { const pie = pieBuilder() .sort(null) .value(getAngle); const pieData = pie(data).reverse(); return pieData.map((row, index) => { return { ...row.data, angle0: row.startAngle, angle: row.endAngle, radius0: row.data.innerRadius || 0, radius: row.data.radius || 1, color: row.data.color || index }; }); } function generateLabels(mappedData, accessors, labelsRadiusMultiplier = 1.1) { const {getLabel, getSubLabel} = accessors; return mappedData.reduce((res, row) => { const {angle, angle0, radius} = row; const centeredAngle = (angle + angle0) / 2; // unfortunate, but true fact: d3 starts its radians at 12 oclock rather than 3 // and move clockwise rather than counter clockwise. why why why! const updatedAngle = -1 * centeredAngle + Math.PI / 2; const newLabels = []; if (getLabel(row)) { newLabels.push({ angle: updatedAngle, radius: radius * labelsRadiusMultiplier, label: getLabel(row) }); } if (getSubLabel(row)) { newLabels.push({ angle: updatedAngle, radius: radius * labelsRadiusMultiplier, label: getSubLabel(row), style: {fontSize: 10}, yOffset: 12 }); } return res.concat(newLabels); }, []); // could add force direction here to make sure the labels dont overlap } /** * Get the max radius so the chart can extend to the margin. * @param {Number} width - container width * @param {Number} height - container height * @return {Number} radius */ function getMaxRadius(width, height) { return Math.min(width, height) / 2 - DEFAULT_RADIUS_MARGIN; } function RadialChart(props) { const { animation, className, children, colorType, data, getAngle, getLabel, getSubLabel, height, hideRootNode, innerRadius, labelsAboveChildren, labelsRadiusMultiplier, labelsStyle, margin, onMouseLeave, onMouseEnter, radius, showLabels, style, width } = props; const mappedData = getWedgesToRender({ data, height, hideRootNode, width, getAngle }); const radialDomain = getRadialDomain(mappedData); const arcProps = { colorType, ...props, animation, radiusDomain: [0, radialDomain], data: mappedData, radiusNoFallBack: true, style, arcClassName: 'rv-radial-chart__series--pie__slice' }; if (radius) { arcProps.radiusDomain = [0, 1]; arcProps.radiusRange = [innerRadius || 0, radius]; arcProps.radiusType = 'linear'; } const maxRadius = radius ? radius : getMaxRadius(width, height); const defaultMargin = getRadialLayoutMargin(width, height, maxRadius); const labels = generateLabels( mappedData, { getLabel, getSubLabel }, labelsRadiusMultiplier ); return ( d.angle} /> {showLabels && !labelsAboveChildren && ( )} {children} {showLabels && labelsAboveChildren && ( )} ); } RadialChart.displayName = 'RadialChart'; RadialChart.propTypes = { animation: AnimationPropType, className: PropTypes.string, colorType: PropTypes.string, data: PropTypes.arrayOf( PropTypes.shape({ angle: PropTypes.number, className: PropTypes.string, label: PropTypes.string, radius: PropTypes.number, style: PropTypes.object }) ).isRequired, getAngle: PropTypes.func, getAngle0: PropTypes.func, padAngle: PropTypes.oneOfType([PropTypes.func, PropTypes.number]), getRadius: PropTypes.func, getRadius0: PropTypes.func, getLabel: PropTypes.func, height: PropTypes.number.isRequired, labelsAboveChildren: PropTypes.bool, labelsStyle: PropTypes.object, margin: MarginPropType, onValueClick: PropTypes.func, onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func, showLabels: PropTypes.bool, style: PropTypes.object, subLabel: PropTypes.func, width: PropTypes.number.isRequired }; RadialChart.defaultProps = { className: '', colorType: 'category', colorRange: DISCRETE_COLOR_RANGE, padAngle: 0, getAngle: d => d.angle, getAngle0: d => d.angle0, getRadius: d => d.radius, getRadius0: d => d.radius0, getLabel: d => d.label, getSubLabel: d => d.subLabel }; export default RadialChart; ================================================ FILE: packages/react-vis/src/sankey/index.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import { sankey, sankeyLinkHorizontal, sankeyLeft, sankeyRight, sankeyCenter, sankeyJustify } from 'd3-sankey'; import XYPlot from 'plot/xy-plot'; import {MarginPropType, getInnerDimensions} from 'utils/chart-utils'; import {getCombinedClassName} from 'utils/styling-utils'; import VerticalRectSeries from 'plot/series/vertical-rect-series'; import LabelSeries from 'plot/series/label-series'; import Voronoi from 'plot/voronoi'; import {DISCRETE_COLOR_RANGE} from 'theme'; import SankeyLink from './sankey-link'; const NOOP = f => f; const ALIGNMENTS = { justify: sankeyJustify, center: sankeyCenter, left: sankeyLeft, right: sankeyRight }; const DEFAULT_MARGINS = { top: 20, left: 20, right: 20, bottom: 20 }; function Sankey(props) { const { align, animation, children, className, hasVoronoi, height, hideLabels, labelRotation, layout, links, linkOpacity, margin, nodePadding, nodes, nodeWidth, onValueClick, onValueMouseOver, onValueMouseOut, onLinkClick, onLinkMouseOver, onLinkMouseOut, style, width } = props; // d3-sankey sankeyInstance no longer accepts empty nodes array, return empty XYPlot by default if (nodes.length === 0) { return ( ); } const nodesCopy = [...new Array(nodes.length)].map((e, i) => ({ ...nodes[i] })); const linksCopy = [...new Array(links.length)].map((e, i) => ({ ...links[i] })); const {marginLeft, marginTop, marginRight, marginBottom} = getInnerDimensions( { margin, height, width }, DEFAULT_MARGINS ); const sankeyInstance = sankey() .extent([ [marginLeft, marginTop], [width - marginRight, height - marginBottom - marginTop] ]) .nodeWidth(nodeWidth) .nodePadding(nodePadding) .nodes(nodesCopy) .links(linksCopy) .nodeAlign(ALIGNMENTS[align]) .iterations(layout); sankeyInstance(nodesCopy); const nWidth = sankeyInstance.nodeWidth(); const path = sankeyLinkHorizontal(); return ( {linksCopy.map((link, i) => ( ))} ({ ...node, y: node.y1 - marginTop, y0: node.y0 - marginTop, x: node.x1, x0: node.x0, color: node.color || DISCRETE_COLOR_RANGE[0], sourceLinks: null, targetLinks: null }))} style={style.rects} onValueClick={onValueClick} onValueMouseOver={onValueMouseOver} onValueMouseOut={onValueMouseOut} colorType="literal" /> {!hideLabels && ( { return { x: node.x0 + (node.x0 < width / 2 ? nWidth + 10 : -10), y: (node.y0 + node.y1) / 2 - marginTop, label: node.name, style: { textAnchor: node.x0 < width / 2 ? 'start' : 'end', dy: '-.5em', ...style.labels }, // unfortunately this can not be ...node as the version // found in nodesCopy is modified by the sankey process ...nodes[i] }; })} /> )} {hasVoronoi && ( d.x0 + (d.x1 - d.x0) / 2} y={d => d.y0 + (d.y1 - d.y0) / 2} /> )} {children} ); } Sankey.defaultProps = { align: 'justify', className: '', hasVoronoi: false, hideLabels: false, labelRotation: 0, layout: 50, margin: DEFAULT_MARGINS, nodePadding: 10, nodeWidth: 10, onValueMouseOver: NOOP, onValueClick: NOOP, onValueMouseOut: NOOP, onLinkClick: NOOP, onLinkMouseOver: NOOP, onLinkMouseOut: NOOP, style: { links: {}, rects: {}, labels: {} } }; Sankey.propTypes = { align: PropTypes.oneOf(['justify', 'left', 'right', 'center']), className: PropTypes.string, hasVoronoi: PropTypes.bool, height: PropTypes.number.isRequired, hideLabels: PropTypes.bool, labelRotation: PropTypes.number, layout: PropTypes.number, links: PropTypes.arrayOf( PropTypes.shape({ source: PropTypes.oneOfType([PropTypes.number, PropTypes.object]) .isRequired, target: PropTypes.oneOfType([PropTypes.number, PropTypes.object]) .isRequired }) ).isRequired, margin: MarginPropType, nodePadding: PropTypes.number, nodes: PropTypes.arrayOf(PropTypes.object).isRequired, nodeWidth: PropTypes.number, onValueMouseOver: PropTypes.func, onValueClick: PropTypes.func, onValueMouseOut: PropTypes.func, onLinkClick: PropTypes.func, onLinkMouseOver: PropTypes.func, onLinkMouseOut: PropTypes.func, style: PropTypes.shape({ links: PropTypes.object, rects: PropTypes.object, labels: PropTypes.object }), width: PropTypes.number.isRequired }; export default Sankey; ================================================ FILE: packages/react-vis/src/sankey/sankey-link.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {DISCRETE_COLOR_RANGE} from 'theme'; import Animation from 'animation'; import {ANIMATED_SERIES_PROPS} from 'utils/series-utils'; const DEFAULT_LINK_COLOR = DISCRETE_COLOR_RANGE[1]; const DEFAULT_LINK_OPACITY = 0.7; function SankeyLink(props) { const { animation, data, node, opacity, color, strokeWidth, style, onLinkClick, onLinkMouseOver, onLinkMouseOut } = props; if (animation) { return ( ); } return ( onLinkClick(node, e)} onMouseOver={e => onLinkMouseOver(node, e)} onMouseOut={e => onLinkMouseOut(node, e)} strokeWidth={strokeWidth} fill="none" /> ); } SankeyLink.displayName = 'SankeyLink'; SankeyLink.requiresSVG = true; export default SankeyLink; ================================================ FILE: packages/react-vis/src/styles/examples.scss ================================================ @import '../main.scss'; $black: #000; $white: #fff; body { font-family: Sintony, Helvetica, sans-serif; font-size: 14px; margin: 0; padding: 0; } h1, h2, h3, h4, h5 { font-weight: normal; } h1 { font-size: 36px; margin: 20px 0; } h2 { font-size: 24px; margin: 15px 0; } main { padding: 40px 0; } header { background: #f0f0f0; line-height: 40px; position: fixed; top: 0; width: 100%; z-index: 1000; } .flex { display: flex; } .docs-link { font-weight: 500; font-size: 11px; margin-right: 5px; text-transform: uppercase; border-left: 1px solid #c0c0c0; padding-left: 5px; line-height: 1; } .docs-link:first-child { border-left: 0px; padding-left: 0px; } .docs-comment { display: flex; max-width: 300px; } .header-contents { align-items: center; display: flex; justify-content: space-between; padding: 0 20px; } .header-logo { color: $black; float: left; font-size: 20px; text-decoration: none; } .background-overlay { bottom: 0; left: 0; position: fixed; right: 0; top: 0; z-index: 1; } .dropdown-button { cursor: pointer; z-index: 10; } .dropdown-wrapper { display: flex; position: relative; .dropdown-inner-wrapper { background: $white; border: 2px solid $black; display: flex; flex-direction: column; font-size: 11px; height: auto; list-style: none; padding: 10px; position: absolute; right: -5px; top: 25px; width: 150px; z-index: 10; } a { display: flex; height: auto; line-height: 15px; text-decoration: none; } li { display: flex; height: 100%; } .subsection-label { font-weight: 600; line-height: 15px; } } article { display: flex; flex-direction: row; flex-wrap: wrap; justify-content: flex-start; margin: 0 auto; max-width: 1200px; min-width: 650px; padding: 30px 20px 0; h1, h2 { flex: 1 100%; small { color: #6b6b76; font-size: 50%; } } section { flex-basis: 400px; flex-grow: 1; margin: 0 0 40px; } .section-title { margin-bottom: 5px; } .section-header { margin-bottom: 1em; } } .click-me { border: 0; background: #ef5d28; color: $white; cursor: pointer; font-family: Sintony, Helvetica, sans-serif; font-size: 14px; outline: none; padding: 11px 20px; text-transform: uppercase; &:hover { background: #ff9833; } animation: shake 5s 1s cubic-bezier(0.36, 0.07, 0.19, 0.97) both infinite; transform: translate3d(0, 0, 0); } @keyframes shake { 1%, 9% { transform: translate3d(-1px, 0, 0); } 2%, 8% { transform: translate3d(2px, 0, 0); } 3%, 5%, 7% { transform: translate3d(-4px, 0, 0); } 4%, 6% { transform: translate3d(4px, 0, 0); } } .example-with-click-me { position: relative; text-align: center; width: 100%; &:hover { .click-me { animation: none; } } .chart { margin-right: 200px; .rv-xy-plot__axis__tick__line { stroke: $rv-xy-plot-axis-font-color; } } .legend { position: absolute; text-align: left; right: 0; } } .custom-hint { background: #f9e7bb; border-radius: 3px; border: 1px solid #edaf00; padding: 10px; color: #333; font-size: 10px; position: relative; margin: 12px 0 0 -10px; &::after { border-radius: 5px; border: 2px solid #edaf00; background: $white; display: block; content: ' '; height: 6px; width: 6px; top: -17px; left: 5px; position: absolute; } } .complex-hint { margin-top: 40px; .rv-hint { /* must be positioned in a parent with relative positioning */ position: absolute; width: 0; height: 100%; $hint-color: black; $margin-left: 30px; $margin-right: 10px; $margin-top: 10px; $margin-bottom: 25px; & .hint--text-container { position: absolute; /* * set to 0,0 so that its content (including children) * can overflow out in vertical and horizontal */ width: 0; height: 0; /* * use flex to place its children (centered) and aligned (bottom). * As its height is 0, align-items flex-end paints its items from cross-axis * up. flex-start, its items would paint from cross-axis down. */ display: flex; justify-content: center; &.rightEdge-top { flex-direction: column-reverse; align-items: flex-start; } &.left-topEdge { flex-direction: row; align-items: flex-end; } &.left-bottomEdge { flex-direction: row; align-items: flex-start; } &.leftEdge-top { flex-direction: column; align-items: flex-end; } & .hint--text { /* text content uses -micro padding */ padding: 4px; border: 2px solid $hint-color; color: $hint-color; white-space: nowrap; } } & .hint--pole { position: absolute; &.rightEdge-top { top: -1px; left: -$margin-right; border-top: 2px solid $hint-color; width: $margin-right; height: 0; } &.left-topEdge { border-left: 2px solid $hint-color; left: -1px; height: $margin-top; width: 0; top: 0; } &.left-bottomEdge { border-left: 2px solid $hint-color; left: -1px; height: $margin-bottom; width: 0; top: -$margin-bottom; } &.leftEdge-top { top: -1px; border-top: 2px solid $hint-color; width: $margin-left; height: 0; } } } .rv-hint--horizontalAlign-rightEdge.rv-hint--verticalAlign-top { width: 0; height: 0; } .rv-hint--horizontalAlign-left.rv-hint--verticalAlign-topEdge { width: 0; height: 100%; } .rv-hint--horizontalAlign-left.rv-hint--verticalAlign-bottomEdge { width: 0; height: 0; } .rv-hint--horizontalAlign-leftEdge.rv-hint--verticalAlign-top { width: 100%; height: 0; } } .centered-and-flexed { align-items: center; display: flex; flex-direction: column; justify-content: center; padding: 0 10px; .centered-and-flexed-controls { align-items: center; display: flex; justify-content: space-between; padding: 10px 0; width: 75%; } } .dynamic-treemap-example { .rv-treemap__leaf--circle { border: thin solid white; } } .clustered-stacked-bar-chart-example { .rv-discrete-color-legend { left: 40px; position: absolute; top: 0; } } .basic-sunburst-example-path-name { height: 20px; } .showcase-button { background: $white; border: thin solid #333; border-radius: 5px; cursor: pointer; font-size: 10px; font-weight: 600; padding: 5px 10px; } .donut-chart-example { .rv-radial-chart__series--pie__slice:hover { stroke: $black !important; stroke-width: 2px !important; } } .parallel-coordinates-example { .rv-xy-plot__series--line { stroke: #12939A !important; &:hover { stroke: #F15C17 !important; } } } .canvas-example-controls { display: flex; } .canvas-wrapper { align-items: center; display: flex; flex-direction: column; width: 100%; } .highlight-container { cursor: crosshair; } .no-select { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } ================================================ FILE: packages/react-vis/src/styles/legends.scss ================================================ $rv-legend-enabled-color: #3a3a48; $rv-legend-disabled-color: #b8b8b8; .rv-discrete-color-legend { box-sizing: border-box; overflow-y: auto; font-size: 12px; &.horizontal { white-space: nowrap; } } .rv-discrete-color-legend-item { color: $rv-legend-enabled-color; border-radius: 1px; padding: 9px 10px; &.horizontal { display: inline-block; .rv-discrete-color-legend-item__title { margin-left: 0; display: block; } } } .rv-discrete-color-legend-item__color { display: inline-block; vertical-align: middle; overflow: visible; } .rv-discrete-color-legend-item__color__path { stroke: #dcdcdc; stroke-width: 2px; } .rv-discrete-color-legend-item__title { margin-left: 10px; } .rv-discrete-color-legend-item.disabled { color: $rv-legend-disabled-color; } .rv-discrete-color-legend-item.clickable { cursor: pointer; &:hover { background: #f9f9f9; } } .rv-search-wrapper { display: flex; flex-direction: column; } .rv-search-wrapper__form { flex: 0; } .rv-search-wrapper__form__input { width: 100%; color: #a6a6a5; border: 1px solid #e5e5e4; padding: 7px 10px; font-size: 12px; box-sizing: border-box; border-radius: 2px; margin: 0 0 9px; outline: 0; } .rv-search-wrapper__contents { flex: 1; overflow: auto; } .rv-continuous-color-legend { font-size: 12px; .rv-gradient { height: 4px; border-radius: 2px; margin-bottom: 5px; } } .rv-continuous-size-legend { font-size: 12px; .rv-bubbles { text-align: justify; overflow: hidden; margin-bottom: 5px; width: 100%; } .rv-bubble { background: #d8d9dc; display: inline-block; vertical-align: bottom; } .rv-spacer { display: inline-block; font-size: 0; line-height: 0; width: 100%; } } .rv-legend-titles { height: 16px; position: relative; } .rv-legend-titles__left, .rv-legend-titles__right, .rv-legend-titles__center { position: absolute; white-space: nowrap; overflow: hidden; } .rv-legend-titles__center { display: block; text-align: center; width: 100%; } .rv-legend-titles__right { right: 0; } ================================================ FILE: packages/react-vis/src/styles/plot.scss ================================================ $rv-xy-plot-axis-font-color: #6b6b76; $rv-xy-plot-axis-line-color: #e6e6e9; $rv-xy-plot-axis-font-size: 11px; $rv-xy-plot-tooltip-background: #3a3a48; $rv-xy-plot-tooltip-color: #fff; $rv-xy-plot-tooltip-font-size: 12px; $rv-xy-plot-tooltip-border-radius: 4px; $rv-xy-plot-tooltip-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); $rv-xy-plot-tooltip-padding: 7px 10px; .rv-xy-plot { color: #c3c3c3; position: relative; canvas { pointer-events: none; } .rv-xy-canvas { pointer-events: none; position: absolute; } } .rv-xy-plot__inner { display: block; } .rv-xy-plot__axis__line { fill: none; stroke-width: 2px; stroke: $rv-xy-plot-axis-line-color; } .rv-xy-plot__axis__tick__line { stroke: $rv-xy-plot-axis-line-color; } .rv-xy-plot__axis__tick__text { fill: $rv-xy-plot-axis-font-color; font-size: $rv-xy-plot-axis-font-size; } .rv-xy-plot__axis__title { text { fill: $rv-xy-plot-axis-font-color; font-size: $rv-xy-plot-axis-font-size; } } .rv-xy-plot__grid-lines__line { stroke: $rv-xy-plot-axis-line-color; } .rv-xy-plot__circular-grid-lines__line { fill-opacity: 0; stroke: $rv-xy-plot-axis-line-color; } .rv-xy-plot__series, .rv-xy-plot__series path { pointer-events: all; } .rv-xy-plot__series--line { fill: none; stroke: #000; stroke-width: 2px; } .rv-crosshair { position: absolute; font-size: 11px; pointer-events: none; } .rv-crosshair__line { background: #47d3d9; width: 1px; } .rv-crosshair__inner { position: absolute; text-align: left; top: 0; } .rv-crosshair__inner__content { border-radius: $rv-xy-plot-tooltip-border-radius; background: $rv-xy-plot-tooltip-background; color: $rv-xy-plot-tooltip-color; font-size: $rv-xy-plot-tooltip-font-size; padding: $rv-xy-plot-tooltip-padding; box-shadow: $rv-xy-plot-tooltip-shadow; } .rv-crosshair__inner--left { right: 4px; } .rv-crosshair__inner--right { left: 4px; } .rv-crosshair__title { font-weight: bold; white-space: nowrap; } .rv-crosshair__item { white-space: nowrap; } .rv-hint { position: absolute; pointer-events: none; } .rv-hint__content { border-radius: $rv-xy-plot-tooltip-border-radius; padding: $rv-xy-plot-tooltip-padding; font-size: $rv-xy-plot-tooltip-font-size; background: $rv-xy-plot-tooltip-background; box-shadow: $rv-xy-plot-tooltip-shadow; color: $rv-xy-plot-tooltip-color; text-align: left; white-space: nowrap; } ================================================ FILE: packages/react-vis/src/styles/radial-chart.scss ================================================ .rv-radial-chart { .rv-xy-plot__series--label { pointer-events: none; } } ================================================ FILE: packages/react-vis/src/styles/treemap.scss ================================================ .rv-treemap { font-size: 12px; position: relative; } .rv-treemap__leaf { overflow: hidden; position: absolute; } .rv-treemap__leaf--circle { align-items: center; border-radius: 100%; display: flex; justify-content: center; } .rv-treemap__leaf__content { overflow: hidden; padding: 10px; text-overflow: ellipsis; } ================================================ FILE: packages/react-vis/src/sunburst/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {hierarchy, partition} from 'd3-hierarchy'; import {scaleLinear, scaleSqrt} from 'd3-scale'; import {AnimationPropType} from 'animation'; import LabelSeries from 'plot/series/label-series'; import ArcSeries from 'plot/series/arc-series'; import XYPlot from 'plot/xy-plot'; import {getRadialDomain} from 'utils/series-utils'; import {getRadialLayoutMargin} from 'utils/chart-utils'; import {getCombinedClassName} from 'utils/styling-utils'; const predefinedClassName = 'rv-sunburst'; const LISTENERS_TO_OVERWRITE = [ 'onValueMouseOver', 'onValueMouseOut', 'onValueClick', 'onValueRightClick', 'onSeriesMouseOver', 'onSeriesMouseOut', 'onSeriesClick', 'onSeriesRightClick' ]; /** * Create the list of nodes to render. * @param {Object} props props.data {Object} - tree structured data (each node has a name anc an array of children) props.height {number} - the height of the graphic to be rendered props.hideRootNode {boolean} - whether or not to hide the root node props.width {number} - the width of the graphic to be rendered props.getSize {function} - accessor for the size * @returns {Array} Array of nodes. */ function getNodesToRender({data, height, hideRootNode, width, getSize}) { const partitionFunction = partition(); const structuredInput = hierarchy(data).sum(getSize); const radius = Math.min(width, height) / 2 - 10; const x = scaleLinear().range([0, 2 * Math.PI]); const y = scaleSqrt().range([0, radius]); return partitionFunction(structuredInput) .descendants() .reduce((res, cell, index) => { if (hideRootNode && index === 0) { return res; } return res.concat([ { angle0: Math.max(0, Math.min(2 * Math.PI, x(cell.x0))), angle: Math.max(0, Math.min(2 * Math.PI, x(cell.x1))), radius0: Math.max(0, y(cell.y0)), radius: Math.max(0, y(cell.y1)), depth: cell.depth, parent: cell.parent, ...cell.data } ]); }, []); } /** * Convert arc nodes into label rows. * Important to use mappedData rather than regular data, bc it is already unrolled * @param {Array} mappedData - Array of nodes. * @param {Object} accessors - object of accessors * @returns {Array} array of node for rendering as labels */ function buildLabels(mappedData, accessors) { const {getAngle, getAngle0, getLabel, getRadius0} = accessors; return mappedData.filter(getLabel).map(row => { const truedAngle = -1 * getAngle(row) + Math.PI / 2; const truedAngle0 = -1 * getAngle0(row) + Math.PI / 2; const angle = (truedAngle0 + truedAngle) / 2; const rotateLabels = !row.dontRotateLabel; const rotAngle = (-angle / (2 * Math.PI)) * 360; return { ...row, children: null, angle: null, radius: null, x: getRadius0(row) * Math.cos(angle), y: getRadius0(row) * Math.sin(angle), style: { textAnchor: rotAngle > 90 ? 'end' : 'start', ...row.labelStyle }, rotation: rotateLabels ? rotAngle > 90 ? rotAngle + 180 : rotAngle === 90 ? 90 : rotAngle : null }; }); } const NOOP = () => {}; function Sunburst(props) { const { getAngle, getAngle0, animation, className, children, data, height, hideRootNode, getLabel, width, getSize, colorType } = props; const mappedData = getNodesToRender({ data, height, hideRootNode, width, getSize }); const radialDomain = getRadialDomain(mappedData); const margin = getRadialLayoutMargin(width, height, radialDomain); const labelData = buildLabels(mappedData, { getAngle, getAngle0, getLabel, getRadius0: d => d.radius0 }); const hofBuilder = f => (e, i) => (f ? f(mappedData[e.index], i) : NOOP); return ( ({ ...row, parent: null, children: null, index })) : mappedData, _data: animation ? mappedData : null, arcClassName: `${predefinedClassName}__series--radial__arc`, ...LISTENERS_TO_OVERWRITE.reduce((acc, propName) => { const prop = props[propName]; acc[propName] = animation ? hofBuilder(prop) : prop; return acc; }, {}) }} /> {labelData.length > 0 && ( )} {children} ); } Sunburst.displayName = 'Sunburst'; Sunburst.propTypes = { animation: AnimationPropType, getAngle: PropTypes.func, getAngle0: PropTypes.func, className: PropTypes.string, colorType: PropTypes.string, data: PropTypes.object.isRequired, height: PropTypes.number.isRequired, hideRootNode: PropTypes.bool, getLabel: PropTypes.func, onValueClick: PropTypes.func, onValueMouseOver: PropTypes.func, onValueMouseOut: PropTypes.func, getSize: PropTypes.func, width: PropTypes.number.isRequired, padAngle: PropTypes.oneOfType([PropTypes.func, PropTypes.number]) }; Sunburst.defaultProps = { getAngle: d => d.angle, getAngle0: d => d.angle0, className: '', colorType: 'literal', getColor: d => d.color, hideRootNode: false, getLabel: d => d.label, getSize: d => d.size, padAngle: 0 }; export default Sunburst; ================================================ FILE: packages/react-vis/src/theme.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. export const DISCRETE_COLOR_RANGE = [ '#12939A', '#79C7E3', '#1A3177', '#FF9833', '#EF5D28' ]; export const EXTENDED_DISCRETE_COLOR_RANGE = [ '#19CDD7', '#DDB27C', '#88572C', '#FF991F', '#F15C17', '#223F9A', '#DA70BF', '#125C77', '#4DC19C', '#776E57', '#12939A', '#17B8BE', '#F6D18A', '#B7885E', '#FFCB99', '#F89570', '#829AE3', '#E79FD5', '#1E96BE', '#89DAC1', '#B3AD9E' ]; export const CONTINUOUS_COLOR_RANGE = ['#EF5D28', '#FF9833']; export const SIZE_RANGE = [1, 10]; export const OPACITY_RANGE = [0.1, 1]; export const OPACITY_TYPE = 'literal'; export const DEFAULT_OPACITY = 1; export const DEFAULT_SIZE = 5; export const DEFAULT_COLOR = DISCRETE_COLOR_RANGE[0]; export const DEFAULT_TICK_SIZE = 7; ================================================ FILE: packages/react-vis/src/treemap/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import { hierarchy, pack, partition, treemapSquarify, treemapResquarify, treemapSlice, treemapDice, treemapSliceDice, treemapBinary, treemap } from 'd3-hierarchy'; import { CONTINUOUS_COLOR_RANGE, DEFAULT_COLOR, DEFAULT_OPACITY, OPACITY_TYPE } from 'theme'; import {AnimationPropType} from 'animation'; import {getAttributeFunctor, getMissingScaleProps} from 'utils/scales-utils'; import {MarginPropType, getInnerDimensions} from 'utils/chart-utils'; import TreemapDOM from './treemap-dom'; import TreemapSVG from './treemap-svg'; const TREEMAP_TILE_MODES = { squarify: treemapSquarify, resquarify: treemapResquarify, slice: treemapSlice, dice: treemapDice, slicedice: treemapSliceDice, binary: treemapBinary }; const TREEMAP_LAYOUT_MODES = ['circlePack', 'partition', 'partition-pivot']; const NOOP = d => d; const ATTRIBUTES = ['opacity', 'color']; const DEFAULT_MARGINS = { left: 40, right: 10, top: 10, bottom: 40 }; /** * Get the map of scale functions from the given props. * @param {Object} props Props for the component. * @returns {Object} Map of scale functions. * @private */ function _getScaleFns(props) { const {data} = props; const allData = data.children || []; // Adding _allData property to the object to reuse the existing // getAttributeFunctor function. const compatibleProps = { ...props, ...getMissingScaleProps(props, allData, ATTRIBUTES), _allData: allData }; return { opacity: getAttributeFunctor(compatibleProps, 'opacity'), color: getAttributeFunctor(compatibleProps, 'color') }; } function Treemap(props) { const scales = _getScaleFns(props); const innerDimensions = getInnerDimensions(props, props.margin); /** * Create the list of nodes to render. * @returns {Array} Array of nodes. * @private */ function _getNodesToRender() { const {innerWidth, innerHeight} = innerDimensions; const {data, mode, padding, sortFunction, getSize} = props; if (!data) { return []; } if (mode === 'partition' || mode === 'partition-pivot') { const partitionFunction = partition() .size( mode === 'partition-pivot' ? [innerHeight, innerWidth] : [innerWidth, innerHeight] ) .padding(padding); const structuredInput = hierarchy(data) .sum(getSize) .sort((a, b) => sortFunction(a, b, getSize)); const mappedNodes = partitionFunction(structuredInput).descendants(); if (mode === 'partition-pivot') { return mappedNodes.map(node => ({ ...node, x0: node.y0, x1: node.y1, y0: node.x0, y1: node.x1 })); } return mappedNodes; } if (mode === 'circlePack') { const packingFunction = pack() .size([innerWidth, innerHeight]) .padding(padding); const structuredInput = hierarchy(data) .sum(getSize) .sort((a, b) => sortFunction(a, b, getSize)); return packingFunction(structuredInput).descendants(); } const tileFn = TREEMAP_TILE_MODES[mode]; const treemapingFunction = treemap(tileFn) .tile(tileFn) .size([innerWidth, innerHeight]) .padding(padding); const structuredInput = hierarchy(data) .sum(getSize) .sort((a, b) => sortFunction(a, b, getSize)); return treemapingFunction(structuredInput).descendants(); } const {renderMode} = props; const nodes = _getNodesToRender(); const TreemapElement = renderMode === 'SVG' ? TreemapSVG : TreemapDOM; return ; } Treemap.displayName = 'Treemap'; Treemap.propTypes = { animation: AnimationPropType, className: PropTypes.string, data: PropTypes.object.isRequired, height: PropTypes.number.isRequired, hideRootNode: PropTypes.bool, margin: MarginPropType, mode: PropTypes.oneOf( Object.keys(TREEMAP_TILE_MODES).concat(TREEMAP_LAYOUT_MODES) ), onLeafClick: PropTypes.func, onLeafMouseOver: PropTypes.func, onLeafMouseOut: PropTypes.func, useCirclePacking: PropTypes.bool, padding: PropTypes.number.isRequired, sortFunction: PropTypes.func, width: PropTypes.number.isRequired, getSize: PropTypes.func, getColor: PropTypes.func }; Treemap.defaultProps = { className: '', colorRange: CONTINUOUS_COLOR_RANGE, _colorValue: DEFAULT_COLOR, data: { children: [] }, hideRootNode: false, margin: DEFAULT_MARGINS, mode: 'squarify', onLeafClick: NOOP, onLeafMouseOver: NOOP, onLeafMouseOut: NOOP, opacityType: OPACITY_TYPE, _opacityValue: DEFAULT_OPACITY, padding: 1, sortFunction: (a, b, accessor) => { if (!accessor) { return 0; } return accessor(a) - accessor(b); }, getSize: d => d.size, getColor: d => d.color, getLabel: d => d.title }; export default Treemap; ================================================ FILE: packages/react-vis/src/treemap/treemap-dom.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import TreemapLeaf from './treemap-leaf'; import {getCombinedClassName} from 'utils/styling-utils'; function TreemapDOM(props) { const { animation, className, height, hideRootNode, getLabel, mode, nodes, width, scales, style } = props; const useCirclePacking = mode === 'circlePack'; return (
{nodes.map((node, index) => { // throw out the rootest node if (hideRootNode && !index) { return null; } const nodeProps = { animation, node, getLabel, ...props, x0: useCirclePacking ? node.x : node.x0, x1: useCirclePacking ? node.x : node.x1, y0: useCirclePacking ? node.y : node.y0, y1: useCirclePacking ? node.y : node.y1, r: useCirclePacking ? node.r : 1, scales, style }; return ; })}
); } TreemapDOM.displayName = 'TreemapDOM'; export default TreemapDOM; ================================================ FILE: packages/react-vis/src/treemap/treemap-leaf.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import Animation, {AnimationPropType} from 'animation'; import {getFontColorFromBackground} from 'utils/scales-utils'; const ANIMATED_PROPS = [ 'colorRange', 'colorDomain', 'color', 'opacityRange', 'opacityDomain', 'opacity', 'x0', 'x1', 'y0', 'y1', 'r' ]; function TreemapLeaf(props) { const { animation, getLabel, mode, node, onLeafClick, onLeafMouseOver, onLeafMouseOut, r, scales, x0, x1, y0, y1, style } = props; if (animation) { return ( ); } const useCirclePacking = mode === 'circlePack'; const background = scales.color(node); const opacity = scales.opacity(node); const color = getFontColorFromBackground(background); const {data} = node; const title = getLabel(data); const leafStyle = { top: useCirclePacking ? y0 - r : y0, left: useCirclePacking ? x0 - r : x0, width: useCirclePacking ? r * 2 : x1 - x0, height: useCirclePacking ? r * 2 : y1 - y0, background, opacity, color, ...style, ...node.data.style }; return (
onLeafMouseOver(node, event)} onMouseLeave={event => onLeafMouseOut(node, event)} onClick={event => onLeafClick(node, event)} style={leafStyle} >
{title}
); } TreemapLeaf.propTypes = { animation: AnimationPropType, height: PropTypes.number.isRequired, mode: PropTypes.string, node: PropTypes.object.isRequired, onLeafClick: PropTypes.func, onLeafMouseOver: PropTypes.func, onLeafMouseOut: PropTypes.func, scales: PropTypes.object.isRequired, width: PropTypes.number.isRequired, r: PropTypes.number.isRequired, x0: PropTypes.number.isRequired, x1: PropTypes.number.isRequired, y0: PropTypes.number.isRequired, y1: PropTypes.number.isRequired }; export default TreemapLeaf; ================================================ FILE: packages/react-vis/src/treemap/treemap-svg.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import XYPlot from 'plot/xy-plot'; import PolygonSeries from 'plot/series/polygon-series'; import MarkSeries from 'plot/series/mark-series'; import LabelSeries from 'plot/series/label-series'; import {getCombinedClassName} from 'utils/styling-utils'; const MARGIN_ADJUST = 1.2; class TreemapSVG extends React.Component { getCircularNodes() { const { animation, hideRootNode, nodes, onLeafMouseOver, onLeafMouseOut, onLeafClick, scales, style } = this.props; const {rows, minY, maxY, minX, maxX} = nodes.reduce( (acc, node, index) => { if (!index && hideRootNode) { return acc; } const {x, y, r} = node; return { maxY: Math.max(y + r, acc.maxY), minY: Math.min(y - r, acc.minY), maxX: Math.max(x + MARGIN_ADJUST * r, acc.maxX), minX: Math.min(x - MARGIN_ADJUST * r, acc.minX), rows: acc.rows.concat([ { x, y, size: r, color: scales.color(node) } ]) }; }, { rows: [], maxY: -Infinity, minY: Infinity, maxX: -Infinity, minX: Infinity } ); return { updatedNodes: ( d.color} sizeType="literal" getSize={d => d.size} style={style} /> ), minY, maxY, minX, maxX }; } getNonCircularNodes() { const { animation, hideRootNode, nodes, onLeafMouseOver, onLeafMouseOut, onLeafClick, scales, style } = this.props; const {color} = scales; return nodes.reduce( (acc, node, index) => { if (!index && hideRootNode) { return acc; } const {x0, x1, y1, y0} = node; const x = x0; const y = y0; const nodeHeight = y1 - y0; const nodeWidth = x1 - x0; acc.maxY = Math.max(y + nodeHeight, acc.maxY); acc.minY = Math.min(y, acc.minY); acc.maxX = Math.max(x + nodeWidth, acc.maxX); acc.minX = Math.min(x, acc.minX); const data = [ {x, y}, {x, y: y + nodeHeight}, {x: x + nodeWidth, y: y + nodeHeight}, {x: x + nodeWidth, y} ]; acc.updatedNodes = acc.updatedNodes.concat([ ]); return acc; }, { updatedNodes: [], maxY: -Infinity, minY: Infinity, maxX: -Infinity, minX: Infinity } ); } render() { const {className, height, mode, nodes, width} = this.props; const useCirclePacking = mode === 'circlePack'; const {minY, maxY, minX, maxX, updatedNodes} = useCirclePacking ? this.getCircularNodes() : this.getNonCircularNodes(); const labels = nodes.reduce((acc, node) => { if (!node.data.title) { return acc; } return acc.concat({ ...node.data, x: node.x0 || node.x, y: node.y0 || node.y, label: `${node.data.title}` }); }, []); return ( {updatedNodes} ); } } TreemapSVG.displayName = 'TreemapSVG'; export default TreemapSVG; ================================================ FILE: packages/react-vis/src/utils/axis-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import {range} from 'd3-array'; import {scaleLinear} from 'd3-scale'; export const ORIENTATION = { TOP: 'top', LEFT: 'left', RIGHT: 'right', BOTTOM: 'bottom', VERTICAL: 'vertical', HORIZONTAL: 'horizontal' }; export const DIRECTION = { VERTICAL: 'vertical', HORIZONTAL: 'horizontal' }; /** * Get total amount of ticks from a given size in pixels. * @param {number} size Size of the axis in pixels. * @returns {number} Total amount of ticks. */ export function getTicksTotalFromSize(size) { if (size < 700) { if (size > 300) { return 10; } return 5; } return 20; } /** * Get the tick values from a given d3 scale. * @param {d3.scale} scale Scale function. * @param {number} tickTotal Total number of ticks * @param {Array} tickValues Array of tick values if they exist. * @returns {Array} Array of tick values. */ export function getTickValues(scale, tickTotal, tickValues) { return !tickValues ? scale.ticks ? scale.ticks(tickTotal) : scale.domain() : tickValues; } /** * Generate a description of a decorative axis in terms of a linear equation * y = slope * x + offset in coordinates * @param {Object} axisStart Object of format {x, y} describing in coordinates * the start position of the decorative axis * @param {Object} axisEnd Object of format {x, y} describing in coordinates * the start position of the decorative axis * @returns {Number} Object describing each the line in coordinates */ export function generateFit(axisStart, axisEnd) { // address the special case when the slope is infinite if (axisStart.x === axisEnd.x) { return { left: axisStart.y, right: axisEnd.y, slope: 0, offset: axisStart.x }; } const slope = (axisStart.y - axisEnd.y) / (axisStart.x - axisEnd.x); return { left: axisStart.x, right: axisEnd.x, // generate the linear projection of the axis direction slope, offset: axisStart.y - slope * axisStart.x }; } /** * Generate a description of a decorative axis in terms of a linear equation * y = slope * x + offset in coordinates * @param props * props.@param {Object} axisStart Object of format {x, y} describing in coordinates * the start position of the decorative axis * props.@param {Object} axisEnd Object of format {x, y} describing in coordinates * the start position of the decorative axis * props.@param {Number} numberOfTicks The number of ticks on the axis * props.@param {Array.Numbers} axisDomain The values to be interpolated across for the axis * @returns {Number} Object describing the slope and the specific coordinates of the points */ export function generatePoints({ axisStart, axisEnd, numberOfTicks, axisDomain }) { const {left, right, slope, offset} = generateFit(axisStart, axisEnd); // construct a linear band of points, then map them const pointSlope = (right - left) / numberOfTicks; const axisScale = scaleLinear() .domain([left, right]) .range(axisDomain); const slopeVertical = axisStart.x === axisEnd.x; return { slope: slopeVertical ? Infinity : slope, points: range(left, right + pointSlope, pointSlope) .map(val => { if (slopeVertical) { return {y: val, x: slope * val + offset, text: axisScale(val)}; } return {x: val, y: slope * val + offset, text: axisScale(val)}; }) .slice(0, numberOfTicks + 1) }; } /** * Compute the angle (in radians) of a decorative axis * @param {Object} axisStart Object of format {x, y} describing in coordinates * the start position of the decorative axis * @param {Object} axisEnd Object of format {x, y} describing in coordinates * the start position of the decorative axis * @returns {Number} Angle in radials */ export function getAxisAngle(axisStart, axisEnd) { if (axisStart.x === axisEnd.x) { return axisEnd.y > axisStart.y ? Math.PI / 2 : (3 * Math.PI) / 2; } return Math.atan((axisEnd.y - axisStart.y) / (axisEnd.x - axisStart.x)); } export default { DIRECTION, ORIENTATION, getTicksTotalFromSize, getTickValues }; ================================================ FILE: packages/react-vis/src/utils/chart-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import PropTypes from 'prop-types'; /** * Get the dimensions of the component for the future use. * @param {Object} props Props. * @param {Object} defaultMargins Object with default margins. * @returns {Object} Dimensions of the component. */ export function getInnerDimensions(props, defaultMargins) { const {margin, width, height} = props; const marginProps = { ...defaultMargins, ...(typeof margin === 'number' ? { left: margin, right: margin, top: margin, bottom: margin } : margin) }; const { left: marginLeft = 0, top: marginTop = 0, right: marginRight = 0, bottom: marginBottom = 0 } = marginProps; return { marginLeft, marginTop, marginRight, marginBottom, innerHeight: height - marginBottom - marginTop, innerWidth: width - marginLeft - marginRight }; } /** * Calculate the margin of the sunburst, * so it can be at the center of the container * @param {Number} width - the width of the container * @param {Number} height - the height of the container * @param {Number} radius - the max radius of the sunburst * @return {Object} an object includes {bottom, left, right, top} */ export function getRadialLayoutMargin(width, height, radius) { const marginX = width / 2 - radius; const marginY = height / 2 - radius; return { bottom: marginY, left: marginX, right: marginX, top: marginY }; } export const MarginPropType = PropTypes.oneOfType([ PropTypes.shape({ left: PropTypes.number, top: PropTypes.number, right: PropTypes.number, bottom: PropTypes.number }), PropTypes.number ]); export const DEFAULT_MARGINS = { left: 40, right: 10, top: 10, bottom: 40 }; ================================================ FILE: packages/react-vis/src/utils/data-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. /** * Get unique property values from an array. * @param {Array} arr Array of data. * @param {string} propertyName Prop name. * @returns {Array} Array of unique values. */ export function getUniquePropertyValues(arr, accessor) { const setOfValues = new Set(arr.map(accessor)); return Array.from(setOfValues); } /** * Add zero to the domain. * @param {Array} arr Add zero to the domain. * @param {Number} value Add zero to domain. * @returns {Array} Adjusted domain. */ export function addValueToArray(arr, value) { const result = [].concat(arr); if (result[0] > value) { result[0] = value; } if (result[result.length - 1] < value) { result[result.length - 1] = value; } return result; } /** * Transforms a value ( number or date ) to a string. * @param {Date | number} value The value as date or number. * @returns {string | number} The value as string. */ export function transformValueToString(value) { return Object.prototype.toString.call(value) === '[object Date]' ? value.toDateString() : value; } ================================================ FILE: packages/react-vis/src/utils/react-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; const [major, minor] = React.version.split('.'); const versionHigherThanThirteen = Number(minor) > 13 || Number(major) > 13; export const isReactDOMSupported = () => versionHigherThanThirteen; /** * Support React 0.13 and greater where refs are React components, not DOM * nodes. * @param {*} ref React's ref. * @returns {Element} DOM element. */ export const getDOMNode = ref => { if (!isReactDOMSupported()) { return ref && ref.getDOMNode(); } return ref; }; const USED_MESSAGES = {}; const HIDDEN_PROCESSES = { test: true, production: true }; /** * Warn the user about something * @param {String} message - the message to be shown * @param {Boolean} onlyShowMessageOnce - whether or not we allow the - message to be show multiple times */ export function warning(message, onlyShowMessageOnce = false) { /* eslint-disable no-undef, no-process-env */ if (global.process && HIDDEN_PROCESSES[process.env.NODE_ENV]) { return; } /* eslint-enable no-undef, no-process-env */ if (!onlyShowMessageOnce || !USED_MESSAGES[message]) { /* eslint-disable no-console */ console.warn(message); /* eslint-enable no-console */ USED_MESSAGES[message] = true; } } /** * Convience wrapper for warning * @param {String} message - the message to be shown */ export function warnOnce(message) { warning(message, true); } ================================================ FILE: packages/react-vis/src/utils/scales-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import { scaleLinear, scalePoint, scaleOrdinal, scaleLog, scaleTime, scaleUtc } from 'd3-scale'; import {extent} from 'd3-array'; import {set} from 'd3-collection'; import {hsl} from 'd3-color'; import PropTypes from 'prop-types'; import {warning} from './react-utils'; import {getUniquePropertyValues, addValueToArray} from './data-utils'; /** * Linear scale name. * @type {string} * @const */ const LINEAR_SCALE_TYPE = 'linear'; /** * Ordinal scale name. * @type {string} * @const */ const ORDINAL_SCALE_TYPE = 'ordinal'; /** * Category scale. * @type {string} * @const */ const CATEGORY_SCALE_TYPE = 'category'; /** * Literal scale. * Differs slightly from d3's identity scale in that it does not coerce value * into numbers, it simply returns exactly what you give it * @type {string} * @const */ const LITERAL_SCALE_TYPE = 'literal'; /** * Log scale name. * @type {string} * @const */ const LOG_SCALE_TYPE = 'log'; /** * Time scale name. * @type {string} * @const */ const TIME_SCALE_TYPE = 'time'; /** * Time UTC scale name. * @type {string} * @const */ const TIME_UTC_SCALE_TYPE = 'time-utc'; /** * Scale functions that are supported in the library. * @type {Object} * @const */ const SCALE_FUNCTIONS = { [LINEAR_SCALE_TYPE]: scaleLinear, [ORDINAL_SCALE_TYPE]: scalePoint, [CATEGORY_SCALE_TYPE]: scaleOrdinal, [LITERAL_SCALE_TYPE]: literalScale, [LOG_SCALE_TYPE]: scaleLog, [TIME_SCALE_TYPE]: scaleTime, [TIME_UTC_SCALE_TYPE]: scaleUtc }; /** * Attrs for which a scale can be set up at XYPlot level * @type {Array} * @const */ const XYPLOT_ATTR = ['color', 'fill', 'opacity', 'stroke']; /** * Title case a given string * @param {String} str Array of values. * @returns {String} titlecased string */ function toTitleCase(str) { return `${str[0].toUpperCase()}${str.slice(1)}`; } /** * Find the smallest distance between the values on a given scale and return * the index of the element, where the smallest distance was found. * It returns the first occurrence of i where * `scale(value[i]) - scale(value[i - 1])` is minimal * @param {Array} values Array of values. * @param {Object} scaleObject Scale object. * @returns {number} Index of an element where the smallest distance was found. * @private */ export function _getSmallestDistanceIndex(values, scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); let result = 0; if (scaleFn) { let nextValue; let currentValue = scaleFn(values[0]); let distance = Infinity; let nextDistance; for (let i = 1; i < values.length; i++) { nextValue = scaleFn(values[i]); nextDistance = Math.abs(nextValue - currentValue); if (nextDistance < distance) { distance = nextDistance; result = i; } currentValue = nextValue; } } return result; } /** * This is a workaround for issue that ordinal scale * does not have invert method implemented in d3-scale. * @param {Object} Ordinal d3-scale object. * @returns {void} * @private */ function addInvertFunctionToOrdinalScaleObject(scale) { if (scale.invert) { return; } scale.invert = function invert(value) { const [lower, upper] = scale.range(); const start = Math.min(lower, upper); const stop = Math.max(lower, upper); if (value < start + scale.padding() * scale.step()) { return scale.domain()[0]; } if (value > stop - scale.padding() * scale.step()) { return scale.domain()[scale.domain().length - 1]; } const index = Math.floor( (value - start - scale.padding() * scale.step()) / scale.step() ); return scale.domain()[index]; }; } /** * Crate a scale function from the scale object. * @param {Object} scaleObject Scale object. - scaleObject.domain {Array} - scaleObject.range {Array} - scaleObject.type {string} - scaleObject.attr {string} * @returns {*} Scale function. * @private */ export function getScaleFnFromScaleObject(scaleObject) { if (!scaleObject) { return null; } const {type, domain, range} = scaleObject; const modDomain = domain[0] === domain[1] ? domain[0] === 0 ? [-1, 0] : [-domain[0], domain[0]] : domain; if (type === LITERAL_SCALE_TYPE) { return literalScale(range[0]); } const scale = SCALE_FUNCTIONS[type]() .domain(modDomain) .range(range); if (type === ORDINAL_SCALE_TYPE) { scale.padding(0.5); addInvertFunctionToOrdinalScaleObject(scale); } return scale; } /** * Get the domain from the array of data. * @param {Array} allData All data. * @param {function} accessor - accessor for main value. * @param {function} accessor0 - accessor for the naught value. * @param {string} type Scale type. * @returns {Array} Domain. * @private */ export function getDomainByAccessor(allData, accessor, accessor0, type) { let domain; // Collect both attr and available attr0 values from the array of data. const values = allData.reduce((data, d) => { const value = accessor(d); const value0 = accessor0(d); if (_isDefined(value)) { data.push(value); } if (_isDefined(value0)) { data.push(value0); } return data; }, []); if (!values.length) { return []; } // Create proper domain depending on the type of the scale. if (type !== ORDINAL_SCALE_TYPE && type !== CATEGORY_SCALE_TYPE) { domain = extent(values); } else { domain = set(values).values(); } return domain; } /** * Create custom scale object from the value. When the scale is created from * this object, it should return the same value all time. * @param {string} attr Attribute. * @param {*} value Value. * @param {string} type - the type of scale being used * @param {function} accessor - the accessor function * @param {function} accessor0 - the accessor function for the potential naught value * @returns {Object} Custom scale object. * @private */ function _createScaleObjectForValue(attr, value, type, accessor, accessor0) { if (type === LITERAL_SCALE_TYPE) { return { type: LITERAL_SCALE_TYPE, domain: [], range: [value], distance: 0, attr, baseValue: undefined, isValue: true, accessor, accessor0 }; } if (typeof value === 'undefined') { return null; } return { type: CATEGORY_SCALE_TYPE, range: [value], domain: [], distance: 0, attr, baseValue: undefined, isValue: true, accessor, accessor0 }; } /** * Create a regular scale object for a further use from the existing parameters. * @param {Array} domain - Domain. * @param {Array} range - Range. * @param {string} type - Type. * @param {number} distance - Distance. * @param {string} attr - Attribute. * @param {number} baseValue - Base value. * @param {function} accessor - Attribute accesor * @param {function} accessor0 - Attribute accesor for potential naught value * @returns {Object} Scale object. * @private */ function _createScaleObjectForFunction({ domain, range, type, distance, attr, baseValue, accessor, accessor0 }) { return { domain, range, type, distance, attr, baseValue, isValue: false, accessor, accessor0 }; } /** * Get scale object from props. E. g. object like {xRange, xDomain, xDistance, * xType} is transformed into {range, domain, distance, type}. * @param {Object} props Props. * @param {string} attr Attribute. * @returns {*} Null or an object with the scale. * @private */ function _collectScaleObjectFromProps(props, attr) { const { [attr]: value, [`_${attr}Value`]: fallbackValue, [`${attr}Range`]: range, [`${attr}Distance`]: distance = 0, [`${attr}BaseValue`]: baseValue, [`${attr}Type`]: type = LINEAR_SCALE_TYPE, [`${attr}NoFallBack`]: noFallBack, [`get${toTitleCase(attr)}`]: accessor = d => d[attr], [`get${toTitleCase(attr)}0`]: accessor0 = d => d[`${attr}0`] } = props; let {[`${attr}Domain`]: domain} = props; // Return value-based scale if the value is assigned. if (!noFallBack && typeof value !== 'undefined') { return _createScaleObjectForValue( attr, value, props[`${attr}Type`], accessor, accessor0 ); } // Pick up the domain from the properties and create a new one if it's not // available. if (typeof baseValue !== 'undefined') { domain = addValueToArray(domain, baseValue); } // Make sure that the minimum necessary properties exist. if (!range || !domain || !domain.length) { // Try to use the fallback value if it is available. return _createScaleObjectForValue( attr, fallbackValue, props[`${attr}Type`], accessor, accessor0 ); } return _createScaleObjectForFunction({ domain, range, type, distance, attr, baseValue, accessor, accessor0 }); } /** * Compute left domain adjustment for the given values. * @param {Array} values Array of values. * @returns {number} Domain adjustment. * @private */ function _computeLeftDomainAdjustment(values) { if (values.length > 1) { return (values[1] - values[0]) / 2; } if (values.length === 1) { return values[0] - 0.5; } return 0; } /** * Compute right domain adjustment for the given values. * @param {Array} values Array of values. * @returns {number} Domain adjustment. * @private */ function _computeRightDomainAdjustment(values) { if (values.length > 1) { return (values[values.length - 1] - values[values.length - 2]) / 2; } if (values.length === 1) { return values[0] - 0.5; } return 0; } /** * Compute distance for the given values. * @param {Array} values Array of values. * @param {Array} domain Domain. * @param {number} bestDistIndex Index of a best distance found. * @param {function} scaleFn Scale function. * @returns {number} Domain adjustment. * @private */ function _computeScaleDistance(values, domain, bestDistIndex, scaleFn) { if (values.length > 1) { // Avoid zero indexes. const i = Math.max(bestDistIndex, 1); return Math.abs(scaleFn(values[i]) - scaleFn(values[i - 1])); } if (values.length === 1) { return Math.abs(scaleFn(domain[1]) - scaleFn(domain[0])); } return 0; } /** * Normilize array of values with a single value. * @param {Array} arr Array of data. * @param {Array} values Array of values. * @param {string} attr Attribute. * @param {string} type Type. * @private */ function _normalizeValues(data, values, accessor0, type) { if (type === TIME_SCALE_TYPE && values.length === 1) { const attr0 = accessor0(data[0]); return [attr0, ...values]; } return values; } /** * Get the distance, the smallest and the largest value of the domain. * @param {Array} data Array of data for the single series. * @param {Object} scaleObject Scale object. * @returns {{domain0: number, domainN: number, distance: number}} Result. * @private */ export function _getScaleDistanceAndAdjustedDomain(data, scaleObject) { const {domain, type, accessor, accessor0} = scaleObject; const uniqueValues = getUniquePropertyValues(data, accessor); // Fix time scale if a data has only one value. const values = _normalizeValues(data, uniqueValues, accessor0, type); const index = _getSmallestDistanceIndex(values, scaleObject); const adjustedDomain = [].concat(domain); adjustedDomain[0] -= _computeLeftDomainAdjustment(values); adjustedDomain[domain.length - 1] += _computeRightDomainAdjustment(values); // Fix log scale if it's too small. if (type === LOG_SCALE_TYPE && domain[0] <= 0) { adjustedDomain[0] = Math.min(domain[1] / 10, 1); } const adjustedScaleFn = getScaleFnFromScaleObject({ ...scaleObject, domain: adjustedDomain }); const distance = _computeScaleDistance( values, adjustedDomain, index, adjustedScaleFn ); return { domain0: adjustedDomain[0], domainN: adjustedDomain[adjustedDomain.length - 1], distance }; } /** * Returns true if scale adjustments are possible for a given scale. * @param {Object} props Props. * @param {Object} scaleObject Scale object. * @returns {boolean} True if scale adjustments possible. * @private */ function _isScaleAdjustmentPossible(props, scaleObject) { const {attr} = scaleObject; const {_adjustBy: adjustBy = [], _adjustWhat: adjustWhat = []} = props; // The scale cannot be adjusted if there's no attributes to adjust, no // suitable values return adjustWhat.length && adjustBy.length && adjustBy.indexOf(attr) !== -1; } /** * Adjust continuous scales (e.g. 'linear', 'log' and 'time') by adding the * space from the left and right of them and by computing the best distance. * @param {Object} props Props. * @param {Object} scaleObject Scale object. * @returns {*} Scale object with adjustments. * @private */ function _adjustContinuousScale(props, scaleObject) { const {_allData: allSeriesData, _adjustWhat: adjustWhat = []} = props; // Assign the initial values. const domainLength = scaleObject.domain.length; const {domain} = scaleObject; let scaleDomain0 = domain[0]; let scaleDomainN = domain[domainLength - 1]; let scaleDistance = scaleObject.distance; // Find the smallest left position of the domain, the largest right position // of the domain and the best distance for them. allSeriesData.forEach((data, index) => { if (adjustWhat.indexOf(index) === -1) { return; } if (data && data.length) { const {domain0, domainN, distance} = _getScaleDistanceAndAdjustedDomain( data, scaleObject ); scaleDomain0 = Math.min(scaleDomain0, domain0); scaleDomainN = Math.max(scaleDomainN, domainN); scaleDistance = Math.max(scaleDistance, distance); } }); scaleObject.domain = [scaleDomain0, ...domain.slice(1, -1), scaleDomainN]; scaleObject.distance = scaleDistance; return scaleObject; } /** * Get an adjusted scale. Suitable for 'category' and 'ordinal' scales. * @param {Object} scaleObject Scale object. * @returns {*} Scale object with adjustments. * @private */ export function _adjustCategoricalScale(scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); const {domain, range} = scaleObject; if (domain.length > 1) { scaleObject.distance = Math.abs(scaleFn(domain[1]) - scaleFn(domain[0])); } else { scaleObject.distance = Math.abs(range[1] - range[0]); } return scaleObject; } /** * Retrieve a scale object or a value from the properties passed. * @param {Object} props Object of props. * @param {string} attr Attribute. * @returns {*} Scale object, value or null. */ export function getScaleObjectFromProps(props, attr) { // Create the initial scale object. const scaleObject = _collectScaleObjectFromProps(props, attr); if (!scaleObject) { return null; } // Make sure if it's possible to add space to the scale object. If not, // return the object immediately. if (!_isScaleAdjustmentPossible(props, scaleObject)) { return scaleObject; } const {type} = scaleObject; // Depending on what type the scale is, apply different adjustments. Distances // for the ordinal and category scales are even, equal domains cannot be // adjusted. if (type === ORDINAL_SCALE_TYPE || type === CATEGORY_SCALE_TYPE) { return _adjustCategoricalScale(scaleObject); } return _adjustContinuousScale(props, scaleObject); } /** * Get d3 scale for a given prop. * @param {Object} props Props. * @param {string} attr Attribute. * @returns {function} d3 scale function. */ export function getAttributeScale(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); return getScaleFnFromScaleObject(scaleObject); } /** * Get the value of `attr` from the object. * @param {Object} d - data Object. * @param {Function} accessor - accessor function. * @returns {*} Value of the point. * @private */ function _getAttrValue(d, accessor) { return accessor(d.data ? d.data : d); } function _isDefined(value) { return typeof value !== 'undefined'; } /* * Adds a percentage of padding to a given domain * @param {Array} domain X or Y domain to pad. * @param {Number} padding Percentage of padding to add to domain * @returns {Array} Padded Domain */ function _padDomain(domain, padding) { if (!domain) { return domain; } if (isNaN(parseFloat(domain[0])) || isNaN(parseFloat(domain[1]))) { return domain; } const [min, max] = domain; const domainPadding = (max - min) * (padding * 0.01); return [min - domainPadding, max + domainPadding]; } /** * Get prop functor (either a value or a function) for a given attribute. * @param {Object} props Series props. * @param {Function} accessor - Property accessor. * @returns {*} Function or value. */ export function getAttributeFunctor(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); if (scaleObject) { const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => scaleFn(_getAttrValue(d, scaleObject.accessor)); } return null; } /** * Get the functor which extracts value form [attr]0 property. Use baseValue if * no attr0 property for a given object is defined. Fall back to domain[0] if no * base value is available. * @param {Object} props Object of props. * @param {string} attr Attribute name. * @returns {*} Function which returns value or null if no values available. */ export function getAttr0Functor(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); if (scaleObject) { const {domain} = scaleObject; const {baseValue = domain[0]} = scaleObject; const scaleFn = getScaleFnFromScaleObject(scaleObject); return d => { const value = _getAttrValue(d, scaleObject.accessor0); return scaleFn(_isDefined(value) ? value : baseValue); }; } return null; } /** * Tries to get the string|number value of the attr and falls back to * a fallback property in case if the value is a scale. * @param {Object} props Series props. * @param {string} attr Property name. * @returns {*} Function or value. */ export function getAttributeValue(props, attr) { const scaleObject = getScaleObjectFromProps(props, attr); if (scaleObject) { if (!scaleObject.isValue && props[`_${attr}Value`] === undefined) { warning( `[React-vis] Cannot use data defined ${attr} for this ` + 'series type. Using fallback value instead.' ); } return props[`_${attr}Value`] || scaleObject.range[0]; } return null; } /** * Get prop types by the attribute. * @param {string} attr Attribute. * @returns {Object} Object of xDomain, xRange, xType, xDistance and _xValue, * where x is an attribute passed to the function. */ export function getScalePropTypesByAttribute(attr) { return { [`_${attr}Value`]: PropTypes.any, [`${attr}Domain`]: PropTypes.array, [`get${toTitleCase(attr)}`]: PropTypes.func, [`get${toTitleCase(attr)}0`]: PropTypes.func, [`${attr}Range`]: PropTypes.array, [`${attr}Type`]: PropTypes.oneOf(Object.keys(SCALE_FUNCTIONS)), [`${attr}Distance`]: PropTypes.number, [`${attr}BaseValue`]: PropTypes.any }; } /** * Extract the list of scale properties from the entire props object. * @param {Object} props Props. * @param {Array} attributes Array of attributes for the given * components (for instance, `['x', 'y', 'color']`). * @returns {Object} Collected props. */ export function extractScalePropsFromProps(props, attributes) { const result = {}; Object.keys(props).forEach(key => { // this filtering is critical for extracting the correct accessors! const attr = attributes.find(a => { // width const isPlainSet = key.indexOf(a) === 0; // Ex: _data const isUnderscoreSet = key.indexOf(`_${a}`) === 0; // EX: getX const usesGet = key.indexOf(`get${toTitleCase(a)}`) === 0; return isPlainSet || isUnderscoreSet || usesGet; }); if (!attr) { return; } result[key] = props[key]; }); return result; } /** * Extract the missing scale props from the given data and return them as * an object. * @param {Object} props Props. * @param {Array} data Array of all data. * @param {Array} attributes Array of attributes for the given * components (for instance, `['x', 'y', 'color']`). * @returns {Object} Collected props. */ export function getMissingScaleProps(props, data, attributes) { const result = {}; // Make sure that the domain is set pad it if specified attributes.forEach(attr => { if (!props[`get${toTitleCase(attr)}`]) { result[`get${toTitleCase(attr)}`] = d => d[attr]; } if (!props[`get${toTitleCase(attr)}0`]) { result[`get${toTitleCase(attr)}0`] = d => d[`${attr}0`]; } if (!props[`${attr}Domain`]) { result[`${attr}Domain`] = getDomainByAccessor( data, props[`get${toTitleCase(attr)}`] || result[`get${toTitleCase(attr)}`], props[`get${toTitleCase(attr)}0`] || result[`get${toTitleCase(attr)}0`], props[`${attr}Type`] ); if (props[`${attr}Padding`]) { result[`${attr}Domain`] = _padDomain( result[`${attr}Domain`], props[`${attr}Padding`] ); } } }); return result; } /** * Return a d3 scale that returns the literal value that was given to it * @returns {function} literal scale. */ export function literalScale(defaultValue) { function scale(d) { if (d === undefined) { return defaultValue; } return d; } function response() { return scale; } scale.domain = response; scale.range = response; scale.unknown = response; scale.copy = response; return scale; } export function getFontColorFromBackground(background) { if (background) { return hsl(background).l > 0.57 ? '#222' : '#fff'; } return null; } /** * Creates fallback values for series from scales defined at XYPlot level. * @param {Object} props Props of the XYPlot object. * @param {Array} children Array of components, children of XYPlot * @returns {Array} Collected props. */ export function getXYPlotValues(props, children) { const XYPlotScales = XYPLOT_ATTR.reduce((prev, attr) => { const { [`${attr}Domain`]: domain, [`${attr}Range`]: range, [`${attr}Type`]: type } = props; if (domain && range && type) { return { ...prev, [attr]: SCALE_FUNCTIONS[type]() .domain(domain) .range(range) }; } return prev; }, {}); return children.map(child => XYPLOT_ATTR.reduce((prev, attr) => { if (child.props && child.props[attr] !== undefined) { const scaleInput = child.props[attr]; const scale = XYPlotScales[attr]; const fallbackValue = scale ? scale(scaleInput) : scaleInput; return { ...prev, [`_${attr}Value`]: fallbackValue }; } return prev; }, {}) ); } const OPTIONAL_SCALE_PROPS = ['Padding']; const OPTIONAL_SCALE_PROPS_REGS = OPTIONAL_SCALE_PROPS.map( str => new RegExp(`${str}$`, 'i') ); /** * Get the list of optional scale-related settings for XYPlot * mostly just used to find padding properties * @param {Object} props Object of props. * @returns {Object} Optional Props. * @private */ export function getOptionalScaleProps(props) { return Object.keys(props).reduce((acc, prop) => { const propIsNotOptional = OPTIONAL_SCALE_PROPS_REGS.every( reg => !prop.match(reg) ); if (propIsNotOptional) { return acc; } acc[prop] = props[prop]; return acc; }, {}); } export default { extractScalePropsFromProps, getAttributeScale, getAttributeFunctor, getAttr0Functor, getAttributeValue, getDomainByAccessor, getFontColorFromBackground, getMissingScaleProps, getOptionalScaleProps, getScaleObjectFromProps, getScalePropTypesByAttribute, getXYPlotValues, literalScale }; ================================================ FILE: packages/react-vis/src/utils/series-utils.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import AbstractSeries from 'plot/series/abstract-series'; import {DISCRETE_COLOR_RANGE, DEFAULT_OPACITY} from 'theme'; /** * Check if the component is series or not. * @param {React.Component} child Component. * @returns {boolean} True if the child is series, false otherwise. */ export function isSeriesChild(child) { const {prototype} = child.type; return prototype instanceof AbstractSeries; } /** * Get all series from the 'children' object of the component. * @param {Object} children Children. * @returns {Array} Array of children. */ export function getSeriesChildren(children) { return React.Children.toArray(children).filter( child => child && isSeriesChild(child) ); } /** * Collect the map of repetitions of the series type for all children. * @param {Array} children Array of children. * @returns {{}} Map of repetitions where sameTypeTotal is the total amount and * sameTypeIndex is always 0. */ function collectSeriesTypesInfo(children) { const result = {}; children.filter(isSeriesChild).forEach(child => { const {displayName} = child.type; const {cluster} = child.props; if (!result[displayName]) { result[displayName] = { sameTypeTotal: 0, sameTypeIndex: 0, clusters: new Set() }; } result[displayName].clusters.add(cluster); result[displayName].sameTypeTotal++; }); return result; } /** * Check series to see if it has angular data that needs to be converted * @param {Array} data - an array of objects to check * @returns {Boolean} whether or not this series contains polar configuration */ function seriesHasAngleRadius(data = []) { if (!data) { return false; } return data.some(row => row.radius && row.angle); } /** * Possibly convert polar coordinates to x/y for computing domain * @param {Array} data - an array of objects to check * @param {String} attr - the property being checked * @returns {Boolean} whether or not this series contains polar configuration */ function prepareData(data) { if (!seriesHasAngleRadius(data)) { return data; } return data.map(row => ({ ...row, x: row.radius * Math.cos(row.angle), y: row.radius * Math.sin(row.angle) })); } /** * Collect the stacked data for all children in use. If the children don't have * the data (e.g. the child is invalid series or something else), then the child * is skipped. * Each next value of attr is equal to the previous value plus the difference * between attr0 and attr. * @param {Array} children Array of children. * @param {string} attr Attribute to stack by. * @returns {Array} New array of children for the series. */ export function getStackedData(children, attr) { const areSomeSeriesStacked = children.some( series => series && series.props.stack ); // It stores the last segment position added to each bar, separated by cluster. const latestAttrPositions = {}; return children.reduce((accumulator, series) => { // Skip the children that are not series (e.g. don't have any data). if (!series) { accumulator.push(null); return accumulator; } const seriesType = series.type.displayName; const {data, cluster = 'default', stack} = series.props; const preppedData = prepareData(data, attr); if ( !attr || !preppedData || !preppedData.length || (areSomeSeriesStacked && !stack) ) { accumulator.push(preppedData); return accumulator; } const attr0 = `${attr}0`; const baseAttr = attr === 'y' ? 'x' : 'y'; accumulator.push( preppedData.map(d => { if (!latestAttrPositions[cluster]) { latestAttrPositions[cluster] = {}; } if (!latestAttrPositions[cluster][seriesType]) { latestAttrPositions[cluster][seriesType] = {}; } const prevD = latestAttrPositions[cluster][seriesType][d[baseAttr]]; // It is the first segment of a bar. if (!prevD) { latestAttrPositions[cluster][seriesType][d[baseAttr]] = { [attr0]: d[attr0], [attr]: d[attr] }; return {...d}; } // Calculate the position of the next segment in a bar. const nextD = { ...d, [attr0]: prevD[attr], [attr]: prevD[attr] + d[attr] - (d[attr0] || 0) }; latestAttrPositions[cluster][seriesType][d[baseAttr]] = { [attr0]: nextD[attr0], [attr]: nextD[attr] }; return nextD; }) ); return accumulator; }, []); } /** * Get the list of series props for a child. * @param {Array} children Array of all children. * @returns {Array} Array of series props for each child. If a child is not a * series, than it's undefined. */ export function getSeriesPropsFromChildren(children) { const result = []; const seriesTypesInfo = collectSeriesTypesInfo(children); let seriesIndex = 0; const _opacityValue = DEFAULT_OPACITY; children.forEach(child => { let props; if (isSeriesChild(child)) { const seriesTypeInfo = seriesTypesInfo[child.type.displayName]; const _colorValue = DISCRETE_COLOR_RANGE[seriesIndex % DISCRETE_COLOR_RANGE.length]; props = { ...seriesTypeInfo, seriesIndex, _colorValue, _opacityValue }; seriesTypeInfo.sameTypeIndex++; seriesIndex++; if (child.props.cluster) { props.cluster = child.props.cluster; // Using Array.from() so we can use .indexOf props.clusters = Array.from(seriesTypeInfo.clusters); props.sameTypeTotal = props.clusters.length; props.sameTypeIndex = props.clusters.indexOf(child.props.cluster); } } result.push(props); }); return result; } /** * Find the max radius value from the nodes to be rendered after they have been * transformed into an array * @param {Array} data - the tree data after it has been broken into a iterable * it is an array of objects! * @returns {number} the maximum value in coordinates for the radial variable */ export function getRadialDomain(data) { return data.reduce((res, row) => Math.max(row.radius, res), 0); } export const ANIMATED_SERIES_PROPS = [ 'xRange', 'xDomain', 'x', 'yRange', 'yDomain', 'y', 'colorRange', 'colorDomain', 'color', 'opacityRange', 'opacityDomain', 'opacity', 'strokeRange', 'strokeDomain', 'stroke', 'fillRange', 'fillDomain', 'fill', 'width', 'height', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'data', 'angleDomain', 'angleRange', 'angle', 'radiusDomain', 'radiusRange', 'radius', 'innerRadiusDomain', 'innerRadiusRange', 'innerRadius' ]; export function getStackParams(props) { const {_stackBy, valuePosAttr, cluster} = props; let {sameTypeTotal = 1, sameTypeIndex = 0} = props; // If bars are stacked, but not clustering, override `sameTypeTotal` and // `sameTypeIndex` such that bars are stacked and not staggered. if (_stackBy === valuePosAttr && !cluster) { sameTypeTotal = 1; sameTypeIndex = 0; } return {sameTypeTotal, sameTypeIndex}; } ================================================ FILE: packages/react-vis/src/utils/styling-utils.js ================================================ // Copyright (c) 2016 - 2019 Uber Technologies, Inc. // // 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. /** * Generates interpolated class names signature based on multiple class names * ignoring the falsy and non-string values * @param {...string} classNames CSS class signatures. * @returns {string} Interpolated string containing all valid class names. */ export function getCombinedClassName(...classNames) { return classNames.filter(cn => cn && typeof cn === 'string').join(' '); } ================================================ FILE: packages/react-vis/tests/.eslintrc ================================================ { "rules": { "max-len": "off", "react/jsx-key": "off" } } ================================================ FILE: packages/react-vis/tests/components/animation.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {mount} from 'enzyme'; import Animation from 'animation'; import Axis from 'plot/axis/axis'; import AxisTicks from 'plot/axis/axis-ticks'; import VerticalBarSeries from 'plot/series/vertical-bar-series'; import XYPlot from 'plot/xy-plot'; describe('Animation', () => { test('interpolates xDomain when specified', () => { const wrapper = mount( ); const renderedAnimationWrapper = wrapper.find(Animation); expect(renderedAnimationWrapper.find(AxisTicks).prop('xDomain')).toEqual([ 'Black' ]); }); }); ================================================ FILE: packages/react-vis/tests/components/arc-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import ArcSeries from 'plot/series/arc-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import ArcSeriesExample from '../../../showcase/radial-chart/arc-series-example'; testRenderWithProps(ArcSeries, GENERIC_XYPLOT_SERIES_PROPS, true); describe('ArcSeries', () => { test('Showcase Example - ArcSeriesExample', () => { const $ = mount( ); expect($.text()).toBe('UPDATE−4−2024−4−2024'); // multiplied by two to account for shadow listeners expect($.find('.rv-xy-plot__series--arc').length).toBe(4); expect($.find('.rv-xy-plot__series--arc path').length).toBe(2 * 8); }); }); ================================================ FILE: packages/react-vis/tests/components/area-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import AreaSeries from 'plot/series/area-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import AreaChartElevated from '../../../showcase/plot/area-chart-elevated'; import AreaChart from '../../../showcase/plot/area-chart'; testRenderWithProps(AreaSeries, GENERIC_XYPLOT_SERIES_PROPS, true); const AREA_PROPS = { className: 'area-chart-example', color: '#12939a', data: [ {x: 1, y: 5, y0: 6}, {x: 2, y: 20, y0: 11}, {x: 3, y: 10, y0: 9} ] }; describe('AreaSeries', () => { test('basic rendering', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series').length).toBe(1); expect($.find('path.rv-xy-plot__series').length).toBe(1); expect($.find('path.area-chart-example').length).toBe(1); $.setProps({children: }); expect($.find('.rv-xy-plot__series').length).toBe(0); expect($.find('.rv-xy-plot__series path').length).toBe(0); expect($.find('.area-chart-example').length).toBe(0); }); test('AreaSeries: Showcase Example - AreaChart', () => { const $ = mount(); expect($.find('.rv-xy-plot__series').length).toBe(1); expect($.find('path.rv-xy-plot__series').length).toBe(1); expect($.find('path.area-series-example').length).toBe(1); }); test('AreaSeries: Showcase Example - AreaChartElevated', () => { const $ = mount(); expect($.find('.rv-xy-plot__series').length).toBe(5); expect($.find('path.rv-xy-plot__series').length).toBe(3); expect($.find('path.area-elevated-series-1').length).toBe(1); expect($.find('path.area-elevated-series-2').length).toBe(1); }); }); ================================================ FILE: packages/react-vis/tests/components/axes.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import CustomAxes from '../../../showcase/axes/custom-axes'; import CustomAxis from '../../../showcase/axes/custom-axis'; import PaddedAxis from '../../../showcase/axes/padded-axis'; import CustomAxesOrientation from '../../../showcase/axes/custom-axes-orientation'; import AxisWithTurnedLabels from '../../../showcase/plot/axis-with-turned-labels'; import AxisOn0 from '../../../showcase/axes/axis-on-0'; describe('Axis', () => { test('Showcase Example - CustomAxesOrientation', () => { const $ = mount(); expect($.text()).toBe('1.01.52.02.53.03.54.0X Axis246810Y Axis'); expect($.find('.rv-xy-plot__series--line').length).toBe(2); expect($.find('line').length).toBe(26); }); test('Showcase Example - Custom axis', () => { const $ = mount(); expect($.text()).toBe('1.01.52.03.0X'); expect($.find('.rv-xy-plot__series--line').length).toBe(1); expect($.find('line').length).toBe(15); const titleStyle = $.find('.rv-xy-plot__axis__title text').prop('style'); expect(titleStyle.fontSize).toBe('16px'); }); test('Showcase Example - Even more Custom axes', () => { const $ = mount(); expect($.text()).toBe( '01345XValue is 0Value is 1Value is 2Value is 3Value is 4Value is 501491625cooldogskateboardwowsuchMultilinedogs' ); expect($.find('line').length).toBe(26); }); test('Showcase Example - AxisWithTurnedLabels', () => { const $ = mount(); expect($.text()).toBe('ApplesBananasCranberries02468101214'); expect($.find('rect').length).toBe(6); expect($.find('line').length).toBe(24); }); test('Showcase Example - Padded', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--line').length).toBe(4); expect($.find('line').length > 30).toBeTruthy(); }); test('axis crosses on 0', () => { // vertical axes const $0 = mount( ).render(); const xOfGridlines0 = $0.find('.rv-xy-plot__grid-lines line')[0].attribs.x1; const xOfAxisSetOn0 = translateToXY( $0.find('.rv-xy-plot__axis.rv-xy-plot__axis--vertical')[0].attribs .transform )[0]; expect(xOfGridlines0).toBe(xOfAxisSetOn0); const $1 = mount( ).render(); const xOfAxisNotSetOn0 = $1 .find('.rv-xy-plot__axis.rv-xy-plot__axis--vertical')[0] .attribs.transform.replace('translate(', '') .split(',')[0]; expect(xOfGridlines0 !== xOfAxisNotSetOn0).toBeTruthy(); // horizontal axes const $2 = mount( ).render(); const gridlinesY = Number( translateToXY($2.find('.rv-xy-plot__grid-lines')[0].attribs.transform)[1] ); const yOfGridlines0 = String( gridlinesY + Number($2.find('.rv-xy-plot__grid-lines__line')[0].attribs.y1) ); const yOfAxisSetOn0 = translateToXY( $2.find('.rv-xy-plot__axis.rv-xy-plot__axis--horizontal')[0].attribs .transform )[1]; expect(yOfAxisSetOn0).toBe(yOfGridlines0); const $3 = mount( ).render(); const yOfAxisNotSetOn0 = translateToXY( $3.find('.rv-xy-plot__axis.rv-xy-plot__axis--horizontal')[0].attribs .transform )[1]; expect(yOfAxisNotSetOn0 !== yOfGridlines0).toBeTruthy(); }); }); function translateToXY(translate) { // 'translate(50,100)' => ['50', '100'] const results = /translate\(\s*([^\s,)]+)[ ,]([^\s,)]+)/.exec(translate); return [results[1], results[2]]; } ================================================ FILE: packages/react-vis/tests/components/axis-tick-format.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import CustomAxisTickElement from '../../../showcase/axes/custom-axis-tick-element'; describe('Axis Format', () => { test('correctly renders return values from tickFormat', () => { const element = mount(); const ticks = element.find( '.rv-xy-plot__axis--horizontal .rv-xy-plot__axis__tick' ); expect(ticks.map(tick => tick.find('text').length)).toEqual([ 0, 0, 1, 0, 1 ]); expect( ticks .at(2) .find('text') .find('tspan').length ).toBe(1); expect( ticks .at(4) .find('text') .text() ).toBe('Label'); }); test('passes props to custom element', () => { const CustomLabel = props => { expect(Object.keys(props).sort()).toEqual([ 'containerWidth', 'dy', 'textAnchor', 'tickCount', 'transform' ]); return Custom Label; }; const element = mount(); element.setState({ data: [...element.state().data, {x: 5, y: 600, label: }] }); expect( element .find('.rv-xy-plot__axis--horizontal .rv-xy-plot__axis__tick') .at(5) .find('text') .text() ).toBe('Custom Label'); }); }); ================================================ FILE: packages/react-vis/tests/components/axis-title.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import AxisTitle from 'plot/axis/axis-title'; import {ORIENTATION} from 'utils/axis-utils'; const {LEFT, RIGHT, TOP, BOTTOM} = ORIENTATION; const baseProps = { width: 400, height: 400, title: 'Title' }; describe('AxisTitle', () => { test('horizontal bottom axis title', () => { const props = Object.assign({}, baseProps, { orientation: BOTTOM }); const $ = mount( ); const innerGroupHtml = $.find('g > g').html(); expect(innerGroupHtml.includes('text-anchor: end')).toBeTruthy(); expect($.find('text').text()).toBe(baseProps.title); }); test('horizontal top axis title', () => { const props = Object.assign({}, baseProps, { orientation: TOP, position: 'start' }); const $ = mount( ); const innerGroupHtml = $.find('g > g').html(); expect(innerGroupHtml.includes('text-anchor: start')).toBeTruthy(); expect($.find('text').text()).toBe(baseProps.title); }); test('vertical left title', () => { const props = Object.assign({}, baseProps, { orientation: LEFT }); const $ = mount( ); const innerGroupHtml = $.find('g > g').html(); expect(innerGroupHtml.includes('text-anchor: end')).toBeTruthy(); expect($.find('text').text()).toBe(baseProps.title); }); test('vertical right title', () => { const props = Object.assign({}, baseProps, { orientation: RIGHT, position: 'start' }); const $ = mount( ); const innerGroupHtml = $.find('g > g').html(); expect(innerGroupHtml.includes('text-anchor: start')).toBeTruthy(); expect($.find('text').text()).toBe(baseProps.title); }); }); ================================================ FILE: packages/react-vis/tests/components/bar-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import HorizontalBarSeries from 'plot/series/horizontal-bar-series'; import VerticalBarSeries from 'plot/series/vertical-bar-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import StackedHorizontalBarChart from '../../../showcase/plot/stacked-horizontal-bar-chart'; import StackedVerticalBarChart from '../../../showcase/plot/stacked-vertical-bar-chart'; import BarChart from '../../../showcase/plot/bar-chart'; import BigBaseBarChart from '../../../showcase/plot/big-base-bar-chart'; import ClusteredStackedVerticalBarChart from '../../../showcase/plot/clustered-stacked-bar-chart'; import DifferenceChart from '../../../showcase/plot/difference-chart'; describe('BarSeries', () => { testRenderWithProps(HorizontalBarSeries, GENERIC_XYPLOT_SERIES_PROPS, true); testRenderWithProps(VerticalBarSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('Showcase Example - BarChart', () => { const $ = mount(); expect($.text()).toBe('TOGGLE TO CANVASABC02468101214ABC'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(6); expect($.find('g.vertical-bar-series-example').length).toBe(1); $.find('.showcase-button').simulate('click'); expect($.find('rect.rv-xy-plot__series--bar').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); }); test('Showcase Example - StackedHorizontalBarChart & StackedVerticalBarChart', () => { [StackedHorizontalBarChart, StackedVerticalBarChart].forEach( (Component, i) => { const $ = mount(); const textContent = ['0510152025', '12345']; const expectedContent = `TOGGLE TO CANVAS${(i === 1 ? textContent.reverse() : textContent ).join('')}`; expect($.text()).toBe(expectedContent); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(6); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); } ); }); test('Ordinal Y-Axis HorizontalBarSeries', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(3); expect( $.find('.rv-xy-plot__series--bar rect') .at(0) .prop('height') > 0 ).toBe(true); }); test('No data', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(0); }); test('Showcase Example - ClusteredStackedVerticalBarChart', () => { const $ = mount(); expect($.text()).toBe('TOGGLE TO CANVASQ1Q2Q3Q40102030ApplesOranges'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(16); expect($.find('.rv-xy-plot__series').length).toBe(4); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); }); test('Showcase Example - BigBaseBarChart', () => { const $ = mount(); expect($.text()).toBe( 'TOGGLE TO CANVAS:38:39:40:41199,800199,900200,000200,100200,200200,300200,400' ); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(15); expect($.find('.rv-xy-plot__series').length).toBe(1); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); }); test('Showcase Example - DifferenceChart', () => { const $ = mount(); expect($.text()).toBe('TOGGLE TO CANVAS02468101214−4−20246810'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(15); expect($.find('.rv-xy-plot__series').length).toBe(1); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); }); }); ================================================ FILE: packages/react-vis/tests/components/borders.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Borders from 'plot/borders'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import GradientExample from '../../../showcase/misc/gradient-example'; testRenderWithProps( Borders, { ...GENERIC_XYPLOT_SERIES_PROPS, marginLeft: 0, marginRight: 0, innerWidth: 100, marginTop: 0, marginBottom: 0, innerHeight: 100 }, true ); describe('Borders', () => { test('GradientExample', () => { const $ = mount(); expect($.find('.rv-xy-plot__borders').length).toBe(1); expect($.find('.rv-xy-plot__borders rect').length).toBe(4); }); }); ================================================ FILE: packages/react-vis/tests/components/canvas-component.test.js ================================================ import HorizontalBarSeriesCanvas from 'plot/series/horizontal-bar-series-canvas'; import VerticalBarSeriesCanvas from 'plot/series/vertical-bar-series-canvas'; import HorizontalRectSeriesCanvas from 'plot/series/horizontal-rect-series-canvas'; import VerticalRectSeriesCanvas from 'plot/series/vertical-rect-series-canvas'; import RectSeriesCanvas from 'plot/series/rect-series-canvas'; import BarSeriesCanvas from 'plot/series/bar-series-canvas'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; testRenderWithProps(HorizontalBarSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); testRenderWithProps(VerticalBarSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); testRenderWithProps(HorizontalRectSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); testRenderWithProps(VerticalRectSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); testRenderWithProps(RectSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); testRenderWithProps(BarSeriesCanvas, GENERIC_XYPLOT_SERIES_PROPS); ================================================ FILE: packages/react-vis/tests/components/circular-grid-lines.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import CircularGridLines from 'plot/circular-grid-lines'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import FauxRadialScatterplot from '../../../showcase/plot/faux-radial-scatterplot'; describe('CircularGridLines', () => { testRenderWithProps(CircularGridLines, GENERIC_XYPLOT_SERIES_PROPS, true); test('Showcase Example - FauxRadialScatterplot', () => { const $ = mount(); expect($.text()).toBe('−3−2−10123−3−2−10123'); expect($.find('.rv-xy-plot__circular-grid-lines__line').length).toBe(7); }); }); ================================================ FILE: packages/react-vis/tests/components/color-article.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import { SensibleDefaults, ColorInXYPlot, ColorSpecificity, CategoryColorAtMarkLevel, CategoryColorAtMarkLevelCustomPalette, CategoryColorAtMarkLevelFixedStroke, GradientCharts, LinearColorAtMarkLevel, LinearColorAtMarkLevelNoPalette, LineSeriesMarkSeries, LiteralColorAtMarkLevel, CategoryColorAtSeriesLevel, LinearColorAtSeriesLevel, LiteralColorAtSeriesLevel, ReactVis5, ReactVis20, Continuous, CustomPalette } from '../../../showcase/color/mini-color-examples'; describe('Color Article Test', () => { test('generateCharts', () => { [ {name: 'SensibleDefaults', Item: SensibleDefaults}, {name: 'ColorInXYPlot', Item: ColorInXYPlot}, {name: 'LiteralColorAtSeriesLevel', Item: LiteralColorAtSeriesLevel}, {name: 'LinearColorAtSeriesLevel', Item: LinearColorAtSeriesLevel}, {name: 'CategoryColorAtSeriesLevel', Item: CategoryColorAtSeriesLevel}, {name: 'LiteralColorAtMarkLevel', Item: LiteralColorAtMarkLevel}, {name: 'CategoryColorAtMarkLevel', Item: CategoryColorAtMarkLevel}, { name: 'CategoryColorAtMarkLevelCustomPalette', Item: CategoryColorAtMarkLevelCustomPalette }, { name: 'CategoryColorAtMarkLevelFixedStroke', Item: CategoryColorAtMarkLevelFixedStroke }, { name: 'LinearColorAtMarkLevelNoPalette', Item: LinearColorAtMarkLevelNoPalette }, {name: 'LinearColorAtMarkLevel', Item: LinearColorAtMarkLevel} ].forEach(obj => { const {Item} = obj; const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(3); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(30); expect($.find('path.rv-xy-plot__series--line').length).toBe(3); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(30); }); }); test('GradientCharts', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(3); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(10); expect($.find('path.rv-xy-plot__series--line').length).toBe(1); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(10); }); test('ColorSpecificity', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(3); expect($.find('.rv-xy-plot__series--bar rect').length).toBe(10); expect($.find('path.rv-xy-plot__series--line').length).toBe(3); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(30); }); test('generatePalette', () => { [ { name: 'ReactVis5', Item: ReactVis5, expectedText: '#12939A#79C7E3#1A3177#FF9833#EF5D28', numberOfBoxes: 5 }, { name: 'ReactVis20', Item: ReactVis20, expectedText: '#19CDD7#DDB27C#88572C#FF991F#F15C17#223F9A#DA70BF#125C77#4DC19C#776E57#12939A#17B8BE#F6D18A#B7885E#FFCB99#F89570#829AE3#E79FD5#1E96BE#89DAC1#B3AD9E', numberOfBoxes: 21 }, { name: 'Continuous', Item: Continuous, expectedText: '#EF5D28#FF9833', numberOfBoxes: 2 }, { name: 'CustomPalette', Item: CustomPalette, expectedText: '#cd3b54#59b953#ba4fb9#99b53e#7f61d3#c9a83a#626dbc#e08b39#5ea0d8#cf4d2a#4fb79b#d24691#528240#c388d2#80742b#9c4a6d#caaa70#e0829f#9d5d30#dc7666', numberOfBoxes: 20 } ].forEach(obj => { const {Item, expectedText, numberOfBoxes} = obj; const $ = mount(); expect($.find('.color-box').length).toBe(numberOfBoxes); expect($.text()).toBe(expectedText); }); }); test('LineSeriesMarkSeries', () => { const $ = mount(); expect($.find('path.rv-xy-plot__series--line').length).toBe(3); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(30); }); }); ================================================ FILE: packages/react-vis/tests/components/contour-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import ContourSeries from 'plot/series/contour-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import ContourSeriesExample from '../../../showcase/plot/contour-series-example'; describe('ContourSeries', () => { testRenderWithProps(ContourSeries, GENERIC_XYPLOT_SERIES_PROPS); test('Showcase Example - ContourSeriesExample', () => { const $ = mount(); expect($.text()).toBe('4045505560657075808590951002345678UPDATE'); expect($.find('.rv-xy-plot__series--contour').length).toBe(1); expect($.find('.rv-xy-plot__series--contour-line').length).toBe(17); }); }); ================================================ FILE: packages/react-vis/tests/components/crosshair.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import DynamicCrosshair from '../../../showcase/axes/dynamic-crosshair'; describe('Crosshair', () => { test('Dynamic Crosshair - Example', () => { const $ = mount(); simulateMouseMove(100); expect($.find('.rv-crosshair').hasClass('test-class-name')).toBe(true); function simulateMouseMove(x) { $.find('.rv-xy-plot__inner').simulate('mousemove', { nativeEvent: {clientX: x, clientY: 150} }); } }); test('Dynamic Crosshair - Touch Example', () => { const $ = mount(); simulateMouseMove(100); expect($.find('.rv-crosshair').hasClass('test-class-name')).toBe(true); function simulateMouseMove(x) { $.find('.rv-xy-plot__inner').simulate('touchmove', { nativeEvent: {type: 'touchmove', touches: [{pageX: x, pageY: 150}]} }); } }); }); ================================================ FILE: packages/react-vis/tests/components/custom-svg-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import CustomSVGSeries from 'plot/series/custom-svg-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import CustomSVGExample from '../../../showcase/plot/custom-svg-example'; import CustomSVGAllTheMarks from '../../../showcase/plot/custom-svg-all-the-marks'; import CustomSVGRootLevelComponent from '../../../showcase/plot/custom-svg-root-level'; describe('CustomSVGSeries', () => { testRenderWithProps(CustomSVGSeries, GENERIC_XYPLOT_SERIES_PROPS); test('Showcase Example - CustomSVGExample', () => { const $ = mount(); expect($.text()).toBe('1.01.52.02.53.068101214x: 187.5y: 200'); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(5); expect($.find('.rv-xy-plot__series--custom-svg polygon').length).toBe(0); expect($.find('.rv-xy-plot__series--custom-svg circle').length).toBe(2); expect($.find('.rv-xy-plot__series--custom-svg rect').length).toBe(3); }); test('Showcase Example - CustomSVGRootLevelComponent', () => { const $ = mount(); expect($.text()).toBe( '1.01.52.02.53.068101214x: 0y: 125x: 87.5y: 75.00000000000001x: 125y: 250x: 250y: 0x: 187.5y: 200' ); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(5); expect($.find('.rv-xy-plot__series--custom-svg polygon').length).toBe(0); expect($.find('.rv-xy-plot__series--custom-svg circle').length).toBe(5); expect($.find('.rv-xy-plot__series--custom-svg rect').length).toBe(0); expect($.find('.rv-xy-plot__series--custom-svg text').length).toBe(5); }); test('Showcase Example - CustomSVGAllTheMarks', () => { const textContent = 'REVERSE0123402468101214'; const hoverText = 'star'; const $ = mount(); expect($.text()).toBe(textContent); $.find('.rv-xy-plot__series--custom-svg') .at(0) .simulate('mouseEnter'); expect($.text()).toBe(`${textContent}${hoverText}`); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(20); expect($.find('.rv-xy-plot__series--custom-svg polygon').length).toBe(10); expect($.find('.rv-xy-plot__series--custom-svg circle').length).toBe(5); expect($.find('.rv-xy-plot__series--custom-svg rect').length).toBe(5); }); }); ================================================ FILE: packages/react-vis/tests/components/data-article.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import {MiniCharts} from '../../../showcase/data/mini-data-examples'; describe('Scales and data examples', () => { test('MiniCharts', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(3); }); }); ================================================ FILE: packages/react-vis/tests/components/decorative-axis.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import DecorativeAxis from 'plot/axis/decorative-axis'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import DecorativeAxisCrissCross from '../../../showcase/axes/decorative-axes-criss-cross'; import ParallelCoordinatesExample from '../../../showcase/axes/parallel-coordinates-example'; testRenderWithProps( DecorativeAxis, { ...GENERIC_XYPLOT_SERIES_PROPS, axisStart: {x: 0, y: 1}, axisEnd: {x: 0, y: 1}, axisDomain: [0, 1] }, true ); describe('DecorativeAxis', () => { test('Showcase Example - DecorativeAxisCrissCross', () => { const $ = mount(); expect($.text()).toBe( '−101.01223344556677889100¡1000!¡990!¡980!¡970!¡960!¡950!¡940!¡930!¡920!¡910!¡900!' ); expect($.find('.rv-xy-manipulable-axis').length).toBe(2); expect($.find('.rv-xy-plot__axis__tick__line').length).toBe(22); expect($.find('.rv-xy-plot__axis__tick__text').length).toBe(22); }); test('Showcase Example - ParallelCoordinatesExample', () => { const $ = mount(); expect($.text()).toBe( '0.04.79.314192328333742473.03.54.04.55.05.56.06.57.07.58.0681101501802202603003403804204600.023466992120140160180210230160020002300270030003400370041004400480051008.09.71113151618202123257071727475767778808182' ); expect($.find('.rv-xy-manipulable-axis').length).toBe(7); expect($.find('.rv-xy-plot__axis__tick__line').length).toBe(77); expect($.find('.rv-xy-plot__axis__tick__text').length).toBe(77); }); }); ================================================ FILE: packages/react-vis/tests/components/gradient.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import GradientDefs from 'plot/gradient-defs'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import TriangleExample from '../../../showcase/misc/triangle-example'; import GradientExample from '../../../showcase/misc/gradient-example'; describe('GradientDefs', () => { testRenderWithProps(GradientDefs, GENERIC_XYPLOT_SERIES_PROPS, true); test('TriangleExample', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--polygon').length).toBe(121); expect($.find('.rv-gradient-defs').length).toBe(1); expect($.find('#grad1').length).toBe(1); }); test('GradientExample', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--line').length).toBe(2); expect($.find('.rv-gradient-defs').length).toBe(1); expect($.find('#CoolGradient').length).toBe(1); }); }); ================================================ FILE: packages/react-vis/tests/components/grid-lines.test.js ================================================ import React from 'react'; import {shallow} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import LineSeries from 'plot/series/line-series'; import HorizontalGridLines from 'plot/horizontal-grid-lines'; import VerticalGridLines from 'plot/vertical-grid-lines'; describe('GridLines', () => { test('HorizontalGridLines', () => { const wrapper = shallow( ); expect( wrapper .find(HorizontalGridLines) .at(0) .hasClass('test-class-name') ).toBe(true); }); test('VerticalGridLines', () => { const wrapper = shallow( ); expect( wrapper .find(VerticalGridLines) .at(0) .hasClass('test-class-name') ).toBe(true); }); }); ================================================ FILE: packages/react-vis/tests/components/heatmap.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import HeatmapSeries from 'plot/series/heatmap-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import HeatmapChart from '../../../showcase/plot/heatmap-chart'; import LabeledHeatmap from '../../../showcase/plot/labeled-heatmap'; const HEATMAP_PROPS = { className: 'heatmap-series-example', data: [ {x: 1, y: 0, color: 10}, {x: 1, y: 5, color: 10}, {x: 1, y: 10, color: 6}, {x: 1, y: 15, color: 7}, {x: 2, y: 0, color: 12}, {x: 2, y: 5, color: 2}, {x: 2, y: 10, color: 1}, {x: 2, y: 15, color: 12}, {x: 3, y: 0, color: 9}, {x: 3, y: 5, color: 2}, {x: 3, y: 10, color: 6}, {x: 3, y: 15, color: 12} ] }; describe('Heatmap', () => { testRenderWithProps(HeatmapSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('basic rendering', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series--heatmap').length).toBe(1); expect($.find('.rv-xy-plot__series--heatmap rect').length).toBe(12); expect($.find('g.heatmap-series-example').length).toBe(1); $.setProps({ children: }); expect($.find('.rv-xy-plot__series--heatmap').length).toBe(0); expect($.find('.rv-xy-plot__series--heatmap rect').length).toBe(0); expect($.find('.heatmap-series-example').length).toBe(0); }); test('Showcase Example - HeatmapChart', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--heatmap').length).toBe(1); expect($.find('.rv-xy-plot__series--heatmap rect').length).toBe(12); expect($.find('g.heatmap-series-example').length).toBe(1); expect($.text()).toBe('0.51.01.52.02.53.03.5051015'); }); test('Showcase Example - LabeledHeatmap', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--heatmap').length).toBe(1); expect($.find('.rv-xy-plot__series--label').length).toBe(1); expect($.find('.rv-xy-plot__series--heatmap rect').length).toBe(100); expect($.find('g.heatmap-series-example').length).toBe(1); expect($.text()).toBe( 'A1B1C1D1E1F1G1H1I1J1J2I2H2G2F2E2D2C2B2A20123456789111111111122222122233333331313444444444155555555556666666666777777777788888888889999999999' ); }); }); ================================================ FILE: packages/react-vis/tests/components/hexbin-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import HexbinSeries from 'plot/series/hexbin-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import HexHeatmap from '../../../showcase/plot/hex-heatmap'; import HexbinSizeExample from '../../../showcase/plot/hexbin-size-example'; describe('HexbinSeries', () => { testRenderWithProps(HexbinSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('Showcase Example - HexHeatmap', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--hexbin').length).toBe(1); expect($.find('.rv-xy-plot__series--hexbin path').length).toBe(53); expect($.find('g.hexbin-example').length).toBe(1); expect($.text()).toBe( '4050607080901002345678UPDATE DATAUPDATE RADIUSUPDATE OFFSET' ); $.find('.rv-xy-plot__series--hexbin path') .at(2) .simulate('mouseOver'); expect($.text()).toBe( '4050607080901002345678x: 138.56406460551017y: 180value: 1UPDATE DATAUPDATE RADIUSUPDATE OFFSET' ); }); test('Showcase Example - HexbinSizeExample', () => { const $ = mount(); expect($.find('g.alt-x-label').length).toBe(1); expect($.find('g.alt-y-label').length).toBe(1); [ { numHexes: 56, text: 'PREV X X AXIS economy (mpg) NEXT XPREV Y Y AXIS power (hp) NEXT Y051015202530354045050100150200economy (mpg)power (hp)', buttonToPress: null }, // click next x { numHexes: 56, text: 'PREV X X AXIS cylinders NEXT XPREV Y Y AXIS power (hp) NEXT Y3.03.54.04.55.05.56.06.57.07.58.0050100150200cylinderspower (hp)', buttonToPress: 1 }, // click next y { numHexes: 20, text: 'PREV X X AXIS cylinders NEXT XPREV Y Y AXIS weight (lb) NEXT Y3.03.54.04.55.05.56.06.57.07.58.02,0002,5003,0003,5004,0004,5005,000cylindersweight (lb)', buttonToPress: 3 }, // click prev y { numHexes: 20, text: 'PREV X X AXIS cylinders NEXT XPREV Y Y AXIS weight (lb) NEXT Y3.03.54.04.55.05.56.06.57.07.58.02,0002,5003,0003,5004,0004,5005,000cylindersweight (lb)', buttonToPress: 0 } ].forEach(({numHexes, text, buttonToPress}) => { if (buttonToPress) { $.find('.showcase-button') .at(buttonToPress) .simulate('click'); } expect($.find('.rv-xy-plot__series--hexbin').length).toBe(1); expect($.find('.rv-xy-plot__series--hexbin path').length).toBe(numHexes); expect($.find('g.hexbin-size-example').length).toBe(1); expect($.text()).toBe(text); }); }); }); ================================================ FILE: packages/react-vis/tests/components/highlight.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Highlight from 'plot/highlight'; import DragableExample from '../../../showcase/misc/dragable-chart-example'; import ZoomableChartExample from '../../../showcase/misc/zoomable-chart-example'; import SelectionPlotExample from '../../../showcase/misc/selection-plot-example'; import BidirectionDragChart from '../../../showcase/misc/2d-dragable-plot'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; describe('Highlight', () => { testRenderWithProps( Highlight, { ...GENERIC_XYPLOT_SERIES_PROPS, marginLeft: 0, marginRight: 0, innerWidth: 100, marginTop: 0, marginBottom: 0, innerHeight: 100 }, true ); test('DragableExample', () => { const $ = mount(); const initialText = '0.00.51.01.52.02.53.03.54.04.55.05.56.06.57.00246810selectionStart: 0,selectionEnd: 0,'; expect($.text()).toBe(initialText); $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 100, offsetY: 100} }); for (let i = 0; i < 100; i++) { $.find('.rv-mouse-target').simulate('mouseMove', { nativeEvent: {offsetX: 100 + i, offsetY: 100} }); } expect($.text()).toBe(initialText); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 200, offsetY: 100} }); expect($.text()).toBe( '0.00.51.01.52.02.53.03.54.04.55.05.56.06.57.00246810selectionStart: 0.93,selectionEnd: 2.47,' ); // click to clear $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 0, offsetY: 0} }); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 0, offsetY: 0} }); expect($.text()).toBe(initialText); }); test('ZoomableChartExample', () => { const $ = mount(); const initialText = '−5051015200102030405060708090Reset ZoomLast Draw AreaN/A'; expect($.text()).toBe(initialText); // brush in a drag area $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 100, offsetY: 100} }); for (let i = 0; i < 100; i++) { $.find('.rv-mouse-target').simulate('mouseMove', { nativeEvent: {offsetX: 100 + i, offsetY: 100 + i} }); } expect($.text()).toBe(initialText); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 200, offsetY: 200} }); expect($.text()).toBe( '−5051015200102030405060708090Reset ZoomLast Draw AreaTop: 11.083578425950623Right: 34.98Bottom: -0.5863163548405383Left: 13.2' ); }); test('SelectionPlotExample', () => { const $ = mount(); const initialText = '0.51.01.52.02.5681012There are 0 selected points'; expect($.find('g.selection-example').length).toBe(1); expect($.text()).toBe(initialText); $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 100, offsetY: 100} }); for (let i = 0; i < 100; i++) { $.find('.rv-mouse-target').simulate('mouseMove', { nativeEvent: {offsetX: 100, offsetY: 100 + i} }); } expect($.text()).toBe('0.51.01.52.02.5681012There are 4 selected points'); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 100, offsetY: 200} }); expect($.text()).toBe('0.51.01.52.02.5681012There are 4 selected points'); // click to clear $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 0, offsetY: 0} }); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 0, offsetY: 0} }); expect($.text()).toBe(initialText); }); test('BidirectionDragChart', () => { const $ = mount(); // brush in a drag area expect($.text()).toBe('12342468There are 0 selected points'); $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 100, offsetY: 100} }); for (let i = 0; i < 100; i++) { $.find('.rv-mouse-target').simulate('mouseMove', { nativeEvent: {offsetX: 100 + i, offsetY: 100 + i} }); } expect($.text()).toBe('12342468There are 5 selected points'); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 200, offsetY: 200} }); expect($.text()).toBe('12342468There are 5 selected points'); // execute dragging $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 150, offsetY: 150} }); for (let i = 0; i < 50; i++) { $.find('.rv-mouse-target').simulate('mouseMove', { nativeEvent: {offsetX: 150 + i, offsetY: 150 + i} }); } expect($.text()).toBe('12342468There are 6 selected points'); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 200, offsetY: 200} }); expect($.text()).toBe('12342468There are 6 selected points'); // click to clear $.find('.rv-mouse-target').simulate('mouseDown', { nativeEvent: {offsetX: 75, offsetY: 75} }); $.find('.rv-mouse-target').simulate('mouseUp', { nativeEvent: {offsetX: 75, offsetY: 75} }); expect($.text()).toBe('12342468There are 0 selected points'); }); }); ================================================ FILE: packages/react-vis/tests/components/hints.test.js ================================================ import React from 'react'; import {shallow} from 'enzyme'; import Hint from 'plot/hint'; describe('Hint', () => { test('Appends user-input class name to the class signatures list', () => { const wrapper = shallow( ); expect(wrapper.hasClass('test-class-name')).toBe(true); }); test('Assigns only default class names when no custom class specified', () => { const wrapper = shallow(); expect(wrapper.hasClass('undefined')).toBe(false); }); }); ================================================ FILE: packages/react-vis/tests/components/interaction-article.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import { LinkedCharts, LineChartMouseOverXY, LineChartMouseOverSeries, ScatterPlotOnNearestXY } from '../../../showcase/interaction/interaction-examples'; describe('Interaction examples', () => { test('LinkedCharts', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(3); }); test('LineChartMouseOverXY', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(1); }); test('LineChartMouseOverSeries', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(1); }); test('ScatterPlotOnNearestXY', () => { const $ = mount(); expect($.find('.rv-xy-plot').length).toBe(1); }); }); ================================================ FILE: packages/react-vis/tests/components/label-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import LabelSeries from 'plot/series/label-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import LabelSeriesExample from '../../../showcase/misc/label-series-example'; import LabeledStackedVerticalBarChart from '../../../showcase/plot/labeled-stacked-vertical-bar-chart'; testRenderWithProps(LabelSeries, GENERIC_XYPLOT_SERIES_PROPS, true); describe('LabelSeries', () => { test('Showcase Example - LabelSeriesExample', () => { const $ = mount( ); expect($.text()).toBe( 'UPDATE−101234505101520WigglytuffPsyduckGeodudeDittoSnorlax' ); expect($.find('.rv-xy-plot__series--label text').length).toBe(5); $.find('.showcase-button').simulate('click'); expect($.text()).toBe( 'UPDATE−101234505101520WigglytuffPsyduckGeoduderedblue' ); $.find('.showcase-button').simulate('click'); expect($.text()).toBe( 'UPDATE−101234505101520WigglytuffPsyduckGeoduderedblue' ); }); test('Showcase Example - LabeledStackedVerticalBarChart', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series--label text').length).toBe(9); }); }); ================================================ FILE: packages/react-vis/tests/components/legends.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import ContinuousSizeLegend from '../../../showcase/legends/continuous-size'; import ContinuousColorLegend from '../../../showcase/legends/continuous-color'; import HorizontalDiscreteLegend from '../../../showcase/legends/horizontal-discrete-color'; import VerticalDiscreteLegend from '../../../showcase/legends/vertical-discrete-color'; import SearchableDiscreteLegend from '../../../showcase/legends/searchable-discrete-color'; import SearchableDiscreteColorLegendHoverExample from '../../../showcase/legends/searchable-discrete-color-hover'; import HorizontalDiscreteCustomPalette from '../../../showcase/legends/horizontal-discrete-custom-palette'; import ClusteredStackedVerticalBarChart from '../../../showcase/plot/clustered-stacked-bar-chart'; describe('Legends', () => { test('Discrete Legend has no clickable className while onItemClick is not passing', () => { const withOnClick$ = mount(); const withoutOnClick$ = mount(); expect( withOnClick$ .find('.rv-discrete-color-legend-item.vertical') .first() .props().onClick ).toBe(null); expect( withoutOnClick$ .find('.rv-discrete-color-legend-item.vertical.clickable') .first() .props().onClick ).not.toBe(Function); }); test('Continuous Size Legend', () => { const $ = mount(); expect($.text()).toBe(' 100200'); expect($.find('.rv-bubble').length).toBe(10); }); test('Continuous Color Legend', () => { const $ = mount(); expect($.text()).toBe('100200150'); const expectedStyle = { background: 'linear-gradient(to right, #EF5D28,#FF9833)' }; expect($.find('.rv-gradient').props().style).toEqual(expectedStyle); }); test('Discrete Legends', () => { const verticalLegend = mount(); expect(verticalLegend.text()).toBe( 'OptionsButtonsSelect boxesDate inputsPassword inputsFormsOther' ); expect( verticalLegend.find('.rv-discrete-color-legend-item__color').length ).toBe(7); expect( verticalLegend .find('.rv-discrete-color-legend-item__color__path') .first() .props().style ).toEqual({stroke: '#12939A'}); expect( mount() .find('.rv-discrete-color-legend-item__color__path') .first() .props().style ).toEqual({stroke: '#6588cd'}); const $ = mount(); expect($.text()).toBe( 'ApplesBananasBlueberriesCarrotsEggplantsLimesPotatoes' ); expect($.find('.rv-discrete-color-legend-item__color').length).toBe(7); expect( mount() .find('.rv-discrete-color-legend-item__color__path') .first() .props().style ).toEqual({strokeDasharray: '6, 2', stroke: '#45aeb1'}); expect( mount() .find('.rv-discrete-color-legend-item__color__path') .at(4) .props().style ).toEqual({strokeWidth: 13, stroke: 'url(#stripes)'}); $.find('.rv-search-wrapper__form__input').simulate('change', { target: {value: 'egg'} }); expect($.text()).toBe('Eggplants'); const itemsFound = $.find('.rv-discrete-color-legend-item__color').length; expect(itemsFound).toBe(1); expect($.find('.disabled').length).toBe(0); $.find('.clickable').simulate('click'); expect($.find('.disabled').length).toBe(1); expect( mount() .find('.rv-discrete-color-legend') .first() .props().style ).toEqual({ width: undefined, height: undefined, position: 'absolute', left: '50px', top: '10px' }); }); test('Discrete Legends Showcase: HorizontalDiscreteCustomPalette', () => { const $ = mount(); const colors = $.find('.rv-discrete-color-legend-item__color__path') .map(colorBrick => { return colorBrick.props().style.stroke; }) .join(' '); expect(colors).toBe( '#6588cd #66b046 #a361c7 #ad953f #c75a87 #55a47b #cb6141' ); expect($.text()).toBe( 'OptionsButtonsSelect boxesDate inputsPassword inputsFormsOther' ); $.find('.rv-discrete-color-legend-item') .first() .simulate('mouseEnter'); expect($.text()).toBe( 'OptionsSELECTEDButtonsSelect boxesDate inputsPassword inputsFormsOther' ); }); test('Discrete Legends Showcase: SearchableDiscreteLegendHover', () => { const $ = mount(); $.find('.rv-discrete-color-legend-item') .first() .simulate('mouseEnter'); expect($.text()).toBe( 'Apples:SELECTEDBananasBlueberriesCarrotsEggplantsLimesPotatoes' ); }); }); ================================================ FILE: packages/react-vis/tests/components/line-series-canvas.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import LineSeriesCanvas from 'plot/series/line-series-canvas'; describe('LineSeriesCanvas', () => { test('should be rendered', () => { const k = 7; const $ = mount( {Array(k) .fill(0) .map((_, index) => ( ))} ); expect( $.find('CanvasWrapper') .children() .find('LineSeriesCanvas').length ).toBe(k); }); test('on onNearestXY should be called and retur ncorrect values', () => { const k = 7; expect.assertions(k); const $ = mount( {[...Array(k).keys()].map(v => ( { expect({x: v, y: v * v}).toEqual(value); }} /> ))} ); $.find('.rv-xy-plot__inner').simulate('mousemove', { nativeEvent: {clientX: 150, clientY: 150} }); }); }); ================================================ FILE: packages/react-vis/tests/components/line-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import XYPlot from 'plot/xy-plot'; import LineSeries from 'plot/series/line-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import LineChart from '../../../showcase/plot/line-chart'; import LineMarkSeries from '../../../showcase/plot/linemark-chart'; import LineChartManyColors from '../../../showcase/color/line-chart-many-colors'; import NullData from '../../../showcase/misc/null-data-example'; import TimeChart from '../../../showcase/misc/time-chart'; import SyncedCharts from '../../../showcase/misc/synced-charts'; testRenderWithProps(LineSeries, GENERIC_XYPLOT_SERIES_PROPS, true); const LINE_PROPS = { className: 'line-chart-example', color: '#12939a', data: [ {x: 1, y: 5, y0: 6}, {x: 2, y: 20, y0: 11}, {x: 3, y: 10, y0: 9} ] }; const LINE_WITH_MANY_COLORS_COLORS = [ 'rgb(255, 255, 0)', 'rgb(255, 245, 0)', 'rgb(255, 235, 0)', 'rgb(255, 225, 0)', 'rgb(255, 215, 0)', 'rgb(255, 205, 0)', 'rgb(255, 195, 0)', 'rgb(255, 185, 0)', 'rgb(255, 175, 0)', 'rgb(255, 165, 0)', 'rgb(255, 155, 0)', 'rgb(255, 145, 0)', 'rgb(255, 135, 0)', 'rgb(255, 125, 0)', 'rgb(255, 115, 0)', 'rgb(255, 105, 0)', 'rgb(255, 95, 0)', 'rgb(255, 85, 0)', 'rgb(255, 75, 0)', 'rgb(255, 65, 0)' ]; describe('LineSeries', () => { test('basic rendering', () => { const $ = mount( ); expect($.find('.rv-xy-plot__series').length).toBe(1); expect($.find('path.rv-xy-plot__series').length).toBe(1); expect($.find('path.line-chart-example').length).toBe(1); $.setProps({children: }); expect($.find('.rv-xy-plot__series').length).toBe(0); expect($.find('.rv-xy-plot__series path').length).toBe(0); expect($.find('.line-chart-example').length).toBe(0); }); test('Showcase Example - LineChart', () => { const $ = mount(); expect($.find('g.alt-x-label').length).toBe(1); expect($.find('g.alt-y-label').length).toBe(1); expect($.text()).toBe( 'TOGGLE TO CANVAS1.01.52.02.53.03.54.02468101214X AxisY Axis' ); expect($.find('.rv-xy-plot__series--line').length).toBe(3); ['first-series', 'third-series', 'fourth-series'].forEach( customClassName => { expect( $.find(`.rv-xy-plot__series--line.${customClassName}`).length ).toBe(1); } ); expect($.find('path.second-series').length).toBe(0); $.find('button').simulate('click'); expect($.find('.rv-xy-plot__series--line').length).toBe(0); $.find('button').simulate('click'); expect($.find('.rv-xy-plot__series--line').length).toBe(3); }); test('Showcase Example - LineMarkSeries', () => { const $ = mount(); expect($.text()).toBe('1.01.52.02.53.0510152025'); expect($.find('.rv-xy-plot__series--linemark').length).toBe(2); expect($.find('.rv-xy-plot__series circle').length).toBe(6); ['linemark-series-example', 'linemark-series-example-2'].forEach( customClassName => { expect($.find(`MarkSeries.${customClassName}`).length).toBe(1); expect($.find(`LineSeries.${customClassName}`).length).toBe(1); } ); }); test('Showcase Example - LineChartManyColors', () => { const $ = mount(); const lines = $.find('.rv-xy-plot__series'); expect(lines.length).toBe(20); lines.forEach((node, i) => expect(node.props().style.stroke).toBe(LINE_WITH_MANY_COLORS_COLORS[i]) ); }); test('Showcase Example - TimeChart', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--line').length).toBe(2); expect($.text()).toBe( 'Sep 1012 PMMon 1112 PMTue 1212 PMWed 13X Axis2468101214Y Axis' ); }); test('Showcase Example - SyncedCharts', () => { const $ = mount(); const tests = () => { expect($.find('.rv-xy-plot').length).toBe(2); expect($.find('.rv-xy-plot__series--line').length).toBe(4); expect($.text()).toBe('1.01.52.02.53.024681012141.01.52.02.53.0246810'); }; tests(); $.find('.rv-xy-plot__series--line') .at(0) .simulate('mouseEnter'); tests(); $.find('.rv-xy-plot') .at(0) .simulate('mouseLeave'); }); test('Line Styling', () => { const $ = mount( ); const lineStyle = $.find('path.rv-xy-plot__series').prop('style'); expect(lineStyle.opacity).toBe(0.5); expect(lineStyle.strokeWidth).toBe('3px'); expect(lineStyle.stroke).toBe('rgb(255, 255, 255)'); expect(lineStyle.strokeDasharray).toBe('3, 1'); }); test('getNull prop: Showcase Example - Null Data Example', () => { const $ = mount(); expect($.find('path.rv-xy-plot__series').length).toBe(2); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(3); simulateMouseMove(35); expect($.find('.rv-crosshair__title').text()).toBe('x: 1'); expect( $.find('.rv-crosshair__item') .at(0) .text() ).toBe('0: 10'); expect( $.find('.rv-crosshair__item') .at(1) .text() ).toBe('1: 30'); $.find('.rv-xy-plot__inner').simulate('mouseleave'); expect($.find('.rv-crosshair').exists()).toBe(false); simulateMouseMove(100); expect($.find('.rv-crosshair__title').text()).toBe('x: 2'); expect( $.find('.rv-crosshair__item') .at(0) .text() ).toBe('0: 10'); expect( $.find('.rv-crosshair__item') .at(1) .text() ).toBe('1: 0'); simulateMouseMove(165); expect($.find('.rv-crosshair__title').text()).toBe('x: 3'); expect( $.find('.rv-crosshair__item') .at(0) .text() ).toBe('0: 13'); expect( $.find('.rv-crosshair__item') .at(1) .exists() ).toBe(false); simulateMouseMove(230); expect($.find('.rv-crosshair__title').text()).toBe('x: 4'); expect( $.find('.rv-crosshair__item') .at(0) .text() ).toBe('0: 7'); expect( $.find('.rv-crosshair__item') .at(1) .text() ).toBe('1: 15'); simulateMouseMove(295); expect($.find('.rv-crosshair').exists()).toBe(false); function simulateMouseMove(x) { $.find('.rv-xy-plot__inner').simulate('mousemove', { nativeEvent: {clientX: x, clientY: 150} }); } }); }); ================================================ FILE: packages/react-vis/tests/components/make-vis-flexible.test.js ================================================ import {makeWidthFlexible} from 'make-vis-flexible'; describe('makeWidthFlexible', () => { test('displayName given', () => { function ChildComponent() {} ChildComponent.displayName = 'ChildComponentWithDisplayName'; const FlexibleComponent = makeWidthFlexible(ChildComponent); expect(FlexibleComponent.displayName).toBe( 'FlexibleChildComponentWithDisplayName' ); }); test('displayName not given', () => { function ChildComponent() {} const FlexibleComponent = makeWidthFlexible(ChildComponent); expect(FlexibleComponent.displayName).toBe('FlexibleChildComponent'); }); }); ================================================ FILE: packages/react-vis/tests/components/mark-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import MarkSeries from 'plot/series/mark-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import Scatterplot from '../../../showcase/plot/scatterplot'; import DynamicCrosshairScatterplot from '../../../showcase/axes/dynamic-crosshair-scatterplot'; describe('MarkSeries', () => { testRenderWithProps(MarkSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('MShowcase Example - Scatterplot', () => { const $ = mount(); expect($.text()).toBe('1.01.52.02.53.068101214'); expect($.find('.rv-xy-plot__series--mark circle').length).toBe(5); expect($.find('g.mark-series-example').length).toBe(1); }); test('MShowcase Example - Dynamic Crosshair Scatterplot', () => { const $ = mount(); // NOTE: Point 0 (P0) and Point 1 (P1) are vertically aligned const yDistanceBetweenP0andP1 = 2.5; const chatTopPadding = 10; const highlightedCircles1 = $.find( '.rv-xy-plot__series--mark circle' ).reduce(highlightedCircle, []); expect(highlightedCircles1.length).toBe(0); updateCursor(0, yDistanceBetweenP0andP1 / 2 - 0.01); const highlightedCircles2 = $.find( '.rv-xy-plot__series--mark circle' ).reduce(highlightedCircle, []); expect(highlightedCircles2.length).toBe(1); expect(highlightedCircles2[0]).toEqual({cx: 0, cy: 0}); updateCursor(0, yDistanceBetweenP0andP1 / 2); const highlightedCircles3 = $.find( '.rv-xy-plot__series--mark circle' ).reduce(highlightedCircle, []); expect(highlightedCircles3.length).toBe(1); expect(Math.abs(highlightedCircles3[0].cx - 0) < 0.005).toBeTruthy(); expect(Math.abs(highlightedCircles3[0].cy - 2.5) < 0.005).toBeTruthy(); function updateCursor(x, y) { $.find('.rv-xy-plot__series--mark').simulate('mousemove', { nativeEvent: {clientX: x, clientY: y + chatTopPadding} }); } function highlightedCircle(_highlightedCircle, circle) { const isHighlighted = circle.prop('style').fill === '#FF9833'; const circlePosition = {cx: circle.prop('cx'), cy: circle.prop('cy')}; return isHighlighted ? [..._highlightedCircle, circlePosition] : _highlightedCircle; } }); }); ================================================ FILE: packages/react-vis/tests/components/parallel-coordinates.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import ParallelCoordinates from 'parallel-coordinates'; import BasicParallelCoordinates from '../../../showcase/parallel-coordinates/basic-parallel-coordinates'; import AnimatedParallelCoordinates from '../../../showcase/parallel-coordinates/animated-parallel-coordinates'; import BrushedParallelCoordinates from '../../../showcase/parallel-coordinates/brushed-parallel-coordinates'; import {testRenderWithProps} from '../test-utils'; const PARALLEL_COODINATES_PROPS = { data: [ { explosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 } ], domains: [ {name: 'nice', domain: [0, 100]}, {name: 'explosions', domain: [6.9, 7.1]}, {name: 'wow', domain: [0, 11]}, {name: 'dog', domain: [0, 16]}, {name: 'sickMoves', domain: [0, 20]} ], height: 300, width: 400 }; describe('Parallel Coordinates', () => { // make sure that the components render at all testRenderWithProps(ParallelCoordinates, PARALLEL_COODINATES_PROPS); test('Basic Parallel Coordinates', () => { const $ = mount(); expect($.find('div.rv-parallel-coordinates-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(6); expect($.find('LineSeries.rv-parallel-coordinates-chart-line').length).toBe( 3 ); expect($.find('MarkSeries.rv-parallel-coordinates-chart-line').length).toBe( 3 ); expect($.find('circle').length).toBe(18); expect($.find('div.rv-parallel-coordinates-chart').text()).toBe( '0.002.004.006.008.0010.0$2.0$4.8$7.6$10$13$165.006.007.008.009.0010.00.002.004.006.008.0010.00.001.402.804.205.607.0010.08.406.805.203.602.00mileagepricesafetyperformanceinteriorwarranty' ); }); test('Animated Parallel Coordinates ', () => { const $ = mount(); expect($.find('div.rv-parallel-coordinates-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(5); expect($.find('path.rv-parallel-coordinates-chart-line').length).toBe(1); // This relies on floating point // t.equal( // $.find('div.rv-parallel-coordinates-chart').text(), // '020406080100niceexplosionswowdogsickMoves', // 'should find the right text content' // ); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(5); expect($.find('path.rv-parallel-coordinates-chart-line').length).toBe(1); }); test('Brushed Parallel Coordinates', () => { const $ = mount(); expect($.find('path.rv-parallel-coordinates-chart-line').length).toBe(150); expect($.find('div.rv-parallel-coordinates-chart').text()).toBe( '4.35.05.76.57.27.92.02.53.03.43.94.41.02.23.44.55.76.90.100.581.11.52.02.5sepal lengthsepal widthpetal lengthpetal width' ); // brush $.find('.rv-mouse-target') .at(0) .simulate('mouseDown', {nativeEvent: {offsetX: 50, offsetY: 100}}); for (let i = 0; i < 100; i++) { $.find('.rv-mouse-target') .at(0) .simulate('mouseMove', {nativeEvent: {offsetX: 50, offsetY: 100 + i}}); } expect( $.find('.rv-parallel-coordinates-chart-line-unselected').length ).toBe(0); $.find('.rv-mouse-target') .at(0) .simulate('mouseUp', {nativeEvent: {offsetX: 50, offsetY: 200}}); expect( $.find('path.rv-parallel-coordinates-chart-line-unselected').length ).toBe(102); // click to clear $.find('.rv-mouse-target') .at(0) .simulate('mouseDown', {nativeEvent: {offsetX: 50, offsetY: 95}}); $.find('.rv-mouse-target') .at(0) .simulate('mouseUp', {nativeEvent: {offsetX: 50, offsetY: 95}}); expect( $.find('.rv-parallel-coordinates-chart-line-unselected').length ).toBe(0); }); }); ================================================ FILE: packages/react-vis/tests/components/polygon-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import PolygonSeries from 'plot/series/mark-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import TriangleExample from '../../../showcase/misc/triangle-example'; describe('PolygonSeries', () => { testRenderWithProps(PolygonSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('Showcase Example - Triangle Example', () => { const $ = mount(); expect($.text()).toBe('024681012024681012'); expect($.find('.rv-xy-plot__series--polygon').length).toBe(121); }); }); ================================================ FILE: packages/react-vis/tests/components/radar-chart.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import RadialChart from 'radial-chart'; import BasicRadarChart from '../../../showcase/radar-chart/basic-radar-chart'; import AnimatedRadarChart from '../../../showcase/radar-chart/animated-radar-chart'; import FourQuadrantRadarChart from '../../../showcase/radar-chart/four-quadrant-radar-chart'; import RadarChartWithTooltips from '../../../showcase/radar-chart/radar-chart-with-tooltips'; import RadarChartSeriesTooltips from '../../../showcase/radar-chart/radar-chart-series-tooltips'; import {testRenderWithProps} from '../test-utils'; const RADAR_PROPS = { data: [ { explosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 } ], domains: [ {name: 'nice', domain: [0, 100]}, {name: 'explosions', domain: [6.9, 7.1]}, {name: 'wow', domain: [0, 11]}, {name: 'dog', domain: [0, 16]}, {name: 'sickMoves', domain: [0, 20]} ], height: 300, width: 400 }; describe('Radar', () => { // make sure that the components render at all testRenderWithProps(RadialChart, RADAR_PROPS); test('Radar: Showcase Example - Basic Radar Chart', () => { const $ = mount(); expect($.find('div.rv-radar-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(6); expect($.find('path.rv-radar-chart-polygon').length).toBe(3); expect($.find('div.rv-radar-chart').text()).toBe( '2.004.006.008.0010.0$4.8$7.6$10$13$166.007.008.009.0010.02.004.006.008.0010.01.402.804.205.607.008.406.805.203.602.00mileagepricesafetyperformanceinteriorwarranty' ); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(0); }); test('Radar: Showcase Example - Animated Radial ', () => { const $ = mount(); expect($.find('div.rv-radar-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(5); expect($.find('path.rv-radar-chart-polygon').length).toBe(1); // Floating point // t.equal( // $.find('div.rv-radar-chart').text(), // '20406080100niceexplosionswowdogsickMoves', // 'should find the right text content' // ); expect($.find('.rv-xy-plot__circular-grid-lines__line').length).toBe(10); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(5); expect($.find('path.rv-radar-chart-polygon').length).toBe(1); // Floating Point // t.equal( // $.find('div.rv-radar-chart').text(), // '20406080100niceexplosionswowdogsickMoves', // 'should find the right text content' // ); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(0); }); test('Radar: Showcase Example - Four Quadrant Radar Chart', () => { const $ = mount(); expect($.find('div.rv-radar-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(4); expect($.find('.rv-xy-manipulable-axis__ticks').children().length).toBe(24); expect($.find('path.rv-radar-chart-polygon').length).toBe(1); expect($.find('div.rv-radar-chart').text()).toBe( '20406080100204060801002040608010020406080100CVisualBasicsExcelAccess' ); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(0); }); test('Radar: Showcase Example - Radar Chart with Tooltips', () => { const $ = mount(); const chartText = 'mileagepricesafetyperformanceinteriorwarranty1234'; expect($.find('div.rv-radar-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(6); expect($.find('path.rv-radar-chart-polygon').length).toBe(7); expect($.find('g.rv-radar-chart-polygonPoint').length).toBe(7); expect($.find('div.rv-radar-chart').text()).toBe(chartText); // Tooltips const tooltipText = 'mileage: 3'; $.find('g.rv-radar-chart-polygonPoint') .at(6) .children() .at(0) .simulate('mouseOver'); expect($.text()).toBe(`${chartText}${tooltipText}`); }); test('Radar: Showcase Example - series tooltips', () => { const $ = mount(); const chartText = '2.004.006.008.0010.0$4.8$7.6$10$13$166.007.008.009.0010.02.004.006.008.0010.01.402.804.205.607.008.406.805.203.602.00mileagepricesafetyperformanceinteriorwarranty'; const hoverText = 'Mercedes'; expect($.find('div.rv-radar-chart').length).toBe(1); expect($.find('.rv-xy-manipulable-axis__ticks').length).toBe(6); expect($.find('path.rv-radar-chart-polygon').length).toBe(3); expect($.find('div.rv-radar-chart').text()).toBe(chartText); expect($.find('.rv-xy-plot__series--custom-svg').length).toBe(0); $.find('.rv-radar-chart-polygon') .at(0) .simulate('mouseOver'); expect($.find('div.rv-radar-chart').text()).toBe( `${chartText}${hoverText}` ); }); }); ================================================ FILE: packages/react-vis/tests/components/radial.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import RadialChart from 'radial-chart'; import SimpleRadialChart from '../../../showcase/radial-chart/simple-radial-chart'; import DonutChart from '../../../showcase/radial-chart/donut-chart'; import CustomRadiusRadialChart from '../../../showcase/radial-chart/custom-radius-radial-chart'; import GradientPie from '../../../showcase/radial-chart/gradient-pie'; import {testRenderWithProps} from '../test-utils'; const RADIAL_PROPS = { data: [ {angle: 1, label: 'green'}, {angle: 2, label: 'yellow'}, {angle: 5, label: 'cyan'}, {angle: 3, label: 'magenta'}, {angle: 5, label: 'yellow again', className: 'custom-class'} ], height: 300, showLabels: true, width: 400, padAngle: 0.3 }; describe('RadialChart', () => { // make sure that the components render at all testRenderWithProps(RadialChart, RADIAL_PROPS); test('Basic rendering', () => { const $ = mount(); const pieSlices = $.find('.rv-radial-chart__series--pie__slice').length; expect(pieSlices).toBe(RADIAL_PROPS.data.length); expect( $.find('.rv-radial-chart__series--pie__slice') .at(0) .prop('className') ).toBe( 'rv-xy-plot__series rv-xy-plot__series--arc-path rv-radial-chart__series--pie__slice custom-class' ); const labels = $.find('.rv-xy-plot__series--label-text').length; expect(labels).toBe(RADIAL_PROPS.data.length); expect($.text()).toBe('yellow againmagentacyanyellowgreen'); $.setProps({data: []}); expect($.find('.rv-radial-chart__series--pie__slice').length).toBe(0); expect($.find('.rv-radial-chart__series--pie__slice-overlay').length).toBe( 0 ); expect($.find('.rv-xy-plot__series--label-text').length).toBe(0); expect($.text()).toBe(''); }); test('Showcase Example - Simple Radial Chart Example', () => { const $ = mount(); expect($.find('.rv-radial-chart__series--pie__slice').length).toBe(5); expect($.find('.rv-xy-plot__series--label-text').length).toBe(5); expect($.text()).toBe('yellow againmagentacyanyellowgreen'); }); test('Showcase Example - DonutChart', () => { const $ = mount(); expect($.find('.rv-radial-chart__series--pie__slice').length).toBe(5); expect($.find('.rv-xy-plot__series--label-text').length).toBe(0); expect($.text()).toBe(''); $.find('.rv-radial-chart__series--pie__slice') .at(1) .simulate('mouseOver'); expect($.text()).toBe( 'theta: 3angle0: -2.9171931783333793angle: -4.263590029871862radius0: 0radius: 1color: 1x: -0.4338837391175583y: 0.900968867902419' ); }); test('Showcase Example - Custom radius example', () => { const $ = mount(); $.find('.rv-radial-chart__series--pie__slice') .at(1) .simulate('mouseEnter'); $.find('.rv-radial-chart__series--pie__slice') .at(1) .simulate('mouseLeave'); // multiplied by two to account for the shadow listeners expect($.find('.rv-radial-chart__series--pie__slice').length).toBe(2 * 5); expect($.find('.rv-xy-plot__series--label-text').length).toBe(4); expect($.text()).toBe( 'Sub Label onlyAlt LabelSuper Custom labelWith annotation' ); }); test('Showcase Example - Gradient Pie Example', () => { const $ = mount(); // multiplied by two to account for the shadow listeners expect($.find('.rv-radial-chart__series--pie__slice').length).toBe(3); expect($.find('.rv-xy-plot__series--label-text').length).toBe(0); expect($.find('.rv-gradient-defs linearGradient').length).toBe(3); }); }); ================================================ FILE: packages/react-vis/tests/components/rect-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import HorizontalRectSeries from 'plot/series/horizontal-bar-series'; import VerticalRectSeries from 'plot/series/vertical-bar-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import Histogram from '../../../showcase/plot/histogram'; import StackedHistogram from '../../../showcase/plot/stacked-histogram'; describe('RectSeries', () => { testRenderWithProps(HorizontalRectSeries, GENERIC_XYPLOT_SERIES_PROPS, true); testRenderWithProps(VerticalRectSeries, GENERIC_XYPLOT_SERIES_PROPS, true); test('Showcase Example - StackedHistogram', () => { const $ = mount(); expect($.text()).toBe('TOGGLE TO CANVAS01234567051015202530'); expect($.find('.rv-xy-plot__series--rect rect').length).toBe(6); $.find('.showcase-button').simulate('click'); expect($.find('.rv-xy-plot__series--rect rect').length).toBe(0); expect($.find('.rv-xy-canvas canvas').length).toBe(1); }); test('Showcase Example - Histogram', () => { const $ = mount(); expect($.text()).toBe('May 21May 28Jun 04Jun 11Jun 180.51.01.52.0'); expect($.find('.rv-xy-plot__series--rect rect').length).toBe(8); }); }); ================================================ FILE: packages/react-vis/tests/components/sankey.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Sankey from 'sankey'; import Hint from 'plot/hint'; import BasicSankey from '../../../showcase/sankey/basic'; import VoronoiSankey from '../../../showcase/sankey/voronoi'; import EnergySankey from '../../../showcase/sankey/energy-sankey'; import LinkEventSankey from '../../../showcase/sankey/link-event'; import LinkHintSankey from '../../../showcase/sankey/link-hint'; const SANKEY_PROPS = { nodes: [], links: [], width: 200, height: 200, strokeWidth: 1 }; import {testRenderWithProps} from '../test-utils'; describe('Sankey', () => { // make sure that the components render at all testRenderWithProps(Sankey, SANKEY_PROPS); test('labels', () => { const wrap = mount( ); expect(wrap.find('text').length).toBe(2); wrap.setProps({hideLabels: true}); expect(wrap.find('text').length).toBe(0); }); test('children', () => { const $ = mount( ); expect($.find(Hint).length).toBe(1); }); test('Showcase Example - BasicSankey', () => { const $ = mount(); expect($.find('.rv-sankey__link').length).toBe(3); expect($.find('.rv-sankey__node rect').length).toBe(3); }); test('Showcase Example - VoronoiSankey', () => { const $ = mount(); expect($.find('.rv-sankey__link').length).toBe(3); expect($.find('.rv-sankey__node rect').length).toBe(3); expect($.find('.rv-voronoi').length).toBe(1); expect($.find('.rv-voronoi__cell').length).toBe(3); expect($.text()).toBe('None selectedabc'); $.find('.rv-voronoi__cell') .at(0) .simulate('mouseOver'); expect($.text()).toBe('a selected!a!bc'); $.find('.rv-voronoi__cell') .at(0) .simulate('mouseLeave'); }); test('Showcase Example - LinkEventSankey', () => { const $ = mount(); expect($.find('.rv-sankey__link').length).toBe(3); expect($.find('.rv-sankey__node rect').length).toBe(3); expect($.text()).toBe('None selectedabc'); $.find('.rv-sankey__link') .at(0) .simulate('mouseOver'); expect($.text()).toBe('a -> b selectedabc'); $.find('.rv-sankey__link') .at(0) .simulate('mouseOut'); expect($.text()).toBe('None selectedabc'); }); test('Showcase Example - EnergySankey', () => { const $ = mount(); [ "PREV MODE justify NEXT MODEAgricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind", "PREV MODE center NEXT MODEAgricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind", "PREV MODE left NEXT MODEAgricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind", "PREV MODE right NEXT MODEAgricultural 'waste'Bio-conversionLiquidLossesSolidGasBiofuel importsBiomass importsCoal importsCoalCoal reservesDistrict heatingIndustryHeating and cooling - commercialHeating and cooling - homesElectricity gridOver generation / exportsH2 conversionRoad transportAgricultureRail transportLighting & appliances - commercialLighting & appliances - homesGas importsNgasGas reservesThermal generationGeothermalH2HydroInternational shippingDomestic aviationInternational aviationNational navigationMarine algaeNuclearOil importsOilOil reservesOther wastePumped heatSolar PVSolar ThermalSolarTidalUK land based bioenergyWaveWind" ].forEach(testMessage => { expect($.text()).toBe(testMessage); $.find('.showcase-button') .at(1) .simulate('click'); expect($.find('.rv-sankey__link').length).toBe(68); expect($.find('.rv-sankey__node rect').length).toBe(48); }); }); test('Showcase Example - LinkHintSankey', () => { const $ = mount(); expect($.find('.rv-sankey__link').length).toBe(3); expect($.find('.rv-sankey__node rect').length).toBe(3); expect($.find(Hint).length).toBe(0); $.find('.rv-sankey__link') .at(0) .simulate('mouseOver'); expect($.find(Hint).length).toBe(1); $.find('.rv-sankey__link') .at(0) .simulate('mouseOut'); expect($.find(Hint).length).toBe(0); }); }); ================================================ FILE: packages/react-vis/tests/components/sunburst.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Sunburst from 'sunburst'; import BasicSunburst from '../../../showcase/sunbursts/basic-sunburst'; import SunburstWithTooltips from '../../../showcase/sunbursts/sunburst-with-tooltips'; import AnimatedSunburst from '../../../showcase/sunbursts/animated-sunburst'; import {testRenderWithProps} from '../test-utils'; const INTERPOLATE_DATA = { title: 'interpolate', children: [ {title: 'ArrayInterpolator', color: '#12939A', size: 1983}, {title: 'ColorInterpolator', color: '#12939A', size: 2047}, {title: 'DateInterpolator', color: '#12939A', size: 1375}, {title: 'Interpolator', color: '#12939A', size: 8746}, {title: 'MatrixInterpolator', color: '#12939A', size: 2202}, {title: 'NumberInterpolator', color: '#12939A', size: 1382}, {title: 'ObjectInterpolator', color: '#12939A', size: 1629}, {title: 'PointInterpolator', color: '#12939A', size: 1675}, {title: 'RectangleInterpolator', color: '#12939A', size: 2042} ] }; const SUNBURST_PROPS = { height: 100, width: 100, className: 'little-nested-burst-example', hideRootNode: true, data: { name: 'animate', children: [ {title: 'Easing', color: '#12939A', size: 17010}, {title: 'FunctionSequence', color: '#12939A', size: 5842}, INTERPOLATE_DATA, {title: 'ISchedulable', color: '#12939A', size: 1041}, {title: 'Parallel', color: '#12939A', size: 5176}, {title: 'Pause', color: '#12939A', size: 449}, {title: 'Scheduler', color: '#12939A', size: 5593}, {title: 'Sequence', color: '#12939A', size: 5534}, {title: 'Transition', color: '#12939A', size: 9201}, {title: 'Transitioner', color: '#12939A', size: 19975}, {title: 'TransitionEvent', color: '#12939A', size: 1116}, {title: 'Neonate', color: '#12939A', size: 6006} ] } }; describe('Sunburst', () => { // make sure that the components render at all testRenderWithProps(Sunburst, SUNBURST_PROPS); test('Basic rendering + data changes', () => { const $ = mount(); expect( $.find('.little-nested-burst-example.rv-xy-plot__series--arc path').length ).toBe(21); $.setProps({data: INTERPOLATE_DATA}); expect($.find('.rv-xy-plot__series--arc-path').length).toBe(9); }); test('Empty', () => { const $ = mount(); expect($.find('.rv-xy-plot__series--arc-path').length).toBe(0); }); test('BasicSunburst', () => { const $ = mount(); // multiplied by two to account for the shadow listeners expect($.find('.rv-xy-plot__series--arc path').length).toBe(251 * 2); expect($.text()).toBe('click to lock selectionSUNBURST'); // check hover state expect($.state().pathValue).toEqual(false); $.find('.rv-xy-plot__series--arc-path') .at(200) .simulate('mouseover'); expect($.state().pathValue).toEqual('root > vis > events > DataEvent'); $.find('.rv-xy-plot__series--arc-path') .at(1) .simulate('click'); expect($.text()).toBe( 'click to unlock selectionDataEventroot > vis > events > DataEvent' ); $.find('.rv-xy-plot__series--arc-path') .at(1) .simulate('mouseLeave'); $.find('.rv-xy-plot__series--arc-path') .at(10) .simulate('mouseEnter'); expect($.text()).toBe( 'click to unlock selectionDataEventroot > vis > events > DataEvent' ); }); test('SunburstWithTooltips', () => { const $ = mount(); expect($.text()).toBe('cooldogssunglassesexcellentchartgreatlabel'); expect($.find('.rv-xy-plot__series--arc path').length).toBe(10); $.find('.rv-xy-plot__series--arc-path') .at(1) .simulate('mouseOver'); expect($.text()).toBe('cooldogssunglassesexcellentchartgreatlabel#FF991F'); }); test('AnimatedSunburst', () => { const $ = mount(); expect($.text()).toBe('UPDATENOT HOVERED'); expect($.find('.rv-xy-plot__series--arc path').length > 2).toBeTruthy(); $.find('.rv-xy-plot__series--arc-path') .at(1) .simulate('mouseOver'); expect($.text()).toBe('UPDATECURRENTLY HOVERING'); }); }); ================================================ FILE: packages/react-vis/tests/components/treemap.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Treemap from 'treemap'; import SimpleTreemap from '../../../showcase/treemap/simple-treemap'; import DynamicTreemap from '../../../showcase/treemap/dynamic-treemap'; import {testRenderWithProps} from '../test-utils'; const INTERPOLATE_DATA = { title: 'interpolate', children: [ {title: 'ArrayInterpolator', color: '#12939A', size: 1983}, {title: 'ColorInterpolator', color: '#12939A', size: 2047}, {title: 'DateInterpolator', color: '#12939A', size: 1375}, {title: 'Interpolator', color: '#12939A', size: 8746}, {title: 'MatrixInterpolator', color: '#12939A', size: 2202}, {title: 'NumberInterpolator', color: '#12939A', size: 1382}, {title: 'ObjectInterpolator', color: '#12939A', size: 1629}, {title: 'PointInterpolator', color: '#12939A', size: 1675}, {title: 'RectangleInterpolator', color: '#12939A', size: 2042} ] }; const TREEMAP_PROPS = { height: 100, width: 100, className: 'little-nested-tree-example', data: { name: 'animate', children: [ {title: 'Easing', color: '#12939A', size: 17010}, {title: 'FunctionSequence', color: '#12939A', size: 5842}, INTERPOLATE_DATA, {title: 'ISchedulable', color: '#12939A', size: 1041}, {title: 'Parallel', color: '#12939A', size: 5176}, {title: 'Pause', color: '#12939A', size: 449}, {title: 'Scheduler', color: '#12939A', size: 5593}, {title: 'Sequence', color: '#12939A', size: 5534}, {title: 'Transition', color: '#12939A', size: 9201}, {title: 'Transitioner', color: '#12939A', size: 19975}, {title: 'TransitionEvent', color: '#12939A', size: 1116}, {title: 'Neonate', color: '#12939A', size: 6006} ] } }; describe('Treemap', () => { // make sure that the components render at all testRenderWithProps(Treemap, TREEMAP_PROPS); test('Basic rendering', () => { const $ = mount(); expect($.find('.rv-treemap__leaf').length).toBe(22); const expectedText = 'EasingFunctionSequenceinterpolateISchedulableParallelPauseSchedulerSequenceTransitionTransitionerTransitionEventNeonateArrayInterpolatorColorInterpolatorDateInterpolatorInterpolatorMatrixInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorRectangleInterpolator'; expect($.find('.rv-treemap').text()).toBe(expectedText); expect($.find('div.little-nested-tree-example').length).toBe(1); $.setProps({data: INTERPOLATE_DATA}); expect($.find('.rv-treemap__leaf').length).toBe(10); const newText = 'interpolateArrayInterpolatorColorInterpolatorDateInterpolatorInterpolatorMatrixInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorRectangleInterpolator'; expect($.find('.rv-treemap').text()).toBe(newText); expect($.find('div.little-nested-tree-example').length).toBe(1); }); test('Custom Sorting', () => { const $ = mount(); const expectedText = 'interpolateTransitionerEasingTransitionNeonateFunctionSequenceSchedulerSequenceParallelTransitionEventISchedulablePauseInterpolatorMatrixInterpolatorColorInterpolatorRectangleInterpolatorArrayInterpolatorPointInterpolatorObjectInterpolatorNumberInterpolatorDateInterpolator'; const expectedReverseText = 'PauseISchedulableTransitionEventParallelSequenceSchedulerFunctionSequenceNeonateTransitionEasingTransitionerinterpolateDateInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorArrayInterpolatorRectangleInterpolatorColorInterpolatorMatrixInterpolatorInterpolator'; const expectedNewText = 'interpolateInterpolatorMatrixInterpolatorColorInterpolatorRectangleInterpolatorArrayInterpolatorPointInterpolatorObjectInterpolatorNumberInterpolatorDateInterpolator'; const expectedReverseNewText = 'interpolateDateInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorArrayInterpolatorRectangleInterpolatorColorInterpolatorMatrixInterpolatorInterpolator'; [ 'circlePack', 'partition', 'partition-pivot', 'squarify', 'resquarify', 'slice', 'dice', 'slicedice', 'binary' ].forEach(mode => { $.setProps({ mode, sortFunction: (a, b) => b.value - a.value, ...TREEMAP_PROPS }); expect($.find('.rv-treemap').text()).toBe(expectedText); $.setProps({sortFunction: (a, b) => a.value - b.value}); expect($.find('.rv-treemap').text()).toBe(expectedReverseText); // circle pack includes the root node, while the other modes do not. The root of INTERPOLATE_DATA has a title, but the root of the default data does not $.setProps({ data: INTERPOLATE_DATA, sortFunction: (a, b) => b.value - a.value }); expect($.find('.rv-treemap').text()).toBe(expectedNewText); $.setProps({sortFunction: (a, b) => a.value - b.value}); expect($.find('.rv-treemap').text()).toBe(expectedReverseNewText); }); }); test('Empty treemap', () => { const $ = mount(); // 1 is the empty root node expect($.find('.rv-treemap__leaf').length).toBe(1); expect($.find('.rv-treemap').text()).toBe(''); expect($.find('div.little-nested-tree-example').length).toBe(1); }); test('Hide Root Node', () => { const $ = mount(); // the tree from TREEMAP_PROPS doesn't have a title so its text is the same with ot without the root const expectedText = 'EasingFunctionSequenceinterpolateISchedulableParallelPauseSchedulerSequenceTransitionTransitionerTransitionEventNeonateArrayInterpolatorColorInterpolatorDateInterpolatorInterpolatorMatrixInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorRectangleInterpolator'; const numberOfElements = 21; const numberOfElementsWithRoot = numberOfElements + 1; const expectedNewText = 'ArrayInterpolatorColorInterpolatorDateInterpolatorInterpolatorMatrixInterpolatorNumberInterpolatorObjectInterpolatorPointInterpolatorRectangleInterpolator'; const expectedNewTextWithRoot = `interpolate${expectedNewText}`; const numberOfNewElements = 9; const numberOfNewElementsWithRoot = numberOfNewElements + 1; [ 'circlePack', 'partition', 'partition-pivot', 'squarify', 'resquarify', 'slice', 'dice', 'slicedice', 'binary' ].forEach(mode => { $.setProps({mode, ...TREEMAP_PROPS, hideRootNode: false}); expect($.find('.rv-treemap').text()).toBe(expectedText); expect($.find('.rv-treemap__leaf').length).toBe(numberOfElementsWithRoot); $.setProps({hideRootNode: true}); expect($.find('.rv-treemap').text()).toBe(expectedText); expect($.find('.rv-treemap__leaf').length).toBe(numberOfElements); $.setProps({data: INTERPOLATE_DATA, hideRootNode: false}); expect($.find('.rv-treemap').text()).toBe(expectedNewTextWithRoot); expect($.find('.rv-treemap__leaf').length).toBe( numberOfNewElementsWithRoot ); $.setProps({hideRootNode: true}); expect($.find('.rv-treemap').text()).toBe(expectedNewText); expect($.find('.rv-treemap__leaf').length).toBe(numberOfNewElements); }); }); test('SimpleTreemap', () => { const $ = mount(); [ 'circlePack', 'partition', 'partition-pivot', 'squarify', 'resquarify', 'slice', 'dice', 'slicedice', 'binary' ].forEach(mode => { const selector = mode === 'circlePack' ? '.rv-treemap__leaf circle' : 'path.rv-treemap__leaf'; // circle pack includes the root node, while the other modes do not const numberOfElements = 252; expect($.find(selector).length).toBe(numberOfElements); expect($.text()).toBe(`USE DOMPREV MODE ${mode} NEXT MODE`); // switch to svg $.find('.showcase-button') .at(0) .simulate('click'); expect($.find('.rv-treemap__leaf').length).toBe(numberOfElements); expect($.text()).toBe(`USE SVGPREV MODE ${mode} NEXT MODE`); // switch back to dom and go to next mode $.find('.showcase-button') .at(0) .simulate('click'); $.find('.showcase-button') .at(2) .simulate('click'); }); }); test('DynamicTreemap', () => { const $ = mount(); expect($.find('.rv-treemap__leaf').length).toBe(21); expect($.find('.rv-treemap').text()).toBe( '2020202020202020202020202020202020202020' ); }); }); ================================================ FILE: packages/react-vis/tests/components/voronoi.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import Voronoi from '../../src/plot/voronoi.js'; import XYPlot from 'plot/xy-plot'; import VoronoiLineChart from '../../../showcase/misc/voronoi-line-chart'; const StatelessVoronoiWrapper = () => ( ({ x, y: 10, className: `my-class-${x}`, style: {color: 'red'} }))} /> ); describe('Voronoi', () => { test('Basic Chart', () => { const $ = mount(); expect( $.find('.rv-voronoi__cell') .at(30) .prop('style').color ).toBe('red'); expect( $.find('.rv-voronoi__cell') .at(30) .hasClass('my-class-30') ).toBe(true); expect( $.find('.rv-voronoi__cell') .at(50) .hasClass('my-class-50') ).toBe(true); }); test('VoronoiLineChart', () => { const $ = mount(); expect($.text()).toBe( 'Show Voronoi1.01.52.02.53.03.54.0X Axis2468101214Y Axis' ); expect($.find('.rv-voronoi__cell').length).toBe(12); expect($.find('.rv-xy-plot__series--line').length).toBe(3); expect($.find('circle').length).toBe(0); $.find('input').simulate('click'); $.find('.rv-voronoi__cell') .at(0) .simulate('mouseOver'); expect($.find('circle').length).toBe(1); $.find('.rv-voronoi__cell') .at(0) .simulate('mouseOut'); expect($.find('circle').length).toBe(0); }); }); ================================================ FILE: packages/react-vis/tests/components/whisker-series.test.js ================================================ import React from 'react'; import {mount} from 'enzyme'; import WhiskerSeries from 'plot/series/whisker-series'; import {testRenderWithProps, GENERIC_XYPLOT_SERIES_PROPS} from '../test-utils'; import WhiskerChart from '../../../showcase/plot/whisker-chart'; testRenderWithProps(WhiskerSeries, GENERIC_XYPLOT_SERIES_PROPS, true); describe('WhiskerSeries', () => { test('Showcase Example - Whisker Scatterplot', () => { const $ = mount( ); expect($.text()).toBe('1.01.52.02.53.068101214'); expect($.find('g.whisker-series-example').length).toBe(1); // 8 lines each per 5 (double) whiskers expect($.find('.rv-xy-plot__series--whisker line').length).toBe(40); }); }); ================================================ FILE: packages/react-vis/tests/components/xy-plot.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {mount, shallow} from 'enzyme'; import AbstractSeries from 'plot/series/abstract-series'; import VerticalBarSeries from 'plot/series/vertical-bar-series'; import BarSeries from 'plot/series/bar-series'; import LineSeries from 'plot/series/line-series'; import XAxis from 'plot/axis/x-axis'; import XYPlot from 'plot/xy-plot'; import HorizontalGridLines from 'plot/horizontal-grid-lines'; import MixedStackedChart from '../../../showcase/plot/mixed-stacked-chart'; import {FlexibleCharts} from '../../../showcase/flexible/flexible-examples'; import EmptyChart from '../../../showcase/axes/empty-chart'; import {testRenderWithProps} from '../test-utils'; const XYPLOT_PROPS = {width: 10, height: 10}; testRenderWithProps(XYPlot, XYPLOT_PROPS, false); describe('XYPlot', () => { test('Render a stacked bar chart', () => { const wrapper = shallow( ); const renderedVerticalBarsWrapper = wrapper.find(VerticalBarSeries); expect(renderedVerticalBarsWrapper.at(0).prop('data')).toEqual([ {x: 1, y: 0}, {x: 2, y: 1}, {x: 3, y: 2} ]); expect(renderedVerticalBarsWrapper.at(1).prop('data')).toEqual([ {x: 1, y: 2, y0: 0}, {x: 2, y: 2, y0: 1}, {x: 3, y: 2, y0: 2} ]); }); test('Render a stacked bar chart with other children', () => { const wrapper = shallow( { // Empty div here is intentional, to test series children handling }
); const renderedVerticalBarsWrapper = wrapper.find(VerticalBarSeries); expect(renderedVerticalBarsWrapper.at(0).prop('data')).toEqual([ {x: 1, y: 0} ]); expect(renderedVerticalBarsWrapper.at(1).prop('data')).toEqual([ {x: 1, y: 2, y0: 0} ]); }); test('Render a bar chart with some nonAnimatedProps', () => { const wrapper = shallow( ); const renderedXAxisWrapper = wrapper.find(XAxis); const renderedVerticalBarsWrapper = wrapper.find(VerticalBarSeries); expect(renderedXAxisWrapper.at(0).prop('animation')).toEqual({ nonAnimatedProps: ['xDomain'] }); expect(renderedVerticalBarsWrapper.at(0).prop('animation')).toEqual({ nonAnimatedProps: ['xDomain'] }); }); test('testing flexible charts', () => { const $ = mount(FlexibleCharts({height: 200, width: 400})); const w = $.find('.flexible-width .rv-xy-plot').prop('style'); const h = $.find('.flexible-height .rv-xy-plot').prop('style'); const v = $.find('.flexible-vis .rv-xy-plot').prop('style'); expect(w.width).not.toBe('100px'); expect(w.height).toEqual('100px'); expect(h.width).toEqual('100px'); expect(h.height).not.toBe('100px'); expect(v.width).not.toBe('100px'); expect(v.height).not.toBe('100px'); }); test('Render two stacked bar series with a non-stacked line series chart', () => { const $ = mount(); const renderedBarsWrapper = $.find(BarSeries); const renderedLineWrapper = $.find(LineSeries); expect(renderedBarsWrapper.at(0).prop('data')).toEqual([ {x: 2, y: 10}, {x: 4, y: 5}, {x: 5, y: 15} ]); expect(renderedBarsWrapper.at(1).prop('data')).toEqual([ {x: 2, y: 22, y0: 10}, {x: 4, y: 7, y0: 5}, {x: 5, y: 26, y0: 15} ]); expect(renderedLineWrapper.at(0).prop('data')).toEqual([ {x: 2, y: 26}, {x: 4, y: 8}, {x: 5, y: 30} ]); }); test('Render a line series with data accessors', () => { const $ = mount( d[0]} getY={d => d[1]}> ); const renderedLineWrapper = $.find(LineSeries); const dataProp = renderedLineWrapper.at(0).prop('data'); const getXProp = renderedLineWrapper.at(0).prop('getX'); const getYProp = renderedLineWrapper.at(0).prop('getY'); expect(dataProp.map(getXProp)).toEqual([1, 2, 3]); expect(dataProp.map(getYProp)).toEqual([0, 1, 2]); }); test('Trigger all onParentMouse handlers on Series components', () => { const onParentMouseHandler = jest.fn(); class ExtendedSeries extends AbstractSeries { onParentMouseUp = onParentMouseHandler; onParentMouseDown = onParentMouseHandler; onParentMouseMove = onParentMouseHandler; onParentMouseLeave = onParentMouseHandler; onParentMouseEnter = onParentMouseHandler; onParentTouchStart = onParentMouseHandler; onParentTouchMove = onParentMouseHandler; render() { return null; } } const $ = mount( d[0]} getY={d => d[1]}> ); $.find('svg') .at(0) .simulate('mouseenter'); $.find('svg') .at(0) .simulate('mousedown'); $.find('svg') .at(0) .simulate('mousemove'); $.find('svg') .at(0) .simulate('mouseup'); $.find('svg') .at(0) .simulate('mouseleave'); $.find('svg') .at(0) .simulate('touchstart'); $.find('svg') .at(0) .simulate('touchmove'); expect(onParentMouseHandler).toHaveBeenCalledTimes(14); }); test('dontCheckIfEmpty - Showcase example EmptyChart', () => { const $ = mount(); expect($.find('.rv-xy-plot__series').length).toBe(0); expect($.text()).toBe('1!1.5!2!3!Empty Chart Right Here'); }); test('attach ref only to series components', () => { const Stateless = () => { return
stateless
; }; const $ = mount( ); const clonedChilds = $.instance()._getClonedChildComponents(); const horizontalGridLinesChild = clonedChilds.find( element => element.type === HorizontalGridLines ); const axisChild = clonedChilds.find(element => element.type === XAxis); const lineSeriesChild = clonedChilds.find( element => element.type === LineSeries ); const statelessChild = clonedChilds.find( element => element.type === Stateless ); expect(horizontalGridLinesChild.ref === null).toBeTruthy(); expect(axisChild.ref === null).toBeTruthy(); expect(typeof lineSeriesChild.ref === 'function').toBeTruthy(); expect(statelessChild.ref === null).toBeTruthy(); }); test('with wheel event callback', () => { const onWheel = jest.fn(); const $ = mount( ); $.find('svg') .at(0) .simulate('wheel'); expect(onWheel).toHaveBeenCalledTimes(1); }); }); ================================================ FILE: packages/react-vis/tests/components.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import HorizontalGrid from 'plot/horizontal-grid-lines'; import VerticalGrid from 'plot/vertical-grid-lines'; import XAxisBottom from 'plot/axis/x-axis'; import YAxisLeft from 'plot/axis/y-axis'; import {testRenderWithProps} from './test-utils'; const XYPLOT_XAXIS_PROPS = { xRange: [0, 1], xDomain: [0, 1], xType: 'linear', width: 100, height: 100, top: 0, left: 0 }; const XYPLOT_YAXIS_PROPS = { yRange: [0, 1], yDomain: [0, 1], yType: 'linear', width: 100, height: 100, top: 0, left: 0 }; testRenderWithProps(HorizontalGrid, XYPLOT_YAXIS_PROPS); testRenderWithProps(VerticalGrid, XYPLOT_XAXIS_PROPS); testRenderWithProps(XAxisBottom, XYPLOT_XAXIS_PROPS); testRenderWithProps(YAxisLeft, XYPLOT_YAXIS_PROPS); ================================================ FILE: packages/react-vis/tests/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import './setup'; import './utils/axis-utils-tests'; import './utils/chart-utils-tests'; import './utils/data-utils-tests'; import './utils/react-utils-tests'; import './utils/scales-utils-tests'; import './utils/series-utils-tests'; import './utils/styling-utils-tests'; import './components'; import './components/line-series-canvas-test'; import './components/animation-tests'; import './components/area-series-tests'; import './components/arc-series-tests'; import './components/axes-tests'; import './components/axis-tick-format-tests'; import './components/axis-title-tests'; import './components/bar-series-tests'; import './components/borders-tests'; import './components/canvas-component-tests'; import './components/circular-grid-lines-tests'; import './components/color-article-tests'; import './components/contour-series-tests'; import './components/crosshair-tests'; import './components/custom-svg-series-tests'; import './components/data-article-tests'; import './components/decorative-axis-tests'; import './components/gradient-tests'; import './components/grid-lines-tests'; import './components/heatmap-tests'; import './components/hexbin-series-tests'; import './components/highlight-tests'; import './components/hints-tests'; import './components/interaction-article-tests'; import './components/legends-tests'; import './components/label-series-tests'; import './components/line-series-tests'; import './components/make-vis-flexible-tests'; import './components/mark-series-tests'; import './components/whisker-series-tests'; import './components/parallel-coordinates-tests'; import './components/polygon-series-tests'; import './components/radial-tests'; import './components/radar-chart-tests'; import './components/rect-series-tests'; import './components/treemap-tests'; import './components/sankey-tests'; import './components/showcase-example-tests'; import './components/sunburst-tests'; import './components/voronoi-tests'; import './components/xy-plot-tests'; ================================================ FILE: packages/react-vis/tests/jsconfig.json ================================================ { "typeAcquisition": { "enable": true, "include": [ "jest" ] } } ================================================ FILE: packages/react-vis/tests/plot/__snapshots__/content-clip-path.test.js.snap ================================================ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`content-clip-path should render 1`] = ` `; ================================================ FILE: packages/react-vis/tests/plot/content-clip-path.test.js ================================================ import React from 'react'; import {shallow} from 'enzyme'; import ContentClipPath from '../../src/plot/content-clip-path'; describe('content-clip-path', () => { it('should render', () => { const wrapper = shallow( ); expect(wrapper).toMatchSnapshot(); }); it('should default id to content-area', () => { const wrapper = shallow( ); const clip = wrapper.find('clipPath'); expect(clip.prop('id')).toEqual('content-area'); }); }); ================================================ FILE: packages/react-vis/tests/setup.js ================================================ /* eslint-disable no-undef */ import jsdom from 'jsdom'; import Enzyme from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; Enzyme.configure({adapter: new Adapter()}); global.document = jsdom.jsdom(''); global.window = document.defaultView; Object.keys(document.defaultView).forEach(function mapProperties(property) { if (typeof global[property] === 'undefined') { global[property] = document.defaultView[property]; } }); global.navigator = { userAgent: 'node.js' }; /* eslint-enable no-undef */ ================================================ FILE: packages/react-vis/tests/test-utils.js ================================================ /* eslint-disable jest/no-export */ import React from 'react'; import {mount} from 'enzyme'; const NOOP = f => f; export const GENERIC_XYPLOT_SERIES_PROPS = { xDomain: [0, 1], xRange: [0, 1], xType: 'linear', xDistance: 1, yDomain: [0, 1], yRange: [0, 1], yDistance: 1, yType: 'linear', data: [ {x: 1, y: 1}, {x: 2, y: 2} ], _allData: [ [ {x: 1, y: 1}, {x: 2, y: 2} ] ], onSeriesMouseOver: NOOP, onSeriesMouseOut: NOOP, onSeriesClick: NOOP, onSeriesRightClick: NOOP, onValueMouseOver: NOOP, onValueMouseOut: NOOP, onValueClick: NOOP, onValueRightClick: NOOP }; export const testRenderWithProps = (Component, props, wrapWithSvg = false) => // eslint-disable-next-line jest/require-top-level-describe test(`Rendering ${Component.displayName}`, () => { const wrapper = mount( wrapWithSvg ? ( ) : ( ) ); const component = wrapper.find(Component); expect(component).toHaveLength(1); const componentProps = component.props(); Object.keys(props).forEach(propName => { expect(componentProps[propName]).toEqual(props[propName]); }); }); ================================================ FILE: packages/react-vis/tests/utils/axis-utils.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import {scaleLinear} from 'd3-scale'; import {range} from 'd3-array'; import { getTicksTotalFromSize, getTickValues, getAxisAngle, generateFit, generatePoints } from 'utils/axis-utils'; describe('axis-utils', () => { test('getTicksTotalFromSize', () => { expect(getTicksTotalFromSize(0) === 5).toBeTruthy(); expect(getTicksTotalFromSize(301) === 10).toBeTruthy(); expect(getTicksTotalFromSize(701) === 20).toBeTruthy(); }); test('getTickValues', () => { const scale = scaleLinear() .domain([0, 1]) .range(['red', 'blue']); expect( getTickValues(scale, 10, false).map(d => Math.round(d * 1000) / 1000) ).toEqual([0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]); const predefinedVals = ['got dang', 1, undefined, 'lolz']; expect(getTickValues(scale, 10, predefinedVals)).toEqual(predefinedVals); }); test('getAxisAngle', () => { expect(getAxisAngle({x: 0, y: 0}, {x: 1, y: 1})).toBe(Math.PI / 4); expect(getAxisAngle({x: 0, y: 0}, {x: 0, y: 1})).toBe(Math.PI / 2); expect(getAxisAngle({x: 0, y: 0}, {x: 0, y: -1})).toBe((3 * Math.PI) / 2); }); test('generateFit', () => { expect(generateFit({x: 0, y: 0}, {x: 1, y: 1})).toEqual({ left: 0, offset: 0, right: 1, slope: 1 }); expect(generateFit({x: 0, y: 0}, {x: 0, y: 1})).toEqual({ left: 0, offset: 0, right: 1, slope: 0 }); expect(generateFit({x: 0, y: 0}, {x: 0, y: -1})).toEqual({ left: 0, offset: 0, right: -1, slope: 0 }); const result = generateFit( {x: 175, y: 125}, {x: 17.33044811707665, y: 179.23546738969475} ); const numberOfTicks = 5; const left = result.left; const right = result.right; const pointSlope = (right - left) / numberOfTicks; const lengthOfGeneratedPoints = range(left, right + pointSlope, pointSlope) .length; expect(lengthOfGeneratedPoints).toBe(7); }); test('generatePoints', () => { const result = generatePoints({ axisStart: {x: 0, y: 1}, axisEnd: {x: 1, y: 1}, numberOfTicks: 5, axisDomain: [10, 100] }); const expectedResult = { points: [ {text: 10, y: 1, x: 0}, {text: 28, y: 1, x: 0.2}, {text: 46, y: 1, x: 0.4}, {text: 64, y: 1, x: 0.6000000000000001}, {text: 82, y: 1, x: 0.8}, {text: 100, y: 1, x: 1} ], slope: -0 }; // const result2 = generatePoints({ // axisStart: {x: 175, y: 125}, // axisEnd: {x: 17.33044811707665, y: 179.23546738969475}, // numberOfTicks: 5, // axisDomain: [0, 100] // }); const expectedResult2 = { points: [ {text: 0, y: 125.00000000000001, x: 175}, {text: 20, y: 135.84709347793896, x: 143.46608962341534}, {text: 40, y: 146.6941869558779, x: 111.93217924683066}, {text: 59.99999999999999, y: 157.54128043381687, x: 80.398268870246}, {text: 80, y: 168.38837391175582, x: 48.86435849366133}, {text: 100, y: 179.23546738969478, x: 17.330448117076656} ], slope: -0.34398187057680607 }; const result3 = generatePoints({ axisStart: {x: 175, y: 125}, axisEnd: {x: 175, y: 250}, numberOfTicks: 5, axisDomain: [0, 100] }); const expectedResult3 = { points: [ {text: 0, y: 125, x: 175}, {text: 20, y: 150, x: 175}, {text: 40, y: 175, x: 175}, {text: 60, y: 200, x: 175}, {text: 80, y: 225, x: 175}, {text: 100, y: 250, x: 175} ], slope: Infinity }; const result4 = generatePoints({ axisStart: {x: 175, y: 125}, axisEnd: {x: 174.99999999999997, y: 250}, numberOfTicks: 5, axisDomain: [0, 100] }); const expectedResult4 = { points: [ {text: 0, y: 128, x: 175}, {text: 0, y: 128, x: 175}, {text: 0, y: 128, x: 175}, {text: 100, y: 256, x: 174.99999999999997}, {text: 100, y: 256, x: 174.99999999999997} ], slope: -4398046511104000 }; expect(result).toEqual(expectedResult); // Relies on testing library to handle differences in floating point numbers. // t.deepEqual(result2, expectedResult2); expect(expectedResult2.points.length).toBe(6); expect(result3).toEqual(expectedResult3); expect(result4).toEqual(expectedResult4); }); }); ================================================ FILE: packages/react-vis/tests/utils/chart-utils.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import {getRadialLayoutMargin} from 'utils/chart-utils'; describe('chart-utils', () => { test('getRadialLayoutMargin', () => { expect(getRadialLayoutMargin(500, 300, 120)).toEqual({ bottom: 30, left: 130, right: 130, top: 30 }); expect(getRadialLayoutMargin(300, 500, 120)).toEqual({ bottom: 130, left: 30, right: 30, top: 130 }); expect(getRadialLayoutMargin(300, 300, 120)).toEqual({ bottom: 30, left: 30, right: 30, top: 30 }); }); }); ================================================ FILE: packages/react-vis/tests/utils/data-utils.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import { getUniquePropertyValues, addValueToArray, transformValueToString } from 'utils/data-utils'; const arr = [{a: 1}, {b: 3, a: 2}, {a: 2}]; describe('data-utils', () => { test('getUniquePropertyValues', () => { const result = getUniquePropertyValues(arr, d => d.a); expect(result.length === 2).toBeTruthy(); expect(result.indexOf(1) !== -1 && result.indexOf(2) !== -1).toBeTruthy(); }); test('addValueToArray', () => { expect(addValueToArray([-10, 10], 1)).toEqual([-10, 10]); expect(addValueToArray([-10, 0], 1)).toEqual([-10, 1]); expect(addValueToArray([0, 10], -1)).toEqual([-1, 10]); }); test('transformValueToString', () => { expect(transformValueToString(0)).toEqual(0); // 43200000 - this is the timestamp for 12PM on 1970-01-01 // This plays much nicer when running tests locally for different timezones. expect(transformValueToString(new Date(43200000))).toEqual( 'Thu Jan 01 1970' ); }); }); ================================================ FILE: packages/react-vis/tests/utils/react-utils.test.js ================================================ import {isReactDOMSupported} from 'utils/react-utils'; describe('react-utils', () => { test('isReactDOMSupported', () => { expect(isReactDOMSupported()).toBeTruthy(); }); }); ================================================ FILE: packages/react-vis/tests/utils/scales-utils.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import { _adjustCategoricalScale, getScaleObjectFromProps, getScalePropTypesByAttribute, getAttributeFunctor, getAttr0Functor, getAttributeScale, getAttributeValue, getFontColorFromBackground, getOptionalScaleProps, getXYPlotValues, _getSmallestDistanceIndex, getScaleFnFromScaleObject, _getScaleDistanceAndAdjustedDomain, extractScalePropsFromProps, getMissingScaleProps, literalScale } from 'utils/scales-utils'; const isScaleConsistent = (scaleObject, attr) => { return ( scaleObject && scaleObject.range && scaleObject.domain && scaleObject.type && scaleObject.attr === attr ); }; const _allData = [[{x: 1}, {x: 2}, {x: 3}, {x: 2}]]; const _xValue = 20; const xRange = [0, 100]; const xDomain = [1, 5]; const xType = 'ordinal'; const xDistance = 10; describe('scales-utils', () => { test('getScaleObjectFromProps ', () => { // with empty props const nullResult = getScaleObjectFromProps({}, 'x'); expect(nullResult).toBe(null); // with empty domain const noRangeResult = getScaleObjectFromProps({xDomain}, 'x'); expect(noRangeResult).toBe(null); // with empty range const noDomainResult = getScaleObjectFromProps({xRange}, 'x'); expect(noDomainResult).toBe(null); // with all props const completeResult = getScaleObjectFromProps( {xRange, _allData, xDomain, xType, xDistance}, 'x' ); expect(isScaleConsistent(completeResult, 'x')).toBeTruthy(); expect(completeResult.type).toBe(xType); // does not mutate passed domain const tXDomain = [1, 5]; const scaleObj = getScaleObjectFromProps( { xRange, _adjustBy: ['x'], _adjustWhat: [0], _allData, xDomain: tXDomain, xDistance }, 'x' ); expect(scaleObj.domain).toEqual([0.5, 5.5]); expect(tXDomain).toEqual([1, 5]); // with the value that overrides props const valueResult = getScaleObjectFromProps({x: 10, _allData}, 'x'); expect(isScaleConsistent(valueResult, 'x')).toBeTruthy(); expect(valueResult.isValue).toBe(true); }); test('getScalePropTypesByAttribute', () => { const result = Object.keys(getScalePropTypesByAttribute('size')); const expectedResult = [ '_sizeValue', 'sizeDomain', 'getSize', 'getSize0', 'sizeRange', 'sizeType', 'sizeDistance', 'sizeBaseValue' ]; expect(result).toEqual(expectedResult); }); test('getAttributeFunctor', () => { // without props let result = getAttributeFunctor({_xValue}, 'x'); expect(result({x: Math.random()}) === _xValue).toBeTruthy(); expect(result({})).toBe(_xValue); // with props result = getAttributeFunctor({xRange, xDomain}, 'x'); const isFunction = typeof result === 'function'; expect(isFunction).toBeTruthy(); expect(result(_allData[0][0])).toBe(xRange[0]); expect(result({data: {x: 10}})).toBe(225); // with custom accessor result = getAttributeFunctor({xRange, xDomain, getX: d => d.value}, 'x'); expect(typeof result === 'function').toBeTruthy(); expect(result({data: {x: 10, value: 1}})).toBe(0); }); test('getAttr0Functor', () => { // without props let result = getAttr0Functor({}, 'x'); expect(null).toBe(null); // using a literal scale to check that the fall back is working correctly const exNaughtData = [ [{x: 1, x0: 1}, {x: 0}, {x: 3, x0: 3}, {x: 2, x0: 4}] ]; result = getAttr0Functor( { xRange, _allData: exNaughtData, xDomain, xType: 'literal', xDistance }, 'x' ); expect(typeof result === 'function').toBeTruthy(); expect(result(exNaughtData[0][0])).toBe(1); expect(result(exNaughtData[0][1])).toBe(1); expect(result({data: {x: 10, x0: 5}})).toBe(5); // with custom accessor result = getAttr0Functor( { xRange, _allData: exNaughtData, getX0: d => d.z, xDomain, xType: 'literal', xDistance }, 'x' ); expect(typeof result === 'function').toBeTruthy(); expect(result({data: {x: 10, x0: 5, z: 1}})).toBe(1); // now with a linear scale result = getAttr0Functor( { xRange, _allData: exNaughtData, xDomain, xType: 'linear', xDistance }, 'x' ); expect(typeof result === 'function').toBeTruthy(); expect(result(exNaughtData[0][0])).toBe(xRange[0]); expect(result(exNaughtData[0][1])).toBe(0); expect(result({data: {x: 10, x0: 5}})).toBe(100); }); test('getAttributeScale', () => { // without props let result = getAttributeScale({}, 'x'); expect(result === null).toBeTruthy(); // with props result = getAttributeScale({xRange, xDomain}, 'x'); const isFunction = typeof result === 'function'; expect(isFunction).toBeTruthy(); expect(result(_allData[0][0].x)).toBe(xRange[0]); }); test('getAttributeValue ', () => { // without props let result = getAttributeValue({_xValue}, 'x'); expect(result === _xValue).toBeTruthy(); // with props result = getAttributeValue({x: 10}, 'x'); expect(result).toBe(10); // with props including a scale type result = getAttributeValue( {opacity: 0.5, opacityType: 'literal'}, 'opacity' ); expect(result).toBe(0.5); }); test('_getSmallestDistanceIndex', () => { const scaleObj = {type: 'linear', domain: [0, 1], range: [0, 1]}; const runTest = arg => _getSmallestDistanceIndex(arg, scaleObj); expect(runTest([0, 0, 2])).toBe(1); expect(runTest([0, 1, 2])).toBe(1); expect(runTest([0, 2, 2])).toBe(2); expect(runTest([0, 2, 2])).toBe(2); expect(runTest([1, 2, 2])).toBe(2); expect(runTest([2, 2, 2])).toBe(1); }); test('extractScalePropsFromProps', () => { expect( Object.keys(extractScalePropsFromProps({}, [])).length === 0 ).toBeTruthy(); const props = { aType: 'linear', aRange: [1, 2], _aValue: 10, somethingElse: [], bDomain: [1, 2, 3], getA: d => d.a }; const result = extractScalePropsFromProps(props, ['a', 'b']); expect( Object.keys(result).length === 5 && result.aType === props.aType && result.aRange === props.aRange && result._aValue === props._aValue && result.bDomain === props.bDomain && result.getA === props.getA ).toBeTruthy(); }); test('getMissingScaleProps', () => { const fakeDataInteger = [ {x: 10, y: 10}, {x: 15, y: 15}, {x: 20, y: 20} ]; const fakeDataIntegerDomain = [9, 21]; const fakeDataString = [{x: 'React'}, {x: 'Vis'}]; const fakeDataStringDomain = ['React', 'Vis']; const dayOne = 971136000; const dayTen = 972000000; const fakeDomain = [0, 100]; const fakeDataUnixTime = [{x: dayOne}, {x: dayTen}]; const paddedDayOne = dayOne - (dayTen - dayOne) * 0.1; const paddedDayTen = dayTen + (dayTen - dayOne) * 0.1; const fakePadding = 10; expect(Object.keys(getMissingScaleProps({}, [], [])).length).toBe(0); const result = getMissingScaleProps({}, _allData[0], ['x']); expect( Boolean(result.xDomain) && result.xDomain.length === 2 && result.xDomain[0] === 1 && result.xDomain[1] === 3 ).toBeTruthy(); expect( getMissingScaleProps( { xPadding: fakePadding }, fakeDataInteger, ['x'] ).xDomain ).toEqual(fakeDataIntegerDomain); // need to use json stringify to peel off the functions expect( JSON.stringify( getMissingScaleProps( { xPadding: fakePadding, xDomain: fakeDomain }, fakeDataInteger, ['x'] ) ) ).toEqual('{}'); expect( Object.keys( getMissingScaleProps( { xPadding: fakePadding, xDomain: fakeDomain }, fakeDataInteger, ['x'] ) ) ).toEqual(['getX', 'getX0']); expect( getMissingScaleProps( { yPadding: fakePadding }, fakeDataInteger, ['y'] ).yDomain ).toEqual(fakeDataIntegerDomain); expect( getMissingScaleProps( { xPadding: fakePadding }, fakeDataString, ['x'] ).xDomain ).toEqual(fakeDataStringDomain); expect( getMissingScaleProps( { xPadding: fakePadding }, fakeDataUnixTime, ['x'] ).xDomain ).toEqual([paddedDayOne, paddedDayTen]); }); test('literalScale', () => { const s = literalScale(5); expect(s(0.5)).toBe(0.5); expect(s(1)).toBe(1); expect(s(1.5)).toBe(1.5); expect(s(2)).toBe(2); expect(s(2.5)).toBe(2.5); expect(s()).toBe(5); expect(s('2')).toBe('2'); }); test('getFontColorFromBackground', () => { expect(getFontColorFromBackground('#fff')).toBe('#222'); expect(getFontColorFromBackground('#000')).toBe('#fff'); expect(getFontColorFromBackground(null)).toBe(null); }); test('getScaleFnFromScaleObject', () => { expect(getScaleFnFromScaleObject()).toBe(null); const linearScale = getScaleFnFromScaleObject({ type: 'linear', domain: [0, 1], range: [1, 0] }); const literalScaleWithDefaultValue = getScaleFnFromScaleObject({ type: 'literal', domain: [], range: [5] }); expect(linearScale.domain()).toEqual([0, 1]); expect(linearScale.range()).toEqual([1, 0]); expect(literalScaleWithDefaultValue()).toEqual(5); expect(literalScaleWithDefaultValue(2)).toEqual(2); const modScaleWithZero = getScaleFnFromScaleObject({ type: 'linear', domain: [0, 0], range: [1, 0] }); expect(modScaleWithZero.domain()).toEqual([-1, 0]); const modScale = getScaleFnFromScaleObject({ type: 'linear', domain: [1, 1], range: [1, 0] }); expect(modScale.domain()).toEqual([-1, 1]); const ordinalScale = getScaleFnFromScaleObject({ type: 'ordinal', domain: ['a', 'b', 'c', 'd', 'e'], range: [20, 120] }); expect(ordinalScale.invert(-10)).toBe('a'); expect(ordinalScale.invert(25)).toBe('a'); expect(ordinalScale.invert(40)).toBe('a'); expect(ordinalScale.invert(60)).toBe('b'); expect(ordinalScale.invert(80)).toBe('c'); expect(ordinalScale.invert(100)).toBe('d'); expect(ordinalScale.invert(115)).toBe('e'); expect(ordinalScale.invert(130)).toBe('e'); }); function generateFakeData() { return new Array(100).fill(0).map((zero, i) => ({xxxx: i})); } test('_getScaleDistanceAndAdjustedDomain', () => { const FAKE_DATA = generateFakeData(); const scaleObject = { attr: 'x', domain: [0, 100], range: [0, 1], type: 'linear', // the extra x's are here to test the accessor behaviour accessor: d => d.xxxx }; const resultObject = _getScaleDistanceAndAdjustedDomain( FAKE_DATA, scaleObject ); const expectedResults = { distance: 0.009900990099009799, domain0: -0.5, domainN: 100.5 }; expect(resultObject).toEqual(expectedResults); const FAKE_TIME_DATA_WITH_ONE_VALUE_AND_X0 = [ { x: 1422774000000, x0: 1420095600000, y: 16 } ]; const FAKE_TIME_DATA_WITH_ONE_VALUE_AND_Y0 = [ { x: 16, y0: 1420095600000, y: 1422774000000 } ]; const timeScaleObjectX = { attr: 'x', domain: [1417546800000, 1430420400000], range: [0, 550], type: 'time', accessor: d => d.x, accessor0: d => d.x0 }; const timeResultWithOneValueAndX0 = _getScaleDistanceAndAdjustedDomain( FAKE_TIME_DATA_WITH_ONE_VALUE_AND_X0, timeScaleObjectX ); const expectedTimeResultsWithOneValueAndX0 = { distance: 94.72222222222223, domain0: 1416207600000, domainN: 1431759600000 }; expect(timeResultWithOneValueAndX0).toEqual( expectedTimeResultsWithOneValueAndX0 ); const timeScaleObjectY = { attr: 'y', domain: [1417546800000, 1430420400000], range: [0, 550], type: 'time', accessor: d => d.y, accessor0: d => d.y0 }; const timeResultWithOneValueAndY0 = _getScaleDistanceAndAdjustedDomain( FAKE_TIME_DATA_WITH_ONE_VALUE_AND_Y0, timeScaleObjectY ); const expectedTimeResultsWithOneValueAndY0 = { distance: 94.72222222222223, domain0: 1416207600000, domainN: 1431759600000 }; expect(timeResultWithOneValueAndY0).toEqual( expectedTimeResultsWithOneValueAndY0 ); const timeResult = _getScaleDistanceAndAdjustedDomain(FAKE_DATA, { ...timeScaleObjectX, accessor: d => d.xxxx }); const expectedTimeResults = { distance: 4.272442311048508e-8, domain0: 1417546799999.5, domainN: 1430420400000.5 }; expect(timeResult).toEqual(expectedTimeResults); const logScaleObject = { attr: 'x', domain: [-0.5, 1], range: [1, 10], type: 'log', accessor: d => d.xxxx }; const logResult = _getScaleDistanceAndAdjustedDomain( FAKE_DATA, logScaleObject ); const expectedLogResults = {distance: NaN, domain0: 0.1, domainN: 1.5}; expect(logResult).toEqual(expectedLogResults); }); test('getXYPlotValues', () => { const XYPlotProps = { colorType: 'linear', colorRange: ['#000', '#fff'], colorDomain: [0, 1] }; const children = [ {props: {color: 0}}, {props: {color: 0.5, opacity: '0.5'}}, {props: {color: 1}} ]; const result = getXYPlotValues(XYPlotProps, children); expect(result[2]._colorValue).toBe('rgb(255, 255, 255)'); expect(result[1]._opacityValue).toBe('0.5'); }); test('_adjustCategoricalScale', () => { [ { scale: {type: 'category', domain: ['a', 'b', 'c'], range: [0, 10]}, distance: 10 }, { scale: {type: 'category', domain: ['a'], range: [1, 10]}, distance: 9 }, { scale: {type: 'ordinal', domain: ['a', 'b', 'c', 'd'], range: [10, 0]}, distance: 2.5 }, { scale: {type: 'ordinal', domain: ['a'], range: [10, 1]}, distance: 9 } ].forEach(({scale, distance}) => { expect(_adjustCategoricalScale(scale)).toEqual({...scale, distance}); }); }); test('getOptionalScaleProps', () => { const foundProps = getOptionalScaleProps({node: 1, x: 2, margins: 4}); expect(foundProps).toEqual({}); const paddingProps = getOptionalScaleProps({ node: 1, x: 2, margins: 4, coolDogExplosionPadding: 10 }); expect(paddingProps).toEqual({coolDogExplosionPadding: 10}); }); }); ================================================ FILE: packages/react-vis/tests/utils/series-utils.test.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {mount} from 'enzyme'; import { isSeriesChild, getSeriesPropsFromChildren, getSeriesChildren, getStackedData } from 'utils/series-utils'; import LineSeries from 'plot/series/line-series'; import XAxis from 'plot/axis/x-axis'; import HorizontalBarSeries from 'plot/series/horizontal-rect-series'; import VerticalBarSeries from 'plot/series/vertical-rect-series'; import LabelSeries from 'plot/series/label-series'; describe('series-utils', () => { test('isSeriesChild', () => { const series = ; expect(isSeriesChild(series)).toBeTruthy(); const axis = ( ); expect(isSeriesChild(axis)).toBeFalsy(); }); test('getSeriesChildren', () => { const children = [ This squid is heavy , , ]; const $ = mount( {children} ); const expectedChildren = [{...children[2], key: '.$woah'}]; expect(getSeriesChildren($.props().children)).toEqual(expectedChildren); }); const arePropsValid = seriesProps => { return ( typeof seriesProps._colorValue !== 'undefined' && typeof seriesProps._opacityValue !== 'undefined' && typeof seriesProps.sameTypeIndex === 'number' && typeof seriesProps.sameTypeTotal === 'number' && typeof seriesProps.seriesIndex === 'number' ); }; test('collectSeriesTypesInfo', () => { const result = getSeriesPropsFromChildren([ , ]); expect(result.length === 2).toBeTruthy(); result.forEach(props => expect(arePropsValid(props)).toBeTruthy()); }); test('seriesClusterProps', () => { const result = getSeriesPropsFromChildren([ , , , ]); const expectedClusters = ['alpha', 'beta', 'gamma']; expect(result.length === 4).toBeTruthy(); result.forEach(props => { expect( props.sameTypeIndex === expectedClusters.indexOf(props.cluster) ).toBeTruthy(); expect(props.sameTypeTotal === props.clusters.length).toBeTruthy(); expect(props.clusters.length === 3).toBeTruthy(); }); }); // eslint-disable-next-line max-statements test('getStackedData', () => { const yData = [ [ {y: 2, x: 10}, {y: 4, x: 5}, {y: 5, x: 15} ], [ {y: 2, x: 12}, {y: 4, x: 2}, {y: 5, x: 11} ] ]; const stackByYExpected = [ [ {x: 2, y: 10}, {x: 4, y: 5}, {x: 5, y: 15} ], [ {x: 2, y: 22, y0: 10}, {x: 4, y: 7, y0: 5}, {x: 5, y: 26, y0: 15} ], undefined ]; const stackByXExpected = [ [ {y: 2, x: 10}, {y: 4, x: 5}, {y: 5, x: 15} ], [ {y: 2, x: 22, x0: 10}, {y: 4, x: 7, x0: 5}, {y: 5, x: 26, x0: 15} ], null ]; const stackByYExpectedPartial = [ [ {x: 2, y: 10}, {x: 4, y: 5} ], [ {x: 4, y: 7, y0: 5}, {x: 5, y: 11} ] ]; const stackByXExpectedPartial = [ [ {y: 2, x: 10}, {y: 4, x: 5} ], [ {y: 4, x: 7, x0: 5}, {y: 5, x: 11} ] ]; // Transpose data to flip stacking const xData = yData.map(arr => arr.map(d => ({x: d.y, y: d.x}))); const partialYData = [yData[0].slice(0, 2), yData[1].slice(1)]; const partialXData = partialYData.map(arr => arr.map(d => ({x: d.y, y: d.x})) ); let children = [ , ,
i think i will by that lamp
]; let results = getStackedData(children, 'y'); expect(results).toEqual(stackByYExpected); children = [ , , null ]; results = getStackedData(children, 'x'); expect(results).toEqual(stackByXExpected); children = [ , , , ]; results = getStackedData(children, 'x'); let expectedResults = [ ...stackByXExpected.slice(0, 2), ...stackByXExpected.slice(0, 2) ]; expect(results).toEqual(expectedResults); children = [ , , , ]; results = getStackedData(children, 'y'); expectedResults = [ ...stackByYExpected.slice(0, 2), ...stackByYExpected.slice(0, 2) ]; expect(results).toEqual(expectedResults); children = [ , , , ]; results = getStackedData(children, 'x'); expectedResults = [...stackByXExpectedPartial, ...stackByXExpectedPartial]; expect(results).toEqual(expectedResults); children = [ , , , ]; results = getStackedData(children, 'y'); expectedResults = [...stackByYExpectedPartial, ...stackByYExpectedPartial]; expect(results).toEqual(expectedResults); children = [ , , ]; results = getStackedData(children, 'y'); expectedResults = [ yData[0], [ {x: 10, y: 5, y0: 2}, {x: 5, y: 10, y0: 4}, {x: 15, y: 12, y0: 5} ], yData[1] ]; expect(results).toEqual(expectedResults); children = [ , , , , ]; results = getStackedData(children, 'y'); expectedResults = [ ...stackByYExpected.slice(0, 2), ...stackByYExpected.slice(0, 2), xData[1] ]; expect(results).toEqual(expectedResults); children = [ , , , ]; results = getStackedData(children, 'y'); expectedResults = [ ...stackByYExpected.slice(0, 2), ...stackByYExpected.slice(0, 2) ]; expect(results).toEqual(expectedResults); }); }); ================================================ FILE: packages/react-vis/tests/utils/styling-utils.test.js ================================================ // Copyright (c) 2016 - 2019 Uber Technologies, Inc. // // 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. import {getCombinedClassName} from 'utils/styling-utils'; describe('styling-utils', () => { test('getCombinedClassName', () => { const allValidStringParams = [ 'test_class--1', 'test_class--2', 'test_class--3' ]; const expectedAllValidStringParamsCombined = 'test_class--1 test_class--2 test_class--3'; const falsyValues = [null, undefined, false, '', 0, NaN]; const nonStringValues = [ ['invalid_class--1', 'invalid_class--2'], {foo: 'bar'}, 123, () => { return 'invalid_class--3'; } ]; expect(getCombinedClassName(...allValidStringParams)).toBe( expectedAllValidStringParamsCombined ); expect(getCombinedClassName(...allValidStringParams, ...falsyValues)).toBe( expectedAllValidStringParamsCombined ); expect( getCombinedClassName(...allValidStringParams, ...nonStringValues) ).toBe(expectedAllValidStringParamsCombined); }); }); ================================================ FILE: packages/showcase/.babelrc.json ================================================ { "plugins": [ ["module-resolver", { "root": ["."], "alias": { "react-vis/*": "../react-vis/src/*", "react-vis": "../react-vis/src" } }] ] } ================================================ FILE: packages/showcase/app.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // 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. import ReactDOM from 'react-dom'; import React from 'react'; import document from 'global/document'; import {BrowserRouter, Route} from 'react-router-dom'; import ShowcaseApp from './showcase-app'; import '../react-vis/src/styles/examples.scss'; export default function App() { // using react-router to trigger react updates on url change return ( ); } const el = document.createElement('div'); document.body.appendChild(el); ReactDOM.render(, el); ================================================ FILE: packages/showcase/axes/axis-on-0.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries } from 'react-vis'; export default function AxisOn0({ xDomain = [-1, 3], yDomain = [-5, 15], xAxisOn0 = true, yAxisOn0 = true, verticalTickValues = [], horizontalTickValues = [0] }) { return ( {!verticalTickValues || verticalTickValues.length ? ( ) : null} {!horizontalTickValues || horizontalTickValues.length ? ( ) : null} ); } ================================================ FILE: packages/showcase/axes/custom-axes-orientation.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/axes/custom-axes.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, XAxis, YAxis, MarkSeries} from 'react-vis'; const MARGIN = { left: 10, right: 10, bottom: 80, top: 20 }; const WORDS = [ 'cool', 'dog', 'skateboard', 'wow', 'such', Multiline dogs ]; export default function Example() { return ( `Value is ${v}`} tickLabelAngle={-90} /> v * v} /> WORDS[v]} /> ); } ================================================ FILE: packages/showcase/axes/custom-axis-tick-element.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries } from 'react-vis'; export default class Example extends React.Component { state = { data: [ {x: 0, y: 100, label: }, { x: 1, y: 200, label: }, {x: 2, y: 500, label: Label}, {x: 3, y: 900, label: }, {x: 4, y: 1000, label: 'Label'} ] }; formatX = (v, i) => { if (i < this.state.data.length) { return this.state.data[i].label; } return null; }; render() { return ( ); } } ================================================ FILE: packages/showcase/axes/custom-axis-tick-format.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries } from 'react-vis'; export default class Example extends React.Component { static _xTickFormatValue(v, i, scale, tickTotal) { // format axis tick with SI prefix (e.g. ms, µs) // see https://github.com/d3/d3-scale#continuous_tickFormat // and https://github.com/d3/d3-format#locale_format return `${scale.tickFormat(tickTotal, 's')(v)}s`; } static _yTickFormatValue(v, i, scale, tickTotal) { // format axis tick with SI prefix (e.g. kWh, MWh) return `${scale.tickFormat(tickTotal, 's')(v)}Wh`; } render() { const data = [ {x: 0.042, y: 100}, {x: 0.051, y: 1200}, {x: 0.063, y: 1600}, {x: 0.07, y: 1300}, {x: 0.073, y: 1220} ]; const xMin = Math.min(...data.map(e => e.x)); const xMax = Math.max(...data.map(e => e.x)); const yMin = 0; const yMax = Math.max(...data.map(e => e.y)); return ( ); } } ================================================ FILE: packages/showcase/axes/custom-axis.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries } from 'react-vis'; export default function Example() { const axisStyle = { ticks: { fontSize: '14px', color: '#333' }, title: { fontSize: '16px', color: '#333' } }; return ( `Value is ${v}`} labelValues={[2]} tickValues={[1, 1.5, 2, 3]} style={axisStyle} /> ); } ================================================ FILE: packages/showcase/axes/decorative-axes-criss-cross.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, DecorativeAxis} from 'react-vis'; const MARGIN = { left: 30, right: 30, top: 30, bottom: 30 }; export default function Example() { return ( `¡${t}!`} /> ); } ================================================ FILE: packages/showcase/axes/dynamic-complex-edge-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, MarkSeries, Hint } from 'react-vis'; const {LEFT, TOP, BOTTOM_EDGE, LEFT_EDGE, RIGHT_EDGE, TOP_EDGE} = Hint.ALIGN; const CHART_MARGINS = {left: 30, right: 10, top: 10, bottom: 25}; const XMIN = 1; const XMAX = 4; const YMIN = 5; const YMAX = 15; const DATA = [ {x: 1, y: 5}, {x: 2, y: 8}, {x: 3, y: 12}, {x: 4, y: 15} ]; const POLE = [ [ {x: XMIN, y: DATA[0].y}, {x: XMAX, y: DATA[0].y} ], [ {x: DATA[1].x, y: DATA[1].y}, {x: DATA[1].x, y: YMAX} ], [ {x: DATA[2].x, y: YMIN}, {x: DATA[2].x, y: DATA[2].y} ], [ {x: XMIN, y: DATA[3].y}, {x: DATA[3].x, y: DATA[3].y} ] ]; const DATA_HINT_ALIGN = [ { horizontal: RIGHT_EDGE, vertical: TOP }, { horizontal: LEFT, vertical: TOP_EDGE }, { horizontal: LEFT, vertical: BOTTOM_EDGE }, { horizontal: LEFT_EDGE, vertical: TOP } ]; export default class Example extends React.Component { constructor(props) { super(props); this.state = { value: null }; } _rememberValue = value => { this.setState({value}); }; render() { const {value} = this.state; return (
{value ? ( ) : null} {value ? (
{`(${value.x}, ${value.y})`}
) : null}
); } } ================================================ FILE: packages/showcase/axes/dynamic-crosshair-scatterplot.js ================================================ // Copyright (c) 2016-2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {scaleLinear} from 'd3-scale'; import { Crosshair, HorizontalGridLines, MarkSeries, VerticalGridLines, XAxis, XYPlot, YAxis, Voronoi } from 'react-vis'; const DATA = [ {x: 1, y: 4, size: 9}, {x: 1, y: 5, size: 18}, {x: 1, y: 10, size: 5}, {x: 1, y: 11, size: 29}, {x: 1, y: 13.9, size: 5}, {x: 1, y: 14, size: 8}, {x: 1.5, y: 11.8, size: 25}, {x: 1.7, y: 9, size: 30}, {x: 2, y: 5, size: 11}, {x: 2.1, y: 11.8, size: 28}, {x: 2.4, y: 7.9, size: 14}, {x: 2.4, y: 13.5, size: 20}, {x: 2.7, y: 13.7, size: 14}, {x: 2.9, y: 7.7, size: 26}, {x: 3, y: 5.4, size: 6} ].map((d, id) => ({...d, id})); const getDomain = (data, key) => { const {min, max} = data.reduce( (acc, row) => ({ min: Math.min(acc.min, row[key]), max: Math.max(acc.max, row[key]) }), {min: Infinity, max: -Infinity} ); return [min, max]; }; // magic numbers chosen for design const sizeRange = [5, 13]; const margin = {top: 10, left: 40, bottom: 40, right: 10}; const width = 300; const height = 300; // Intentionally using explicit sales here to show another way of using the voronoi const x = scaleLinear() .domain(getDomain(DATA, 'x')) .range([0, width]); const y = scaleLinear() .domain(getDomain(DATA, 'y')) .range([height, 0]); export default class Example extends React.Component { state = { selectedPointId: null, showVoronoi: true }; render() { const {crosshairValues, selectedPointId, showVoronoi} = this.state; return (
this.setState({selectedPointId: null, crosshairValues: null}) } width={width} height={height} > this.setState({ selectedPointId: index, crosshairValues: [value] }) } getColor={({id}) => selectedPointId === id ? '#FF9833' : '#12939A' } sizeRange={sizeRange} /> {crosshairValues && } {showVoronoi && ( x(d.x)} y={d => y(d.y)} /> )}
); } } ================================================ FILE: packages/showcase/axes/dynamic-crosshair.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, Crosshair } from 'react-vis'; const DATA = [ [ {x: 1, y: 10}, {x: 2, y: 7}, {x: 3, y: 15} ], [ {x: 1, y: 20}, {x: 2, y: 5}, {x: 3, y: 15} ] ]; export default class DynamicCrosshair extends React.Component { constructor(props) { super(props); this.state = { crosshairValues: [] }; } /** * Event handler for onMouseLeave. * @private */ _onMouseLeave = () => { this.setState({crosshairValues: []}); }; /** * Event handler for onNearestX. * @param {Object} value Selected value. * @param {index} index Index of the value in the data array. * @private */ _onNearestX = (value, {index}) => { this.setState({crosshairValues: DATA.map(d => d[index])}); }; render() { return ( ); } } ================================================ FILE: packages/showcase/axes/dynamic-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries, Hint } from 'react-vis'; export default class Example extends React.Component { constructor(props) { super(props); this.state = { value: null }; } _forgetValue = () => { this.setState({ value: null }); }; _rememberValue = value => { this.setState({value}); }; render() { const {value} = this.state; return ( {value ? : null} ); } } ================================================ FILE: packages/showcase/axes/dynamic-programmatic-rightedge-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, MarkSeries, Hint } from 'react-vis'; const CHART_MARGINS = {left: 50, right: 10, top: 10, bottom: 25}; const DATA = [ {x: 1, y: 5}, {x: 2, y: 12}, {x: 3, y: 8}, {x: 4, y: 15} ]; const XMAX = 4; function getAlignStyle(align, x, y) { return { right: 0, top: CHART_MARGINS.top + y }; } export default class Example extends React.Component { constructor(props) { super(props); this.state = { value: null }; } _rememberValue = value => { this.setState({value}); }; render() { const {value} = this.state; return ( {value ? ( ) : null} {value ? (
{`(${value.x}, ${value.y})`}
) : null}
); } } ================================================ FILE: packages/showcase/axes/dynamic-simple-edge-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries, Hint } from 'react-vis'; const {LEFT, RIGHT, TOP, BOTTOM_EDGE, RIGHT_EDGE, TOP_EDGE} = Hint.ALIGN; const CHART_MARGINS = {left: 50, right: 10, top: 10, bottom: 25}; const DATA = [ {x: 1, y: 5}, {x: 2, y: 10}, {x: 3, y: 10}, {x: 4, y: 15} ]; const DATA_HINT_ALIGN = [ { horizontal: RIGHT_EDGE, vertical: TOP }, { horizontal: RIGHT, vertical: BOTTOM_EDGE }, { horizontal: LEFT, vertical: TOP_EDGE }, { horizontal: LEFT, vertical: BOTTOM_EDGE } ]; export default class Example extends React.Component { constructor(props) { super(props); this.state = { value: null }; } _rememberValue = value => { this.setState({value}); }; render() { const {value} = this.state; return ( {value ? (
{`(${value.x}, ${value.y})`}
{`${DATA_HINT_ALIGN[value.x - 1].horizontal}-${ DATA_HINT_ALIGN[value.x - 1].vertical }`}
) : null}
); } } ================================================ FILE: packages/showcase/axes/dynamic-simple-topedge-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, MarkSeries, Hint } from 'react-vis'; const CHART_MARGINS = {left: 50, right: 10, top: 10, bottom: 25}; const DATA = [ {x: 1, y: 5}, {x: 2, y: 10}, {x: 3, y: 10}, {x: 4, y: 15} ]; const YMAX = 15; export default class Example extends React.Component { constructor(props) { super(props); this.state = { value: null }; } _rememberValue = value => { this.setState({value}); }; render() { const {value} = this.state; return ( {value ? ( ) : null} {value ? (
{`(${value.x}, ${value.y})`}
) : null}
); } } ================================================ FILE: packages/showcase/axes/empty-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines } from 'react-vis'; export default function EmptyChart() { return ( `${v}!`} tickValues={[1, 1.5, 2, 3]} /> ); } ================================================ FILE: packages/showcase/axes/padded-axis.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; export default class Example extends React.Component { getRandomData() { const randomYData = [...new Array(100)].map(() => Math.round(Math.random() * 40) ); return randomYData.map((val, idx) => { return {x: idx, y: val}; }); } render() { const firstData = this.getRandomData(); const secondData = this.getRandomData(); return (
); } } ================================================ FILE: packages/showcase/axes/parallel-coordinates-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {scaleLinear} from 'd3-scale'; import CarData from '../datasets/car-data.json'; import {XYPlot, DecorativeAxis, LineSeries} from 'react-vis'; const DEFAULT_DOMAIN = {min: Infinity, max: -Infinity}; // begin by figuring out the domain of each of the columns const domains = CarData.reduce((res, row) => { Object.keys(row).forEach(key => { if (!res[key]) { res[key] = {...DEFAULT_DOMAIN}; } res[key] = { min: Math.min(res[key].min, row[key]), max: Math.max(res[key].max, row[key]) }; }); return res; }, {}); // use that to generate columns that map the data to a unit scale const scales = Object.keys(domains).reduce((res, key) => { const domain = domains[key]; res[key] = scaleLinear() .domain([domain.min, domain.max]) .range([0, 1]); return res; }, {}); // break each object into an array and rescale it const mappedData = CarData.map(row => { return Object.keys(row) .filter(key => key !== 'name') .map(key => ({ x: key, y: scales[key](Number(row[key])) })); }); const MARGIN = { left: 10, right: 10, top: 10, bottom: 10 }; function ParallelCoordinatesExample() { return ( {mappedData.map((series, index) => { return ; })} {mappedData[0].map((cell, index) => { return ( ); })} ); } export default ParallelCoordinatesExample; ================================================ FILE: packages/showcase/axes/static-crosshair.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, Crosshair } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/axes/static-hints.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries, Hint } from 'react-vis'; export default function Example() { return (
This is a custom hint
for a non-existent value
); } ================================================ FILE: packages/showcase/color/line-chart-many-colors.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; const data = []; for (let i = 0; i < 20; i++) { const series = []; for (let j = 0; j < 100; j++) { series.push({x: j, y: (i / 10 + 1) * Math.sin((Math.PI * (i + j)) / 50)}); } data.push({color: i, key: i, data: series, opacity: 0.8}); } export default function Example() { return ( {data.map(({key, ...props}) => ( ))} ); } ================================================ FILE: packages/showcase/color/mini-color-examples.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { GradientDefs, XYPlot, LineSeries, MarkSeries, VerticalBarSeries } from 'react-vis'; import { DISCRETE_COLOR_RANGE, EXTENDED_DISCRETE_COLOR_RANGE, CONTINUOUS_COLOR_RANGE } from '../../react-vis/src/theme'; const colors = [ '#cd3b54', '#59b953', '#ba4fb9', '#99b53e', '#7f61d3', '#c9a83a', '#626dbc', '#e08b39', '#5ea0d8', '#cf4d2a', '#4fb79b', '#d24691', '#528240', '#c388d2', '#80742b', '#9c4a6d', '#caaa70', '#e0829f', '#9d5d30', '#dc7666' ]; const data = { noColor: [], categoryColorAtSeriesLevel: [], literalColorAtSeriesLevel: [], linearColorAtSeriesLevel: [], literalColorAtMarkLevel: [], linearColorAtMarkLevel: [], categoryColorAtMarkLevel: [] }; for (let i = 0; i < 3; i++) { const noColorSeries = []; const categoryColorSeries = []; const literalColorSeries = []; const linearColorSeries = []; for (let j = 0; j < 10; j++) { const datapoint = {x: j, y: Math.random() * 10}; const categoryDatapoint = { ...datapoint, color: Math.floor(Math.random() * 20) }; const linearDatapoint = {...datapoint, color: Math.random() * 10}; const literalDatapoint = { ...datapoint, color: colors[Math.floor(Math.random() * 20)] }; noColorSeries.push(datapoint); categoryColorSeries.push(categoryDatapoint); literalColorSeries.push(literalDatapoint); linearColorSeries.push(linearDatapoint); } data.noColor.push({key: i, data: noColorSeries}); data.categoryColorAtSeriesLevel.push({key: i, data: noColorSeries, color: i}); data.literalColorAtSeriesLevel.push({ key: i, data: noColorSeries, color: colors[i] }); data.linearColorAtSeriesLevel.push({ key: i, data: noColorSeries, color: Math.floor(Math.random() * 20) }); data.literalColorAtMarkLevel.push({key: i, data: literalColorSeries}); data.linearColorAtMarkLevel.push({key: i, data: linearColorSeries}); data.categoryColorAtMarkLevel.push({key: i, data: categoryColorSeries}); } const defaultXYPlotProps = { width: 200, height: 200, xDomain: [-0.5, 9.5], yDomain: [-0.5, 10.5], margin: {top: 5, bottom: 5, left: 5, right: 5} }; export function SensibleDefaults() { return generateCharts(data.noColor); } export function ColorInXYPlot() { return generateCharts(data.noColor, {color: 'red', stroke: 'red'}); } export function LiteralColorAtSeriesLevel() { return generateCharts(data.literalColorAtSeriesLevel); } export function LinearColorAtSeriesLevel() { return generateCharts(data.linearColorAtSeriesLevel, { colorType: 'linear', colorDomain: [0, 9], colorRange: CONTINUOUS_COLOR_RANGE }); } export function CategoryColorAtSeriesLevel() { return generateCharts(data.categoryColorAtSeriesLevel, { colorType: 'category', colorDomain: [0, 1, 2], colorRange: EXTENDED_DISCRETE_COLOR_RANGE }); } export function LiteralColorAtMarkLevel() { return generateCharts(data.literalColorAtMarkLevel, {colorType: 'literal'}); } export function CategoryColorAtMarkLevel() { return generateCharts(data.categoryColorAtMarkLevel, {colorType: 'category'}); } export function CategoryColorAtMarkLevelCustomPalette() { return generateCharts(data.categoryColorAtMarkLevel, { colorType: 'category', colorRange: colors }); } export function CategoryColorAtMarkLevelFixedStroke() { return generateCharts(data.categoryColorAtMarkLevel, { colorType: 'category', stroke: '#f70' }); } export function LinearColorAtMarkLevelNoPalette() { return generateCharts(data.linearColorAtMarkLevel); } export function LinearColorAtMarkLevel() { return generateCharts(data.linearColorAtMarkLevel, { colorRange: ['#c7e9c0', '#00441b'] }); } export function LineSeriesMarkSeries() { return ( {data.noColor.map((d, i) => ( ))} {data.noColor.map((d, i) => ( ))} ); } export function GradientCharts() { const gradient = ( ); return (
{gradient} {gradient} {gradient}
); } export function ColorSpecificity() { const accentColor = '#FF9833'; const seventhElementColored = [...data.noColor[2].data]; seventhElementColored[6].color = accentColor; return (
); } export function ReactVis5() { return generatePalette(DISCRETE_COLOR_RANGE); } export function ReactVis20() { return generatePalette(EXTENDED_DISCRETE_COLOR_RANGE); } export function Continuous() { return generatePalette(CONTINUOUS_COLOR_RANGE); } export function CustomPalette() { return generatePalette(colors); } function generatePalette(range) { return (
{range.map((d, i) => (
{d}
))}
); } function generateCharts(seriesData, props) { return (
{[VerticalBarSeries, LineSeries, MarkSeries].map((Type, key) => ( {seriesData.map((d, i) => ( ))} ))}
); } ================================================ FILE: packages/showcase/data/mini-data-examples.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, LineSeries, MarkSeries, VerticalBarSeries} from 'react-vis'; const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2}, {x: 9, y: 0} ]; const defaultProps = { width: 200, height: 200, margin: {top: 5, left: 5, right: 5, bottom: 5} }; export function MiniCharts() { return (
); } ================================================ FILE: packages/showcase/datasets/car-data.json ================================================ [ { "name": "AMC Ambassador Brougham", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "360", "power (hp)": "175", "weight (lb)": "3821", "0-60 mph (s)": "11", "year": "73" }, { "name": "AMC Ambassador DPL", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "390", "power (hp)": "190", "weight (lb)": "3850", "0-60 mph (s)": "8.5", "year": "70" }, { "name": "AMC Ambassador SST", "economy (mpg)": "17", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "150", "weight (lb)": "3672", "0-60 mph (s)": "11.5", "year": "72" }, { "name": "AMC Concord DL 6", "economy (mpg)": "20.2", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "90", "weight (lb)": "3265", "0-60 mph (s)": "18.2", "year": "79" }, { "name": "AMC Concord DL", "economy (mpg)": "18.1", "cylinders": "6", "displacement (cc)": "258", "power (hp)": "120", "weight (lb)": "3410", "0-60 mph (s)": "15.1", "year": "78" }, { "name": "AMC Concord DL", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "", "weight (lb)": "3035", "0-60 mph (s)": "20.5", "year": "82" }, { "name": "AMC Concord", "economy (mpg)": "19.4", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "90", "weight (lb)": "3210", "0-60 mph (s)": "17.2", "year": "78" }, { "name": "AMC Concord", "economy (mpg)": "24.3", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "3003", "0-60 mph (s)": "20.1", "year": "80" }, { "name": "AMC Gremlin", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "2789", "0-60 mph (s)": "15", "year": "73" }, { "name": "AMC Gremlin", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "2634", "0-60 mph (s)": "13", "year": "71" }, { "name": "AMC Gremlin", "economy (mpg)": "20", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "2914", "0-60 mph (s)": "16", "year": "75" }, { "name": "AMC Gremlin", "economy (mpg)": "21", "cylinders": "6", "displacement (cc)": "199", "power (hp)": "90", "weight (lb)": "2648", "0-60 mph (s)": "15", "year": "70" }, { "name": "AMC Hornet Sportabout (Wagon)", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "258", "power (hp)": "110", "weight (lb)": "2962", "0-60 mph (s)": "13.5", "year": "71" }, { "name": "AMC Hornet", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "199", "power (hp)": "97", "weight (lb)": "2774", "0-60 mph (s)": "15.5", "year": "70" }, { "name": "AMC Hornet", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "2945", "0-60 mph (s)": "16", "year": "73" }, { "name": "AMC Hornet", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "2901", "0-60 mph (s)": "16", "year": "74" }, { "name": "AMC Hornet", "economy (mpg)": "22.5", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "90", "weight (lb)": "3085", "0-60 mph (s)": "17.6", "year": "76" }, { "name": "AMC Matador (Wagon)", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "150", "weight (lb)": "4257", "0-60 mph (s)": "15.5", "year": "74" }, { "name": "AMC Matador (Wagon)", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "150", "weight (lb)": "3892", "0-60 mph (s)": "12.5", "year": "72" }, { "name": "AMC Matador", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "150", "weight (lb)": "3672", "0-60 mph (s)": "11.5", "year": "73" }, { "name": "AMC Matador", "economy (mpg)": "15", "cylinders": "6", "displacement (cc)": "258", "power (hp)": "110", "weight (lb)": "3730", "0-60 mph (s)": "19", "year": "75" }, { "name": "AMC Matador", "economy (mpg)": "15.5", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "120", "weight (lb)": "3962", "0-60 mph (s)": "13.9", "year": "76" }, { "name": "AMC Matador", "economy (mpg)": "16", "cylinders": "6", "displacement (cc)": "258", "power (hp)": "110", "weight (lb)": "3632", "0-60 mph (s)": "18", "year": "74" }, { "name": "AMC Matador", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "100", "weight (lb)": "3288", "0-60 mph (s)": "15.5", "year": "71" }, { "name": "AMC Pacer D/L", "economy (mpg)": "17.5", "cylinders": "6", "displacement (cc)": "258", "power (hp)": "95", "weight (lb)": "3193", "0-60 mph (s)": "17.8", "year": "76" }, { "name": "AMC Pacer", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "90", "weight (lb)": "3211", "0-60 mph (s)": "17", "year": "75" }, { "name": "AMC Rebel SST (Wagon)", "economy (mpg)": "", "cylinders": "8", "displacement (cc)": "360", "power (hp)": "175", "weight (lb)": "3850", "0-60 mph (s)": "11", "year": "70" }, { "name": "AMC Rebel SST", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "150", "weight (lb)": "3433", "0-60 mph (s)": "12", "year": "70" }, { "name": "AMC Spirit DL", "economy (mpg)": "27.4", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "80", "weight (lb)": "2670", "0-60 mph (s)": "15", "year": "79" }, { "name": "Audi 100 LS", "economy (mpg)": "20", "cylinders": "4", "displacement (cc)": "114", "power (hp)": "91", "weight (lb)": "2582", "0-60 mph (s)": "14", "year": "73" }, { "name": "Audi 100 LS", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "115", "power (hp)": "95", "weight (lb)": "2694", "0-60 mph (s)": "15", "year": "75" }, { "name": "Audi 100 LS", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "107", "power (hp)": "90", "weight (lb)": "2430", "0-60 mph (s)": "14.5", "year": "70" }, { "name": "Audi 4000", "economy (mpg)": "34.3", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "78", "weight (lb)": "2188", "0-60 mph (s)": "15.8", "year": "80" }, { "name": "Audi 5000", "economy (mpg)": "20.3", "cylinders": "5", "displacement (cc)": "131", "power (hp)": "103", "weight (lb)": "2830", "0-60 mph (s)": "15.9", "year": "78" }, { "name": "Audi 5000S (Diesel)", "economy (mpg)": "36.4", "cylinders": "5", "displacement (cc)": "121", "power (hp)": "67", "weight (lb)": "2950", "0-60 mph (s)": "19.9", "year": "80" }, { "name": "Audi Fox", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "83", "weight (lb)": "2219", "0-60 mph (s)": "16.5", "year": "74" }, { "name": "BMW 2002", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "113", "weight (lb)": "2234", "0-60 mph (s)": "12.5", "year": "70" }, { "name": "BMW 320i", "economy (mpg)": "21.5", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "110", "weight (lb)": "2600", "0-60 mph (s)": "12.8", "year": "77" }, { "name": "Buick Century 350", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "175", "weight (lb)": "4100", "0-60 mph (s)": "13", "year": "73" }, { "name": "Buick Century Limited", "economy (mpg)": "25", "cylinders": "6", "displacement (cc)": "181", "power (hp)": "110", "weight (lb)": "2945", "0-60 mph (s)": "16.4", "year": "82" }, { "name": "Buick Century Luxus (Wagon)", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "150", "weight (lb)": "4699", "0-60 mph (s)": "14.5", "year": "74" }, { "name": "Buick Century Special", "economy (mpg)": "20.6", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "105", "weight (lb)": "3380", "0-60 mph (s)": "15.8", "year": "78" }, { "name": "Buick Century", "economy (mpg)": "17", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "110", "weight (lb)": "3907", "0-60 mph (s)": "21", "year": "75" }, { "name": "Buick Century", "economy (mpg)": "22.4", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "110", "weight (lb)": "3415", "0-60 mph (s)": "15.8", "year": "81" }, { "name": "Buick Electra 225 Custom", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "455", "power (hp)": "225", "weight (lb)": "4951", "0-60 mph (s)": "11", "year": "73" }, { "name": "Buick Estate Wagon (Wagon)", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "455", "power (hp)": "225", "weight (lb)": "3086", "0-60 mph (s)": "10", "year": "70" }, { "name": "Buick Estate Wagon (Wagon)", "economy (mpg)": "16.9", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "155", "weight (lb)": "4360", "0-60 mph (s)": "14.9", "year": "79" }, { "name": "Buick Lesabre Custom", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "155", "weight (lb)": "4502", "0-60 mph (s)": "13.5", "year": "72" }, { "name": "Buick Opel Isuzu Deluxe", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "111", "power (hp)": "80", "weight (lb)": "2155", "0-60 mph (s)": "14.8", "year": "77" }, { "name": "Buick Regal Sport Coupe (Turbo)", "economy (mpg)": "17.7", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "165", "weight (lb)": "3445", "0-60 mph (s)": "13.4", "year": "78" }, { "name": "Buick Skyhawk", "economy (mpg)": "21", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "110", "weight (lb)": "3039", "0-60 mph (s)": "15", "year": "75" }, { "name": "Buick Skylark 320", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "165", "weight (lb)": "3693", "0-60 mph (s)": "11.5", "year": "70" }, { "name": "Buick Skylark Limited", "economy (mpg)": "28.4", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "2670", "0-60 mph (s)": "16", "year": "79" }, { "name": "Buick Skylark", "economy (mpg)": "20.5", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "105", "weight (lb)": "3425", "0-60 mph (s)": "16.9", "year": "77" }, { "name": "Buick Skylark", "economy (mpg)": "26.6", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "84", "weight (lb)": "2635", "0-60 mph (s)": "16.4", "year": "81" }, { "name": "Cadillac Eldorado", "economy (mpg)": "23", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "125", "weight (lb)": "3900", "0-60 mph (s)": "17.4", "year": "79" }, { "name": "Cadillac Seville", "economy (mpg)": "16.5", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "180", "weight (lb)": "4380", "0-60 mph (s)": "12.1", "year": "76" }, { "name": "Chevroelt Chevelle Malibu", "economy (mpg)": "16", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "105", "weight (lb)": "3897", "0-60 mph (s)": "18.5", "year": "75" }, { "name": "Chevrolet Bel Air", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "145", "weight (lb)": "4440", "0-60 mph (s)": "14", "year": "75" }, { "name": "Chevrolet Camaro", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "2950", "0-60 mph (s)": "17.3", "year": "82" }, { "name": "Chevrolet Caprice Classic", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "150", "weight (lb)": "4464", "0-60 mph (s)": "12", "year": "73" }, { "name": "Chevrolet Caprice Classic", "economy (mpg)": "17", "cylinders": "8", "displacement (cc)": "305", "power (hp)": "130", "weight (lb)": "3840", "0-60 mph (s)": "15.4", "year": "79" }, { "name": "Chevrolet Caprice Classic", "economy (mpg)": "17.5", "cylinders": "8", "displacement (cc)": "305", "power (hp)": "145", "weight (lb)": "3880", "0-60 mph (s)": "12.5", "year": "77" }, { "name": "Chevrolet Cavalier 2-Door", "economy (mpg)": "34", "cylinders": "4", "displacement (cc)": "112", "power (hp)": "88", "weight (lb)": "2395", "0-60 mph (s)": "18", "year": "82" }, { "name": "Chevrolet Cavalier Wagon", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "112", "power (hp)": "88", "weight (lb)": "2640", "0-60 mph (s)": "18.6", "year": "82" }, { "name": "Chevrolet Cavalier", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "112", "power (hp)": "88", "weight (lb)": "2605", "0-60 mph (s)": "19.6", "year": "82" }, { "name": "Chevrolet Chevelle Concours (Wagon)", "economy (mpg)": "", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "165", "weight (lb)": "4142", "0-60 mph (s)": "11.5", "year": "70" }, { "name": "Chevrolet Chevelle Concours (Wagon)", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "307", "power (hp)": "130", "weight (lb)": "4098", "0-60 mph (s)": "14", "year": "72" }, { "name": "Chevrolet Chevelle Malibu Classic", "economy (mpg)": "16", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "100", "weight (lb)": "3781", "0-60 mph (s)": "17", "year": "74" }, { "name": "Chevrolet Chevelle Malibu Classic", "economy (mpg)": "17.5", "cylinders": "8", "displacement (cc)": "305", "power (hp)": "140", "weight (lb)": "4215", "0-60 mph (s)": "13", "year": "76" }, { "name": "Chevrolet Chevelle Malibu", "economy (mpg)": "17", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "100", "weight (lb)": "3329", "0-60 mph (s)": "15.5", "year": "71" }, { "name": "Chevrolet Chevelle Malibu", "economy (mpg)": "18", "cylinders": "8", "displacement (cc)": "307", "power (hp)": "130", "weight (lb)": "3504", "0-60 mph (s)": "12", "year": "70" }, { "name": "Chevrolet Chevette", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "52", "weight (lb)": "2035", "0-60 mph (s)": "22.2", "year": "76" }, { "name": "Chevrolet Chevette", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "68", "weight (lb)": "2155", "0-60 mph (s)": "16.5", "year": "78" }, { "name": "Chevrolet Chevette", "economy (mpg)": "30.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "63", "weight (lb)": "2051", "0-60 mph (s)": "17", "year": "77" }, { "name": "Chevrolet Chevette", "economy (mpg)": "32.1", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "70", "weight (lb)": "2120", "0-60 mph (s)": "15.5", "year": "80" }, { "name": "Chevrolet Citation", "economy (mpg)": "23.5", "cylinders": "6", "displacement (cc)": "173", "power (hp)": "110", "weight (lb)": "2725", "0-60 mph (s)": "12.6", "year": "81" }, { "name": "Chevrolet Citation", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "2678", "0-60 mph (s)": "16.5", "year": "80" }, { "name": "Chevrolet Citation", "economy (mpg)": "28.8", "cylinders": "6", "displacement (cc)": "173", "power (hp)": "115", "weight (lb)": "2595", "0-60 mph (s)": "11.3", "year": "79" }, { "name": "Chevrolet Concours", "economy (mpg)": "17.5", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "110", "weight (lb)": "3520", "0-60 mph (s)": "16.4", "year": "77" }, { "name": "Chevrolet Impala", "economy (mpg)": "11", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "150", "weight (lb)": "4997", "0-60 mph (s)": "14", "year": "73" }, { "name": "Chevrolet Impala", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "165", "weight (lb)": "4274", "0-60 mph (s)": "12", "year": "72" }, { "name": "Chevrolet Impala", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "165", "weight (lb)": "4209", "0-60 mph (s)": "12", "year": "71" }, { "name": "Chevrolet Impala", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "454", "power (hp)": "220", "weight (lb)": "4354", "0-60 mph (s)": "9", "year": "70" }, { "name": "Chevrolet Malibu Classic (Wagon)", "economy (mpg)": "19.2", "cylinders": "8", "displacement (cc)": "267", "power (hp)": "125", "weight (lb)": "3605", "0-60 mph (s)": "15", "year": "79" }, { "name": "Chevrolet Malibu", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "145", "weight (lb)": "3988", "0-60 mph (s)": "13", "year": "73" }, { "name": "Chevrolet Malibu", "economy (mpg)": "20.5", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "95", "weight (lb)": "3155", "0-60 mph (s)": "18.2", "year": "78" }, { "name": "Chevrolet Monte Carlo Landau", "economy (mpg)": "15.5", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "170", "weight (lb)": "4165", "0-60 mph (s)": "11.4", "year": "77" }, { "name": "Chevrolet Monte Carlo Landau", "economy (mpg)": "19.2", "cylinders": "8", "displacement (cc)": "305", "power (hp)": "145", "weight (lb)": "3425", "0-60 mph (s)": "13.2", "year": "78" }, { "name": "Chevrolet Monte Carlo S", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "145", "weight (lb)": "4082", "0-60 mph (s)": "13", "year": "73" }, { "name": "Chevrolet Monte Carlo", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "150", "weight (lb)": "3761", "0-60 mph (s)": "9.5", "year": "70" }, { "name": "Chevrolet Monza 2+2", "economy (mpg)": "20", "cylinders": "8", "displacement (cc)": "262", "power (hp)": "110", "weight (lb)": "3221", "0-60 mph (s)": "13.5", "year": "75" }, { "name": "Chevrolet Nova Custom", "economy (mpg)": "16", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "100", "weight (lb)": "3278", "0-60 mph (s)": "18", "year": "73" }, { "name": "Chevrolet Nova", "economy (mpg)": "15", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "100", "weight (lb)": "3336", "0-60 mph (s)": "17", "year": "74" }, { "name": "Chevrolet Nova", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "105", "weight (lb)": "3459", "0-60 mph (s)": "16", "year": "75" }, { "name": "Chevrolet Nova", "economy (mpg)": "22", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "105", "weight (lb)": "3353", "0-60 mph (s)": "14.5", "year": "76" }, { "name": "Chevrolet Vega (Wagon)", "economy (mpg)": "22", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "72", "weight (lb)": "2408", "0-60 mph (s)": "19", "year": "71" }, { "name": "Chevrolet Vega 2300", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "90", "weight (lb)": "2264", "0-60 mph (s)": "15.5", "year": "71" }, { "name": "Chevrolet Vega", "economy (mpg)": "20", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "90", "weight (lb)": "2408", "0-60 mph (s)": "19.5", "year": "72" }, { "name": "Chevrolet Vega", "economy (mpg)": "21", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "72", "weight (lb)": "2401", "0-60 mph (s)": "19.5", "year": "73" }, { "name": "Chevrolet Vega", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "75", "weight (lb)": "2542", "0-60 mph (s)": "17", "year": "74" }, { "name": "Chevrolet Woody", "economy (mpg)": "24.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "60", "weight (lb)": "2164", "0-60 mph (s)": "22.1", "year": "76" }, { "name": "Chevy C10", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "145", "weight (lb)": "4055", "0-60 mph (s)": "12", "year": "76" }, { "name": "Chevy C20", "economy (mpg)": "10", "cylinders": "8", "displacement (cc)": "307", "power (hp)": "200", "weight (lb)": "4376", "0-60 mph (s)": "15", "year": "70" }, { "name": "Chevy S-10", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "82", "weight (lb)": "2720", "0-60 mph (s)": "19.4", "year": "82" }, { "name": "Chrysler Cordoba", "economy (mpg)": "15.5", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "190", "weight (lb)": "4325", "0-60 mph (s)": "12.2", "year": "77" }, { "name": "Chrysler Lebaron Medallion", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "156", "power (hp)": "92", "weight (lb)": "2585", "0-60 mph (s)": "14.5", "year": "82" }, { "name": "Chrysler Lebaron Salon", "economy (mpg)": "17.6", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "85", "weight (lb)": "3465", "0-60 mph (s)": "16.6", "year": "81" }, { "name": "Chrysler Lebaron Town & Country (Wagon)", "economy (mpg)": "18.5", "cylinders": "8", "displacement (cc)": "360", "power (hp)": "150", "weight (lb)": "3940", "0-60 mph (s)": "13", "year": "79" }, { "name": "Chrysler New Yorker Brougham", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "440", "power (hp)": "215", "weight (lb)": "4735", "0-60 mph (s)": "11", "year": "73" }, { "name": "Chrysler Newport Royal", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "190", "weight (lb)": "4422", "0-60 mph (s)": "12.5", "year": "72" }, { "name": "Citroen DS-21 Pallas", "economy (mpg)": "", "cylinders": "4", "displacement (cc)": "133", "power (hp)": "115", "weight (lb)": "3090", "0-60 mph (s)": "17.5", "year": "70" }, { "name": "Datsun 1200", "economy (mpg)": "35", "cylinders": "4", "displacement (cc)": "72", "power (hp)": "69", "weight (lb)": "1613", "0-60 mph (s)": "18", "year": "71" }, { "name": "Datsun 200SX", "economy (mpg)": "23.9", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "97", "weight (lb)": "2405", "0-60 mph (s)": "14.9", "year": "78" }, { "name": "Datsun 200SX", "economy (mpg)": "32.9", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "100", "weight (lb)": "2615", "0-60 mph (s)": "14.8", "year": "81" }, { "name": "Datsun 210", "economy (mpg)": "31.8", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "65", "weight (lb)": "2020", "0-60 mph (s)": "19.2", "year": "79" }, { "name": "Datsun 210", "economy (mpg)": "37", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "65", "weight (lb)": "1975", "0-60 mph (s)": "19.4", "year": "81" }, { "name": "Datsun 210", "economy (mpg)": "40.8", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "65", "weight (lb)": "2110", "0-60 mph (s)": "19.2", "year": "80" }, { "name": "Datsun 280ZX", "economy (mpg)": "32.7", "cylinders": "6", "displacement (cc)": "168", "power (hp)": "132", "weight (lb)": "2910", "0-60 mph (s)": "11.4", "year": "80" }, { "name": "Datsun 310 GX", "economy (mpg)": "38", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "67", "weight (lb)": "1995", "0-60 mph (s)": "16.2", "year": "82" }, { "name": "Datsun 310", "economy (mpg)": "37.2", "cylinders": "4", "displacement (cc)": "86", "power (hp)": "65", "weight (lb)": "2019", "0-60 mph (s)": "16.4", "year": "80" }, { "name": "Datsun 510 (Wagon)", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "92", "weight (lb)": "2288", "0-60 mph (s)": "17", "year": "72" }, { "name": "Datsun 510 Hatchback", "economy (mpg)": "37", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "92", "weight (lb)": "2434", "0-60 mph (s)": "15", "year": "80" }, { "name": "Datsun 510", "economy (mpg)": "27.2", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "97", "weight (lb)": "2300", "0-60 mph (s)": "14.7", "year": "78" }, { "name": "Datsun 610", "economy (mpg)": "22", "cylinders": "4", "displacement (cc)": "108", "power (hp)": "94", "weight (lb)": "2379", "0-60 mph (s)": "16.5", "year": "73" }, { "name": "Datsun 710", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "119", "power (hp)": "97", "weight (lb)": "2545", "0-60 mph (s)": "17", "year": "75" }, { "name": "Datsun 710", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "83", "power (hp)": "61", "weight (lb)": "2003", "0-60 mph (s)": "19", "year": "74" }, { "name": "Datsun 810 Maxima", "economy (mpg)": "24.2", "cylinders": "6", "displacement (cc)": "146", "power (hp)": "120", "weight (lb)": "2930", "0-60 mph (s)": "13.8", "year": "81" }, { "name": "Datsun 810", "economy (mpg)": "22", "cylinders": "6", "displacement (cc)": "146", "power (hp)": "97", "weight (lb)": "2815", "0-60 mph (s)": "14.5", "year": "77" }, { "name": "Datsun B-210", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "70", "weight (lb)": "1990", "0-60 mph (s)": "17", "year": "76" }, { "name": "Datsun B210 GX", "economy (mpg)": "39.4", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "70", "weight (lb)": "2070", "0-60 mph (s)": "18.6", "year": "78" }, { "name": "Datsun B210", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "67", "weight (lb)": "1950", "0-60 mph (s)": "19", "year": "74" }, { "name": "Datsun F-10 Hatchback", "economy (mpg)": "33.5", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "70", "weight (lb)": "1945", "0-60 mph (s)": "16.8", "year": "77" }, { "name": "Datsun PL510", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "88", "weight (lb)": "2130", "0-60 mph (s)": "14.5", "year": "70" }, { "name": "Datsun PL510", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "88", "weight (lb)": "2130", "0-60 mph (s)": "14.5", "year": "71" }, { "name": "Dodge Aries SE", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "135", "power (hp)": "84", "weight (lb)": "2525", "0-60 mph (s)": "16", "year": "82" }, { "name": "Dodge Aries Wagon (Wagon)", "economy (mpg)": "25.8", "cylinders": "4", "displacement (cc)": "156", "power (hp)": "92", "weight (lb)": "2620", "0-60 mph (s)": "14.4", "year": "81" }, { "name": "Dodge Aspen 6", "economy (mpg)": "20.6", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "110", "weight (lb)": "3360", "0-60 mph (s)": "16.6", "year": "79" }, { "name": "Dodge Aspen SE", "economy (mpg)": "20", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "100", "weight (lb)": "3651", "0-60 mph (s)": "17.7", "year": "76" }, { "name": "Dodge Aspen", "economy (mpg)": "18.6", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "110", "weight (lb)": "3620", "0-60 mph (s)": "18.7", "year": "78" }, { "name": "Dodge Aspen", "economy (mpg)": "19.1", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "90", "weight (lb)": "3381", "0-60 mph (s)": "18.7", "year": "80" }, { "name": "Dodge Challenger SE", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "383", "power (hp)": "170", "weight (lb)": "3563", "0-60 mph (s)": "10", "year": "70" }, { "name": "Dodge Charger 2.2", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "135", "power (hp)": "84", "weight (lb)": "2370", "0-60 mph (s)": "13", "year": "82" }, { "name": "Dodge Colt (Wagon)", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "80", "weight (lb)": "2164", "0-60 mph (s)": "15", "year": "72" }, { "name": "Dodge Colt Hardtop", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "97.5", "power (hp)": "80", "weight (lb)": "2126", "0-60 mph (s)": "17", "year": "72" }, { "name": "Dodge Colt Hatchback Custom", "economy (mpg)": "35.7", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "80", "weight (lb)": "1915", "0-60 mph (s)": "14.4", "year": "79" }, { "name": "Dodge Colt M/M", "economy (mpg)": "33.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "83", "weight (lb)": "2075", "0-60 mph (s)": "15.9", "year": "77" }, { "name": "Dodge Colt", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "79", "weight (lb)": "2255", "0-60 mph (s)": "17.7", "year": "76" }, { "name": "Dodge Colt", "economy (mpg)": "27.9", "cylinders": "4", "displacement (cc)": "156", "power (hp)": "105", "weight (lb)": "2800", "0-60 mph (s)": "14.4", "year": "80" }, { "name": "Dodge Colt", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "75", "weight (lb)": "2125", "0-60 mph (s)": "14.5", "year": "74" }, { "name": "Dodge Coronet Brougham", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4190", "0-60 mph (s)": "13", "year": "76" }, { "name": "Dodge Coronet Custom (Wagon)", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4457", "0-60 mph (s)": "13.5", "year": "74" }, { "name": "Dodge Coronet Custom", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "3777", "0-60 mph (s)": "12.5", "year": "73" }, { "name": "Dodge D100", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "3755", "0-60 mph (s)": "14", "year": "76" }, { "name": "Dodge D200", "economy (mpg)": "11", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "210", "weight (lb)": "4382", "0-60 mph (s)": "13.5", "year": "70" }, { "name": "Dodge Dart Custom", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "3399", "0-60 mph (s)": "11", "year": "73" }, { "name": "Dodge Diplomat", "economy (mpg)": "19.4", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "140", "weight (lb)": "3735", "0-60 mph (s)": "13.2", "year": "78" }, { "name": "Dodge Magnum XE", "economy (mpg)": "17.5", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "140", "weight (lb)": "4080", "0-60 mph (s)": "13.7", "year": "78" }, { "name": "Dodge Monaco (Wagon)", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "383", "power (hp)": "180", "weight (lb)": "4955", "0-60 mph (s)": "11.5", "year": "71" }, { "name": "Dodge Monaco Brougham", "economy (mpg)": "15.5", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "145", "weight (lb)": "4140", "0-60 mph (s)": "13.7", "year": "77" }, { "name": "Dodge Omni", "economy (mpg)": "30.9", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "75", "weight (lb)": "2230", "0-60 mph (s)": "14.5", "year": "78" }, { "name": "Dodge Rampage", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "135", "power (hp)": "84", "weight (lb)": "2295", "0-60 mph (s)": "11.6", "year": "82" }, { "name": "Dodge St. Regis", "economy (mpg)": "18.2", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "135", "weight (lb)": "3830", "0-60 mph (s)": "15.2", "year": "79" }, { "name": "Fiat 124 Sport Coupe", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "90", "weight (lb)": "2265", "0-60 mph (s)": "15.5", "year": "73" }, { "name": "Fiat 124 TC", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "116", "power (hp)": "75", "weight (lb)": "2246", "0-60 mph (s)": "14", "year": "74" }, { "name": "Fiat 124B", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "88", "power (hp)": "76", "weight (lb)": "2065", "0-60 mph (s)": "14.5", "year": "71" }, { "name": "Fiat 128", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "75", "weight (lb)": "2108", "0-60 mph (s)": "15.5", "year": "74" }, { "name": "Fiat 128", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "68", "power (hp)": "49", "weight (lb)": "1867", "0-60 mph (s)": "19.5", "year": "73" }, { "name": "Fiat 131", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "107", "power (hp)": "86", "weight (lb)": "2464", "0-60 mph (s)": "15.5", "year": "76" }, { "name": "Fiat Strada Custom", "economy (mpg)": "37.3", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "69", "weight (lb)": "2130", "0-60 mph (s)": "14.7", "year": "79" }, { "name": "Fiat X1.9", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "67", "weight (lb)": "2000", "0-60 mph (s)": "16", "year": "74" }, { "name": "Ford Capri II", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "92", "weight (lb)": "2572", "0-60 mph (s)": "14.9", "year": "76" }, { "name": "Ford Country Squire (Wagon)", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "170", "weight (lb)": "4746", "0-60 mph (s)": "12", "year": "71" }, { "name": "Ford Country Squire (Wagon)", "economy (mpg)": "15.5", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "142", "weight (lb)": "4054", "0-60 mph (s)": "14.3", "year": "79" }, { "name": "Ford Country", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "167", "weight (lb)": "4906", "0-60 mph (s)": "12.5", "year": "73" }, { "name": "Ford Escort 2H", "economy (mpg)": "29.9", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "65", "weight (lb)": "2380", "0-60 mph (s)": "20.7", "year": "81" }, { "name": "Ford Escort 4W", "economy (mpg)": "34.4", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "65", "weight (lb)": "2045", "0-60 mph (s)": "16.2", "year": "81" }, { "name": "Ford F108", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "130", "weight (lb)": "3870", "0-60 mph (s)": "15", "year": "76" }, { "name": "Ford F250", "economy (mpg)": "10", "cylinders": "8", "displacement (cc)": "360", "power (hp)": "215", "weight (lb)": "4615", "0-60 mph (s)": "14", "year": "70" }, { "name": "Ford Fairmont (Auto)", "economy (mpg)": "20.2", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "85", "weight (lb)": "2965", "0-60 mph (s)": "15.8", "year": "78" }, { "name": "Ford Fairmont (Man)", "economy (mpg)": "25.1", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "88", "weight (lb)": "2720", "0-60 mph (s)": "15.4", "year": "78" }, { "name": "Ford Fairmont 4", "economy (mpg)": "22.3", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "88", "weight (lb)": "2890", "0-60 mph (s)": "17.3", "year": "79" }, { "name": "Ford Fairmont Futura", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "92", "weight (lb)": "2865", "0-60 mph (s)": "16.4", "year": "82" }, { "name": "Ford Fairmont", "economy (mpg)": "26.4", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "88", "weight (lb)": "2870", "0-60 mph (s)": "18.1", "year": "80" }, { "name": "Ford Fiesta", "economy (mpg)": "36.1", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "66", "weight (lb)": "1800", "0-60 mph (s)": "14.4", "year": "78" }, { "name": "Ford Futura", "economy (mpg)": "18.1", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "139", "weight (lb)": "3205", "0-60 mph (s)": "11.2", "year": "78" }, { "name": "Ford Galaxie 500", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "153", "weight (lb)": "4129", "0-60 mph (s)": "13", "year": "72" }, { "name": "Ford Galaxie 500", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "153", "weight (lb)": "4154", "0-60 mph (s)": "13.5", "year": "71" }, { "name": "Ford Galaxie 500", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "429", "power (hp)": "198", "weight (lb)": "4341", "0-60 mph (s)": "10", "year": "70" }, { "name": "Ford Gran Torino (Wagon)", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "140", "weight (lb)": "4294", "0-60 mph (s)": "16", "year": "72" }, { "name": "Ford Gran Torino (Wagon)", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "140", "weight (lb)": "4638", "0-60 mph (s)": "16", "year": "74" }, { "name": "Ford Gran Torino", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "137", "weight (lb)": "4042", "0-60 mph (s)": "14.5", "year": "73" }, { "name": "Ford Gran Torino", "economy (mpg)": "14.5", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "152", "weight (lb)": "4215", "0-60 mph (s)": "12.8", "year": "76" }, { "name": "Ford Gran Torino", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "140", "weight (lb)": "4141", "0-60 mph (s)": "14", "year": "74" }, { "name": "Ford Granada Ghia", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "78", "weight (lb)": "3574", "0-60 mph (s)": "21", "year": "76" }, { "name": "Ford Granada GL", "economy (mpg)": "20.2", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "88", "weight (lb)": "3060", "0-60 mph (s)": "17.1", "year": "81" }, { "name": "Ford Granada L", "economy (mpg)": "22", "cylinders": "6", "displacement (cc)": "232", "power (hp)": "112", "weight (lb)": "2835", "0-60 mph (s)": "14.7", "year": "82" }, { "name": "Ford Granada", "economy (mpg)": "18.5", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "98", "weight (lb)": "3525", "0-60 mph (s)": "19", "year": "77" }, { "name": "Ford LTD Landau", "economy (mpg)": "17.6", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "129", "weight (lb)": "3725", "0-60 mph (s)": "13.4", "year": "79" }, { "name": "Ford LTD", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "158", "weight (lb)": "4363", "0-60 mph (s)": "13", "year": "73" }, { "name": "Ford LTD", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "148", "weight (lb)": "4657", "0-60 mph (s)": "13.5", "year": "75" }, { "name": "Ford Maverick", "economy (mpg)": "15", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "72", "weight (lb)": "3158", "0-60 mph (s)": "19.5", "year": "75" }, { "name": "Ford Maverick", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "88", "weight (lb)": "3021", "0-60 mph (s)": "16.5", "year": "73" }, { "name": "Ford Maverick", "economy (mpg)": "21", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "", "weight (lb)": "2875", "0-60 mph (s)": "17", "year": "74" }, { "name": "Ford Maverick", "economy (mpg)": "21", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "85", "weight (lb)": "2587", "0-60 mph (s)": "16", "year": "70" }, { "name": "Ford Maverick", "economy (mpg)": "24", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "81", "weight (lb)": "3012", "0-60 mph (s)": "17.6", "year": "76" }, { "name": "Ford Mustang Boss 302", "economy (mpg)": "", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "140", "weight (lb)": "3353", "0-60 mph (s)": "8", "year": "70" }, { "name": "Ford Mustang Cobra", "economy (mpg)": "23.6", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "", "weight (lb)": "2905", "0-60 mph (s)": "14.3", "year": "80" }, { "name": "Ford Mustang GL", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "86", "weight (lb)": "2790", "0-60 mph (s)": "15.6", "year": "82" }, { "name": "Ford Mustang II 2+2", "economy (mpg)": "25.5", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "89", "weight (lb)": "2755", "0-60 mph (s)": "15.8", "year": "77" }, { "name": "Ford Mustang II", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "129", "weight (lb)": "3169", "0-60 mph (s)": "12", "year": "75" }, { "name": "Ford Mustang", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "88", "weight (lb)": "3139", "0-60 mph (s)": "14.5", "year": "71" }, { "name": "Ford Pinto (Wagon)", "economy (mpg)": "22", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "86", "weight (lb)": "2395", "0-60 mph (s)": "16", "year": "72" }, { "name": "Ford Pinto Runabout", "economy (mpg)": "21", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "86", "weight (lb)": "2226", "0-60 mph (s)": "16.5", "year": "72" }, { "name": "Ford Pinto", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "171", "power (hp)": "97", "weight (lb)": "2984", "0-60 mph (s)": "14.5", "year": "75" }, { "name": "Ford Pinto", "economy (mpg)": "19", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "85", "weight (lb)": "2310", "0-60 mph (s)": "18.5", "year": "73" }, { "name": "Ford Pinto", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "83", "weight (lb)": "2639", "0-60 mph (s)": "17", "year": "75" }, { "name": "Ford Pinto", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "", "weight (lb)": "2046", "0-60 mph (s)": "19", "year": "71" }, { "name": "Ford Pinto", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "80", "weight (lb)": "2451", "0-60 mph (s)": "16.5", "year": "74" }, { "name": "Ford Pinto", "economy (mpg)": "26.5", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "72", "weight (lb)": "2565", "0-60 mph (s)": "13.6", "year": "76" }, { "name": "Ford Ranger", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "79", "weight (lb)": "2625", "0-60 mph (s)": "18.6", "year": "82" }, { "name": "Ford Thunderbird", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "149", "weight (lb)": "4335", "0-60 mph (s)": "14.5", "year": "77" }, { "name": "Ford Torino (Wagon)", "economy (mpg)": "", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "153", "weight (lb)": "4034", "0-60 mph (s)": "11", "year": "70" }, { "name": "Ford Torino 500", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "88", "weight (lb)": "3302", "0-60 mph (s)": "15.5", "year": "71" }, { "name": "Ford Torino", "economy (mpg)": "17", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "140", "weight (lb)": "3449", "0-60 mph (s)": "10.5", "year": "70" }, { "name": "Hi 1200D", "economy (mpg)": "9", "cylinders": "8", "displacement (cc)": "304", "power (hp)": "193", "weight (lb)": "4732", "0-60 mph (s)": "18.5", "year": "70" }, { "name": "Honda Accord CVCC", "economy (mpg)": "31.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "68", "weight (lb)": "2045", "0-60 mph (s)": "18.5", "year": "77" }, { "name": "Honda Accord LX", "economy (mpg)": "29.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "68", "weight (lb)": "2135", "0-60 mph (s)": "16.6", "year": "78" }, { "name": "Honda Accord", "economy (mpg)": "32.4", "cylinders": "4", "displacement (cc)": "107", "power (hp)": "72", "weight (lb)": "2290", "0-60 mph (s)": "17", "year": "80" }, { "name": "Honda Accord", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "107", "power (hp)": "75", "weight (lb)": "2205", "0-60 mph (s)": "14.5", "year": "82" }, { "name": "Honda Civic (Auto)", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "67", "weight (lb)": "1965", "0-60 mph (s)": "15.7", "year": "82" }, { "name": "Honda Civic 1300", "economy (mpg)": "35.1", "cylinders": "4", "displacement (cc)": "81", "power (hp)": "60", "weight (lb)": "1760", "0-60 mph (s)": "16.1", "year": "81" }, { "name": "Honda Civic 1500 GL", "economy (mpg)": "44.6", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "67", "weight (lb)": "1850", "0-60 mph (s)": "13.8", "year": "80" }, { "name": "Honda Civic CVCC", "economy (mpg)": "33", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "53", "weight (lb)": "1795", "0-60 mph (s)": "17.5", "year": "75" }, { "name": "Honda Civic CVCC", "economy (mpg)": "36.1", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "60", "weight (lb)": "1800", "0-60 mph (s)": "16.4", "year": "78" }, { "name": "Honda Civic", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "97", "weight (lb)": "2489", "0-60 mph (s)": "15", "year": "74" }, { "name": "Honda Civic", "economy (mpg)": "33", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "53", "weight (lb)": "1795", "0-60 mph (s)": "17.4", "year": "76" }, { "name": "Honda Civic", "economy (mpg)": "38", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "67", "weight (lb)": "1965", "0-60 mph (s)": "15", "year": "82" }, { "name": "Honda Prelude", "economy (mpg)": "33.7", "cylinders": "4", "displacement (cc)": "107", "power (hp)": "75", "weight (lb)": "2210", "0-60 mph (s)": "14.4", "year": "81" }, { "name": "Maxda GLC Deluxe", "economy (mpg)": "34.1", "cylinders": "4", "displacement (cc)": "86", "power (hp)": "65", "weight (lb)": "1975", "0-60 mph (s)": "15.2", "year": "79" }, { "name": "Maxda RX-3", "economy (mpg)": "18", "cylinders": "3", "displacement (cc)": "70", "power (hp)": "90", "weight (lb)": "2124", "0-60 mph (s)": "13.5", "year": "73" }, { "name": "Mazda 626", "economy (mpg)": "31.3", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "75", "weight (lb)": "2542", "0-60 mph (s)": "17.5", "year": "80" }, { "name": "Mazda 626", "economy (mpg)": "31.6", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "74", "weight (lb)": "2635", "0-60 mph (s)": "18.3", "year": "81" }, { "name": "Mazda GLC 4", "economy (mpg)": "34.1", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "68", "weight (lb)": "1985", "0-60 mph (s)": "16", "year": "81" }, { "name": "Mazda GLC Custom L", "economy (mpg)": "37", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "68", "weight (lb)": "2025", "0-60 mph (s)": "18.2", "year": "82" }, { "name": "Mazda GLC Custom", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "68", "weight (lb)": "1970", "0-60 mph (s)": "17.6", "year": "82" }, { "name": "Mazda GLC Deluxe", "economy (mpg)": "32.8", "cylinders": "4", "displacement (cc)": "78", "power (hp)": "52", "weight (lb)": "1985", "0-60 mph (s)": "19.4", "year": "78" }, { "name": "Mazda GLC", "economy (mpg)": "46.6", "cylinders": "4", "displacement (cc)": "86", "power (hp)": "65", "weight (lb)": "2110", "0-60 mph (s)": "17.9", "year": "80" }, { "name": "Mazda RX-2 Coupe", "economy (mpg)": "19", "cylinders": "3", "displacement (cc)": "70", "power (hp)": "97", "weight (lb)": "2330", "0-60 mph (s)": "13.5", "year": "72" }, { "name": "Mazda RX-4", "economy (mpg)": "21.5", "cylinders": "3", "displacement (cc)": "80", "power (hp)": "110", "weight (lb)": "2720", "0-60 mph (s)": "13.5", "year": "77" }, { "name": "Mazda RX-7 Gs", "economy (mpg)": "23.7", "cylinders": "3", "displacement (cc)": "70", "power (hp)": "100", "weight (lb)": "2420", "0-60 mph (s)": "12.5", "year": "80" }, { "name": "Mercedes-Benz 240D", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "146", "power (hp)": "67", "weight (lb)": "3250", "0-60 mph (s)": "21.8", "year": "80" }, { "name": "Mercedes-Benz 280S", "economy (mpg)": "16.5", "cylinders": "6", "displacement (cc)": "168", "power (hp)": "120", "weight (lb)": "3820", "0-60 mph (s)": "16.7", "year": "76" }, { "name": "Mercedes-Benz 300D", "economy (mpg)": "25.4", "cylinders": "5", "displacement (cc)": "183", "power (hp)": "77", "weight (lb)": "3530", "0-60 mph (s)": "20.1", "year": "79" }, { "name": "Mercury Capri 2000", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "86", "weight (lb)": "2220", "0-60 mph (s)": "14", "year": "71" }, { "name": "Mercury Capri V6", "economy (mpg)": "21", "cylinders": "6", "displacement (cc)": "155", "power (hp)": "107", "weight (lb)": "2472", "0-60 mph (s)": "14", "year": "73" }, { "name": "Mercury Cougar Brougham", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "130", "weight (lb)": "4295", "0-60 mph (s)": "14.9", "year": "77" }, { "name": "Mercury Grand Marquis", "economy (mpg)": "16.5", "cylinders": "8", "displacement (cc)": "351", "power (hp)": "138", "weight (lb)": "3955", "0-60 mph (s)": "13.2", "year": "79" }, { "name": "Mercury Lynx L", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "70", "weight (lb)": "2125", "0-60 mph (s)": "17.3", "year": "82" }, { "name": "Mercury Marquis Brougham", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "429", "power (hp)": "198", "weight (lb)": "4952", "0-60 mph (s)": "11.5", "year": "73" }, { "name": "Mercury Marquis", "economy (mpg)": "11", "cylinders": "8", "displacement (cc)": "429", "power (hp)": "208", "weight (lb)": "4633", "0-60 mph (s)": "11", "year": "72" }, { "name": "Mercury Monarch Ghia", "economy (mpg)": "20.2", "cylinders": "8", "displacement (cc)": "302", "power (hp)": "139", "weight (lb)": "3570", "0-60 mph (s)": "12.8", "year": "78" }, { "name": "Mercury Monarch", "economy (mpg)": "15", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "72", "weight (lb)": "3432", "0-60 mph (s)": "21", "year": "75" }, { "name": "Mercury Zephyr 6", "economy (mpg)": "19.8", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "85", "weight (lb)": "2990", "0-60 mph (s)": "18.2", "year": "79" }, { "name": "Mercury Zephyr", "economy (mpg)": "20.8", "cylinders": "6", "displacement (cc)": "200", "power (hp)": "85", "weight (lb)": "3070", "0-60 mph (s)": "16.7", "year": "78" }, { "name": "Nissan Stanza XE", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "88", "weight (lb)": "2160", "0-60 mph (s)": "14.5", "year": "82" }, { "name": "Oldsmobile Cutlass Ciera (Diesel)", "economy (mpg)": "38", "cylinders": "6", "displacement (cc)": "262", "power (hp)": "85", "weight (lb)": "3015", "0-60 mph (s)": "17", "year": "82" }, { "name": "Oldsmobile Cutlass LS", "economy (mpg)": "26.6", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "105", "weight (lb)": "3725", "0-60 mph (s)": "19", "year": "81" }, { "name": "Oldsmobile Cutlass Salon Brougham", "economy (mpg)": "19.9", "cylinders": "8", "displacement (cc)": "260", "power (hp)": "110", "weight (lb)": "3365", "0-60 mph (s)": "15.5", "year": "78" }, { "name": "Oldsmobile Cutlass Salon Brougham", "economy (mpg)": "23.9", "cylinders": "8", "displacement (cc)": "260", "power (hp)": "90", "weight (lb)": "3420", "0-60 mph (s)": "22.2", "year": "79" }, { "name": "Oldsmobile Cutlass Supreme", "economy (mpg)": "17", "cylinders": "8", "displacement (cc)": "260", "power (hp)": "110", "weight (lb)": "4060", "0-60 mph (s)": "19", "year": "77" }, { "name": "Oldsmobile Delta 88 Royale", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "160", "weight (lb)": "4456", "0-60 mph (s)": "13.5", "year": "72" }, { "name": "Oldsmobile Omega Brougham", "economy (mpg)": "26.8", "cylinders": "6", "displacement (cc)": "173", "power (hp)": "115", "weight (lb)": "2700", "0-60 mph (s)": "12.9", "year": "79" }, { "name": "Oldsmobile Omega", "economy (mpg)": "11", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "180", "weight (lb)": "3664", "0-60 mph (s)": "11", "year": "73" }, { "name": "Oldsmobile Starfire SX", "economy (mpg)": "23.8", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "85", "weight (lb)": "2855", "0-60 mph (s)": "17.6", "year": "78" }, { "name": "Oldsmobile Vista Cruiser", "economy (mpg)": "12", "cylinders": "8", "displacement (cc)": "350", "power (hp)": "180", "weight (lb)": "4499", "0-60 mph (s)": "12.5", "year": "73" }, { "name": "Opel 1900", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "116", "power (hp)": "81", "weight (lb)": "2220", "0-60 mph (s)": "16.9", "year": "76" }, { "name": "Opel 1900", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "116", "power (hp)": "90", "weight (lb)": "2123", "0-60 mph (s)": "14", "year": "71" }, { "name": "Opel Manta", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "116", "power (hp)": "75", "weight (lb)": "2158", "0-60 mph (s)": "15.5", "year": "73" }, { "name": "Opel Manta", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "78", "weight (lb)": "2300", "0-60 mph (s)": "14.5", "year": "74" }, { "name": "Peugeot 304", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "70", "weight (lb)": "2074", "0-60 mph (s)": "19.5", "year": "71" }, { "name": "Peugeot 504 (Wagon)", "economy (mpg)": "21", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "87", "weight (lb)": "2979", "0-60 mph (s)": "19.5", "year": "72" }, { "name": "Peugeot 504", "economy (mpg)": "19", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "88", "weight (lb)": "3270", "0-60 mph (s)": "21.9", "year": "76" }, { "name": "Peugeot 504", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "88", "weight (lb)": "2957", "0-60 mph (s)": "17", "year": "75" }, { "name": "Peugeot 504", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "110", "power (hp)": "87", "weight (lb)": "2672", "0-60 mph (s)": "17.5", "year": "70" }, { "name": "Peugeot 504", "economy (mpg)": "27.2", "cylinders": "4", "displacement (cc)": "141", "power (hp)": "71", "weight (lb)": "3190", "0-60 mph (s)": "24.8", "year": "79" }, { "name": "Peugeot 505S Turbo Diesel", "economy (mpg)": "28.1", "cylinders": "4", "displacement (cc)": "141", "power (hp)": "80", "weight (lb)": "3230", "0-60 mph (s)": "20.4", "year": "81" }, { "name": "Peugeot 604SL", "economy (mpg)": "16.2", "cylinders": "6", "displacement (cc)": "163", "power (hp)": "133", "weight (lb)": "3410", "0-60 mph (s)": "15.8", "year": "78" }, { "name": "Plymouth Arrow GS", "economy (mpg)": "25.5", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "96", "weight (lb)": "2300", "0-60 mph (s)": "15.5", "year": "77" }, { "name": "Plymouth Barracuda 340", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "340", "power (hp)": "160", "weight (lb)": "3609", "0-60 mph (s)": "8", "year": "70" }, { "name": "Plymouth Champ", "economy (mpg)": "39", "cylinders": "4", "displacement (cc)": "86", "power (hp)": "64", "weight (lb)": "1875", "0-60 mph (s)": "16.4", "year": "81" }, { "name": "Plymouth Cricket", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "91", "power (hp)": "70", "weight (lb)": "1955", "0-60 mph (s)": "20.5", "year": "71" }, { "name": "Plymouth Custom Suburb", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "360", "power (hp)": "170", "weight (lb)": "4654", "0-60 mph (s)": "13", "year": "73" }, { "name": "Plymouth Duster", "economy (mpg)": "20", "cylinders": "6", "displacement (cc)": "198", "power (hp)": "95", "weight (lb)": "3102", "0-60 mph (s)": "16.5", "year": "74" }, { "name": "Plymouth Duster", "economy (mpg)": "22", "cylinders": "6", "displacement (cc)": "198", "power (hp)": "95", "weight (lb)": "2833", "0-60 mph (s)": "15.5", "year": "70" }, { "name": "Plymouth Duster", "economy (mpg)": "23", "cylinders": "6", "displacement (cc)": "198", "power (hp)": "95", "weight (lb)": "2904", "0-60 mph (s)": "16", "year": "73" }, { "name": "Plymouth Fury Gran Sedan", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4237", "0-60 mph (s)": "14.5", "year": "73" }, { "name": "Plymouth Fury III", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4096", "0-60 mph (s)": "13", "year": "71" }, { "name": "Plymouth Fury III", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "440", "power (hp)": "215", "weight (lb)": "4312", "0-60 mph (s)": "8.5", "year": "70" }, { "name": "Plymouth Fury III", "economy (mpg)": "15", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4135", "0-60 mph (s)": "13.5", "year": "72" }, { "name": "Plymouth Fury", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "95", "weight (lb)": "3785", "0-60 mph (s)": "19", "year": "75" }, { "name": "Plymouth Grand Fury", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4498", "0-60 mph (s)": "14.5", "year": "75" }, { "name": "Plymouth Horizon 4", "economy (mpg)": "34.7", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "63", "weight (lb)": "2215", "0-60 mph (s)": "14.9", "year": "81" }, { "name": "Plymouth Horizon Miser", "economy (mpg)": "38", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "63", "weight (lb)": "2125", "0-60 mph (s)": "14.7", "year": "82" }, { "name": "Plymouth Horizon TC3", "economy (mpg)": "34.5", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "70", "weight (lb)": "2150", "0-60 mph (s)": "14.9", "year": "79" }, { "name": "Plymouth Horizon", "economy (mpg)": "34.2", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "70", "weight (lb)": "2200", "0-60 mph (s)": "13.2", "year": "79" }, { "name": "Plymouth Reliant", "economy (mpg)": "27.2", "cylinders": "4", "displacement (cc)": "135", "power (hp)": "84", "weight (lb)": "2490", "0-60 mph (s)": "15.7", "year": "81" }, { "name": "Plymouth Reliant", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "135", "power (hp)": "84", "weight (lb)": "2385", "0-60 mph (s)": "12.9", "year": "81" }, { "name": "Plymouth Sapporo", "economy (mpg)": "23.2", "cylinders": "4", "displacement (cc)": "156", "power (hp)": "105", "weight (lb)": "2745", "0-60 mph (s)": "16.7", "year": "78" }, { "name": "Plymouth Satellite (Wagon)", "economy (mpg)": "", "cylinders": "8", "displacement (cc)": "383", "power (hp)": "175", "weight (lb)": "4166", "0-60 mph (s)": "10.5", "year": "70" }, { "name": "Plymouth Satellite Custom (Wagon)", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "4077", "0-60 mph (s)": "14", "year": "72" }, { "name": "Plymouth Satellite Custom", "economy (mpg)": "16", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "105", "weight (lb)": "3439", "0-60 mph (s)": "15.5", "year": "71" }, { "name": "Plymouth Satellite Sebring", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "105", "weight (lb)": "3613", "0-60 mph (s)": "16.5", "year": "74" }, { "name": "Plymouth Satellite", "economy (mpg)": "18", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "3436", "0-60 mph (s)": "11", "year": "70" }, { "name": "Plymouth Valiant Custom", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "95", "weight (lb)": "3264", "0-60 mph (s)": "16", "year": "75" }, { "name": "Plymouth Valiant", "economy (mpg)": "18", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "105", "weight (lb)": "3121", "0-60 mph (s)": "16.5", "year": "73" }, { "name": "Plymouth Valiant", "economy (mpg)": "22", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "100", "weight (lb)": "3233", "0-60 mph (s)": "15.4", "year": "76" }, { "name": "Plymouth Volare Custom", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "100", "weight (lb)": "3630", "0-60 mph (s)": "17.7", "year": "77" }, { "name": "Plymouth Volare Premier V8", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "318", "power (hp)": "150", "weight (lb)": "3940", "0-60 mph (s)": "13.2", "year": "76" }, { "name": "Plymouth Volare", "economy (mpg)": "20.5", "cylinders": "6", "displacement (cc)": "225", "power (hp)": "100", "weight (lb)": "3430", "0-60 mph (s)": "17.2", "year": "78" }, { "name": "Pontiac Astro", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "140", "power (hp)": "78", "weight (lb)": "2592", "0-60 mph (s)": "18.5", "year": "75" }, { "name": "Pontiac Catalina Brougham", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "175", "weight (lb)": "4464", "0-60 mph (s)": "11.5", "year": "71" }, { "name": "Pontiac Catalina", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "175", "weight (lb)": "4385", "0-60 mph (s)": "12", "year": "72" }, { "name": "Pontiac Catalina", "economy (mpg)": "14", "cylinders": "8", "displacement (cc)": "455", "power (hp)": "225", "weight (lb)": "4425", "0-60 mph (s)": "10", "year": "70" }, { "name": "Pontiac Catalina", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "170", "weight (lb)": "4668", "0-60 mph (s)": "11.5", "year": "75" }, { "name": "Pontiac Firebird", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "100", "weight (lb)": "3282", "0-60 mph (s)": "15", "year": "71" }, { "name": "Pontiac Grand Prix Lj", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "180", "weight (lb)": "4220", "0-60 mph (s)": "11.1", "year": "77" }, { "name": "Pontiac Grand Prix", "economy (mpg)": "16", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "230", "weight (lb)": "4278", "0-60 mph (s)": "9.5", "year": "73" }, { "name": "Pontiac J2000 Se Hatchback", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "112", "power (hp)": "85", "weight (lb)": "2575", "0-60 mph (s)": "16.2", "year": "82" }, { "name": "Pontiac Lemans V6", "economy (mpg)": "21.5", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "115", "weight (lb)": "3245", "0-60 mph (s)": "15.4", "year": "79" }, { "name": "Pontiac Phoenix LJ", "economy (mpg)": "19.2", "cylinders": "6", "displacement (cc)": "231", "power (hp)": "105", "weight (lb)": "3535", "0-60 mph (s)": "19.2", "year": "78" }, { "name": "Pontiac Phoenix", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "2735", "0-60 mph (s)": "18", "year": "82" }, { "name": "Pontiac Phoenix", "economy (mpg)": "33.5", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "90", "weight (lb)": "2556", "0-60 mph (s)": "13.2", "year": "79" }, { "name": "Pontiac Safari (Wagon)", "economy (mpg)": "13", "cylinders": "8", "displacement (cc)": "400", "power (hp)": "175", "weight (lb)": "5140", "0-60 mph (s)": "12", "year": "71" }, { "name": "Pontiac Sunbird Coupe", "economy (mpg)": "24.5", "cylinders": "4", "displacement (cc)": "151", "power (hp)": "88", "weight (lb)": "2740", "0-60 mph (s)": "16", "year": "77" }, { "name": "Pontiac Ventura Sj", "economy (mpg)": "18.5", "cylinders": "6", "displacement (cc)": "250", "power (hp)": "110", "weight (lb)": "3645", "0-60 mph (s)": "16.2", "year": "76" }, { "name": "Renault 12 (Wagon)", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "96", "power (hp)": "69", "weight (lb)": "2189", "0-60 mph (s)": "18", "year": "72" }, { "name": "Renault 12TL", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "101", "power (hp)": "83", "weight (lb)": "2202", "0-60 mph (s)": "15.3", "year": "76" }, { "name": "Renault 18I", "economy (mpg)": "34.5", "cylinders": "4", "displacement (cc)": "100", "power (hp)": "", "weight (lb)": "2320", "0-60 mph (s)": "15.8", "year": "81" }, { "name": "Renault 5 Gtl", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "58", "weight (lb)": "1825", "0-60 mph (s)": "18.6", "year": "77" }, { "name": "Renault Lecar Deluxe", "economy (mpg)": "40.9", "cylinders": "4", "displacement (cc)": "85", "power (hp)": "", "weight (lb)": "1835", "0-60 mph (s)": "17.3", "year": "80" }, { "name": "Saab 900S", "economy (mpg)": "", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "110", "weight (lb)": "2800", "0-60 mph (s)": "15.4", "year": "81" }, { "name": "Saab 99E", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "104", "power (hp)": "95", "weight (lb)": "2375", "0-60 mph (s)": "17.5", "year": "70" }, { "name": "Saab 99GLE", "economy (mpg)": "21.6", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "115", "weight (lb)": "2795", "0-60 mph (s)": "15.7", "year": "78" }, { "name": "Saab 99LE", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "110", "weight (lb)": "2660", "0-60 mph (s)": "14", "year": "73" }, { "name": "Saab 99LE", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "115", "weight (lb)": "2671", "0-60 mph (s)": "13.5", "year": "75" }, { "name": "Subaru DL", "economy (mpg)": "30", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "67", "weight (lb)": "1985", "0-60 mph (s)": "16.4", "year": "77" }, { "name": "Subaru DL", "economy (mpg)": "33.8", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "67", "weight (lb)": "2145", "0-60 mph (s)": "18", "year": "80" }, { "name": "Subaru", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "108", "power (hp)": "93", "weight (lb)": "2391", "0-60 mph (s)": "15.5", "year": "74" }, { "name": "Subaru", "economy (mpg)": "32.3", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "67", "weight (lb)": "2065", "0-60 mph (s)": "17.8", "year": "81" }, { "name": "Toyota Carina", "economy (mpg)": "20", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "88", "weight (lb)": "2279", "0-60 mph (s)": "19", "year": "73" }, { "name": "Toyota Celica GT Liftback", "economy (mpg)": "21.1", "cylinders": "4", "displacement (cc)": "134", "power (hp)": "95", "weight (lb)": "2515", "0-60 mph (s)": "14.8", "year": "78" }, { "name": "Toyota Celica GT", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "144", "power (hp)": "96", "weight (lb)": "2665", "0-60 mph (s)": "13.9", "year": "82" }, { "name": "Toyota Corolla 1200", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "71", "power (hp)": "65", "weight (lb)": "1773", "0-60 mph (s)": "19", "year": "71" }, { "name": "Toyota Corolla 1200", "economy (mpg)": "32", "cylinders": "4", "displacement (cc)": "71", "power (hp)": "65", "weight (lb)": "1836", "0-60 mph (s)": "21", "year": "74" }, { "name": "Toyota Corolla 1600 (Wagon)", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "88", "weight (lb)": "2100", "0-60 mph (s)": "16.5", "year": "72" }, { "name": "Toyota Corolla Liftback", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "75", "weight (lb)": "2265", "0-60 mph (s)": "18.2", "year": "77" }, { "name": "Toyota Corolla Tercel", "economy (mpg)": "38.1", "cylinders": "4", "displacement (cc)": "89", "power (hp)": "60", "weight (lb)": "1968", "0-60 mph (s)": "18.8", "year": "80" }, { "name": "Toyota Corolla", "economy (mpg)": "28", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "75", "weight (lb)": "2155", "0-60 mph (s)": "16.4", "year": "76" }, { "name": "Toyota Corolla", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "75", "weight (lb)": "2171", "0-60 mph (s)": "16", "year": "75" }, { "name": "Toyota Corolla", "economy (mpg)": "32.2", "cylinders": "4", "displacement (cc)": "108", "power (hp)": "75", "weight (lb)": "2265", "0-60 mph (s)": "15.2", "year": "80" }, { "name": "Toyota Corolla", "economy (mpg)": "32.4", "cylinders": "4", "displacement (cc)": "108", "power (hp)": "75", "weight (lb)": "2350", "0-60 mph (s)": "16.8", "year": "81" }, { "name": "Toyota Corolla", "economy (mpg)": "34", "cylinders": "4", "displacement (cc)": "108", "power (hp)": "70", "weight (lb)": "2245", "0-60 mph (s)": "16.9", "year": "82" }, { "name": "Toyota Corona Hardtop", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "113", "power (hp)": "95", "weight (lb)": "2278", "0-60 mph (s)": "15.5", "year": "72" }, { "name": "Toyota Corona Liftback", "economy (mpg)": "29.8", "cylinders": "4", "displacement (cc)": "134", "power (hp)": "90", "weight (lb)": "2711", "0-60 mph (s)": "15.5", "year": "80" }, { "name": "Toyota Corona Mark II", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "113", "power (hp)": "95", "weight (lb)": "2372", "0-60 mph (s)": "15", "year": "70" }, { "name": "Toyota Corona", "economy (mpg)": "24", "cylinders": "4", "displacement (cc)": "134", "power (hp)": "96", "weight (lb)": "2702", "0-60 mph (s)": "13.5", "year": "75" }, { "name": "Toyota Corona", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "113", "power (hp)": "95", "weight (lb)": "2228", "0-60 mph (s)": "14", "year": "71" }, { "name": "Toyota Corona", "economy (mpg)": "27.5", "cylinders": "4", "displacement (cc)": "134", "power (hp)": "95", "weight (lb)": "2560", "0-60 mph (s)": "14.2", "year": "78" }, { "name": "Toyota Corona", "economy (mpg)": "31", "cylinders": "4", "displacement (cc)": "76", "power (hp)": "52", "weight (lb)": "1649", "0-60 mph (s)": "16.5", "year": "74" }, { "name": "Toyota Cressida", "economy (mpg)": "25.4", "cylinders": "6", "displacement (cc)": "168", "power (hp)": "116", "weight (lb)": "2900", "0-60 mph (s)": "12.6", "year": "81" }, { "name": "Toyota Mark II", "economy (mpg)": "19", "cylinders": "6", "displacement (cc)": "156", "power (hp)": "108", "weight (lb)": "2930", "0-60 mph (s)": "15.5", "year": "76" }, { "name": "Toyota Mark II", "economy (mpg)": "20", "cylinders": "6", "displacement (cc)": "156", "power (hp)": "122", "weight (lb)": "2807", "0-60 mph (s)": "13.5", "year": "73" }, { "name": "Toyota Starlet", "economy (mpg)": "39.1", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "58", "weight (lb)": "1755", "0-60 mph (s)": "16.9", "year": "81" }, { "name": "Toyota Tercel", "economy (mpg)": "37.7", "cylinders": "4", "displacement (cc)": "89", "power (hp)": "62", "weight (lb)": "2050", "0-60 mph (s)": "17.3", "year": "81" }, { "name": "Toyouta Corona Mark II (Wagon)", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "120", "power (hp)": "97", "weight (lb)": "2506", "0-60 mph (s)": "14.5", "year": "72" }, { "name": "Triumph TR7 Coupe", "economy (mpg)": "35", "cylinders": "4", "displacement (cc)": "122", "power (hp)": "88", "weight (lb)": "2500", "0-60 mph (s)": "15.1", "year": "80" }, { "name": "Vokswagen Rabbit", "economy (mpg)": "29.8", "cylinders": "4", "displacement (cc)": "89", "power (hp)": "62", "weight (lb)": "1845", "0-60 mph (s)": "15.3", "year": "80" }, { "name": "Volkswagen 1131 Deluxe Sedan", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "46", "weight (lb)": "1835", "0-60 mph (s)": "20.5", "year": "70" }, { "name": "Volkswagen 411 (Wagon)", "economy (mpg)": "22", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "76", "weight (lb)": "2511", "0-60 mph (s)": "18", "year": "72" }, { "name": "Volkswagen Dasher (Diesel)", "economy (mpg)": "43.4", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "48", "weight (lb)": "2335", "0-60 mph (s)": "23.7", "year": "80" }, { "name": "Volkswagen Dasher", "economy (mpg)": "25", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "71", "weight (lb)": "2223", "0-60 mph (s)": "16.5", "year": "75" }, { "name": "Volkswagen Dasher", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "79", "power (hp)": "67", "weight (lb)": "1963", "0-60 mph (s)": "15.5", "year": "74" }, { "name": "Volkswagen Dasher", "economy (mpg)": "30.5", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "78", "weight (lb)": "2190", "0-60 mph (s)": "14.1", "year": "77" }, { "name": "Volkswagen Jetta", "economy (mpg)": "33", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "74", "weight (lb)": "2190", "0-60 mph (s)": "14.2", "year": "81" }, { "name": "Volkswagen Model 111", "economy (mpg)": "27", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "60", "weight (lb)": "1834", "0-60 mph (s)": "19", "year": "71" }, { "name": "Volkswagen Pickup", "economy (mpg)": "44", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "52", "weight (lb)": "2130", "0-60 mph (s)": "24.6", "year": "82" }, { "name": "Volkswagen Rabbit C (Diesel)", "economy (mpg)": "44.3", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "48", "weight (lb)": "2085", "0-60 mph (s)": "21.7", "year": "80" }, { "name": "Volkswagen Rabbit Custom Diesel", "economy (mpg)": "43.1", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "48", "weight (lb)": "1985", "0-60 mph (s)": "21.5", "year": "78" }, { "name": "Volkswagen Rabbit Custom", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "78", "weight (lb)": "1940", "0-60 mph (s)": "14.5", "year": "77" }, { "name": "Volkswagen Rabbit Custom", "economy (mpg)": "31.9", "cylinders": "4", "displacement (cc)": "89", "power (hp)": "71", "weight (lb)": "1925", "0-60 mph (s)": "14", "year": "79" }, { "name": "Volkswagen Rabbit L", "economy (mpg)": "36", "cylinders": "4", "displacement (cc)": "105", "power (hp)": "74", "weight (lb)": "1980", "0-60 mph (s)": "15.3", "year": "82" }, { "name": "Volkswagen Rabbit", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "70", "weight (lb)": "1937", "0-60 mph (s)": "14", "year": "75" }, { "name": "Volkswagen Rabbit", "economy (mpg)": "29", "cylinders": "4", "displacement (cc)": "90", "power (hp)": "70", "weight (lb)": "1937", "0-60 mph (s)": "14.2", "year": "76" }, { "name": "Volkswagen Rabbit", "economy (mpg)": "29.5", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "71", "weight (lb)": "1825", "0-60 mph (s)": "12.2", "year": "76" }, { "name": "Volkswagen Rabbit", "economy (mpg)": "41.5", "cylinders": "4", "displacement (cc)": "98", "power (hp)": "76", "weight (lb)": "2144", "0-60 mph (s)": "14.7", "year": "80" }, { "name": "Volkswagen Scirocco", "economy (mpg)": "31.5", "cylinders": "4", "displacement (cc)": "89", "power (hp)": "71", "weight (lb)": "1990", "0-60 mph (s)": "14.9", "year": "78" }, { "name": "Volkswagen Super Beetle 117", "economy (mpg)": "", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "48", "weight (lb)": "1978", "0-60 mph (s)": "20", "year": "71" }, { "name": "Volkswagen Super Beetle", "economy (mpg)": "26", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "46", "weight (lb)": "1950", "0-60 mph (s)": "21", "year": "73" }, { "name": "Volkswagen Type 3", "economy (mpg)": "23", "cylinders": "4", "displacement (cc)": "97", "power (hp)": "54", "weight (lb)": "2254", "0-60 mph (s)": "23.5", "year": "72" }, { "name": "Volvo 144EA", "economy (mpg)": "19", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "112", "weight (lb)": "2868", "0-60 mph (s)": "15.5", "year": "73" }, { "name": "Volvo 145E (Wagon)", "economy (mpg)": "18", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "112", "weight (lb)": "2933", "0-60 mph (s)": "14.5", "year": "72" }, { "name": "Volvo 244DL", "economy (mpg)": "22", "cylinders": "4", "displacement (cc)": "121", "power (hp)": "98", "weight (lb)": "2945", "0-60 mph (s)": "14.5", "year": "75" }, { "name": "Volvo 245", "economy (mpg)": "20", "cylinders": "4", "displacement (cc)": "130", "power (hp)": "102", "weight (lb)": "3150", "0-60 mph (s)": "15.7", "year": "76" }, { "name": "Volvo 264GL", "economy (mpg)": "17", "cylinders": "6", "displacement (cc)": "163", "power (hp)": "125", "weight (lb)": "3140", "0-60 mph (s)": "13.6", "year": "78" }, { "name": "Volvo Diesel", "economy (mpg)": "30.7", "cylinders": "6", "displacement (cc)": "145", "power (hp)": "76", "weight (lb)": "3160", "0-60 mph (s)": "19.6", "year": "81" } ] ================================================ FILE: packages/showcase/datasets/d3-flare-example.json ================================================ { "children": [ { "name": "analytics", "hex": "#12939A", "children": [ { "name": "cluster", "children": [ {"name": "AgglomerativeCluster", "hex": "#12939A", "value": 3938}, {"name": "CommunityStructure", "hex": "#12939A", "value": 3812}, {"name": "HierarchicalCluster", "hex": "#12939A", "value": 6714}, {"name": "MergeEdge", "hex": "#12939A", "value": 743} ] }, { "name": "graph", "children": [ {"name": "BetweennessCentrality", "hex": "#12939A", "value": 3534}, {"name": "LinkDistance", "hex": "#12939A", "value": 5731}, {"name": "MaxFlowMinCut", "hex": "#12939A", "value": 7840}, {"name": "ShortestPaths", "hex": "#12939A", "value": 5914}, {"name": "SpanningTree", "hex": "#12939A", "value": 3416} ] }, { "name": "optimization", "children": [ {"name": "AspectRatioBanker", "hex": "#12939A", "value": 7074} ] } ] }, { "name": "animate", "children": [ {"name": "Easing", "hex": "#12939A", "value": 17010}, {"name": "FunctionSequence", "hex": "#12939A", "value": 5842}, { "name": "interpolate", "children": [ {"name": "ArrayInterpolator", "hex": "#12939A", "value": 1983}, {"name": "hexInterpolator", "hex": "#12939A", "value": 2047}, {"name": "DateInterpolator", "hex": "#12939A", "value": 1375}, {"name": "Interpolator", "hex": "#12939A", "value": 8746}, {"name": "MatrixInterpolator", "hex": "#12939A", "value": 2202}, {"name": "NumberInterpolator", "hex": "#12939A", "value": 1382}, {"name": "ObjectInterpolator", "hex": "#12939A", "value": 1629}, {"name": "PointInterpolator", "hex": "#12939A", "value": 1675}, {"name": "RectangleInterpolator", "hex": "#12939A", "value": 2042} ] }, {"name": "ISchedulable", "hex": "#12939A", "value": 1041}, {"name": "Parallel", "hex": "#12939A", "value": 5176}, {"name": "Pause", "hex": "#12939A", "value": 449}, {"name": "Scheduler", "hex": "#12939A", "value": 5593}, {"name": "Sequence", "hex": "#12939A", "value": 5534}, {"name": "Transition", "hex": "#12939A", "value": 9201}, {"name": "Transitioner", "hex": "#12939A", "value": 19975}, {"name": "TransitionEvent", "hex": "#12939A", "value": 1116}, {"name": "Neonate", "hex": "#12939A", "value": 6006} ] }, { "name": "data", "children": [ { "name": "converters", "children": [ {"name": "Converters", "hex": "#12939A", "value": 721}, {"name": "DelimitedTextConverter", "hex": "#12939A", "value": 4294}, {"name": "GraphMLConverter", "hex": "#12939A", "value": 9800}, {"name": "IDataConverter", "hex": "#12939A", "value": 1314}, {"name": "JSONConverter", "hex": "#12939A", "value": 2220} ] }, {"name": "DataField", "hex": "#12939A", "value": 1759}, {"name": "DataSchema", "hex": "#12939A", "value": 2165}, {"name": "DataSet", "hex": "#12939A", "value": 586}, {"name": "DataSource", "hex": "#12939A", "value": 3331}, {"name": "DataTable", "hex": "#12939A", "value": 772}, {"name": "DataUtil", "hex": "#12939A", "value": 3322} ] }, { "name": "display", "children": [ {"name": "DirtySprite", "hex": "#12939A", "value": 8833}, {"name": "LineSprite", "hex": "#12939A", "value": 1732}, {"name": "RectSprite", "hex": "#12939A", "value": 3623}, {"name": "TextSprite", "hex": "#12939A", "value": 10066} ] }, { "name": "flex", "children": [ {"name": "FlareVis", "hex": "#12939A", "value": 4116} ] }, { "name": "physics", "children": [ {"name": "DragForce", "hex": "#12939A", "value": 1082}, {"name": "GravityForce", "hex": "#12939A", "value": 1336}, {"name": "IForce", "hex": "#12939A", "value": 319}, {"name": "NBodyForce", "hex": "#12939A", "value": 10498}, {"name": "Particle", "hex": "#12939A", "value": 2822}, {"name": "Simulation", "hex": "#12939A", "value": 9983}, {"name": "Spring", "hex": "#12939A", "value": 2213}, {"name": "SpringForce", "hex": "#12939A", "value": 1681} ] }, { "name": "query", "children": [ {"name": "AggregateExpression", "hex": "#12939A", "value": 1616}, {"name": "And", "hex": "#12939A", "value": 1027}, {"name": "Arithmetic", "hex": "#12939A", "value": 3891}, {"name": "Average", "hex": "#12939A", "value": 891}, {"name": "BinaryExpression", "hex": "#12939A", "value": 2893}, {"name": "Comparison", "hex": "#12939A", "value": 5103}, {"name": "CompositeExpression", "hex": "#12939A", "value": 3677}, {"name": "Count", "hex": "#12939A", "value": 781}, {"name": "DateUtil", "hex": "#12939A", "value": 4141}, {"name": "Distinct", "hex": "#12939A", "value": 933}, {"name": "Expression", "hex": "#12939A", "value": 5130}, {"name": "ExpressionIterator", "hex": "#12939A", "value": 3617}, {"name": "Fn", "hex": "#12939A", "value": 3240}, {"name": "If", "hex": "#12939A", "value": 2732}, {"name": "IsA", "hex": "#12939A", "value": 2039}, {"name": "Literal", "hex": "#12939A", "value": 1214}, {"name": "Match", "hex": "#12939A", "value": 3748}, {"name": "Maximum", "hex": "#12939A", "value": 843}, { "name": "methods", "children": [ {"name": "add", "hex": "#12939A", "value": 593}, {"name": "and", "hex": "#12939A", "value": 330}, {"name": "average", "hex": "#12939A", "value": 287}, {"name": "count", "hex": "#12939A", "value": 277}, {"name": "distinct", "hex": "#12939A", "value": 292}, {"name": "div", "hex": "#12939A", "value": 595}, {"name": "eq", "hex": "#12939A", "value": 594}, {"name": "fn", "hex": "#12939A", "value": 460}, {"name": "gt", "hex": "#12939A", "value": 603}, {"name": "gte", "hex": "#12939A", "value": 625}, {"name": "iff", "hex": "#12939A", "value": 748}, {"name": "isa", "hex": "#12939A", "value": 461}, {"name": "lt", "hex": "#12939A", "value": 597}, {"name": "lte", "hex": "#12939A", "value": 619}, {"name": "max", "hex": "#12939A", "value": 283}, {"name": "min", "hex": "#12939A", "value": 283}, {"name": "mod", "hex": "#12939A", "value": 591}, {"name": "mul", "hex": "#12939A", "value": 603}, {"name": "neq", "hex": "#12939A", "value": 599}, {"name": "not", "hex": "#12939A", "value": 386}, {"name": "or", "hex": "#12939A", "value": 323}, {"name": "orderby", "hex": "#12939A", "value": 307}, {"name": "range", "hex": "#12939A", "value": 772}, {"name": "select", "hex": "#12939A", "value": 296}, {"name": "stddev", "hex": "#12939A", "value": 363}, {"name": "sub", "hex": "#12939A", "value": 600}, {"name": "sum", "hex": "#12939A", "value": 280}, {"name": "update", "hex": "#12939A", "value": 307}, {"name": "variance", "hex": "#12939A", "value": 335}, {"name": "where", "hex": "#12939A", "value": 299}, {"name": "xor", "hex": "#12939A", "value": 354}, {"name": "_", "hex": "#12939A", "value": 264} ] }, {"name": "Minimum", "hex": "#12939A", "value": 843}, {"name": "Not", "hex": "#12939A", "value": 1554}, {"name": "Or", "hex": "#12939A", "value": 970}, {"name": "Query", "hex": "#12939A", "value": 13896}, {"name": "Range", "hex": "#12939A", "value": 1594}, {"name": "StringUtil", "hex": "#12939A", "value": 4130}, {"name": "Sum", "hex": "#12939A", "value": 791}, {"name": "Variable", "hex": "#12939A", "value": 1124}, {"name": "Variance", "hex": "#12939A", "value": 1876}, {"name": "Xor", "hex": "#12939A", "value": 1101} ] }, { "name": "scale", "children": [ {"name": "IScaleMap", "hex": "#FF9833", "value": 2105}, {"name": "LinearScale", "hex": "#FF9833", "value": 1316}, {"name": "LogScale", "hex": "#FF9833", "value": 3151}, {"name": "OrdinalScale", "hex": "#FF9833", "value": 3770}, {"name": "QuantileScale", "hex": "#1A3177", "value": 2435}, {"name": "QuantitativeScale", "hex": "#FF9833", "value": 4839}, {"name": "RootScale", "hex": "#FF9833", "value": 1756}, {"name": "Scale", "hex": "#FF9833", "value": 4268}, {"name": "ScaleType", "hex": "#FF9833", "value": 1821}, {"name": "TimeScale", "hex": "#FF9833", "value": 5833} ] }, { "name": "util", "children": [ {"name": "Arrays", "hex": "#12939A", "value": 8258}, {"name": "hexs", "hex": "#12939A", "value": 10001}, {"name": "Dates", "hex": "#12939A", "value": 8217}, {"name": "Displays", "hex": "#12939A", "value": 12555}, {"name": "Filter", "hex": "#12939A", "value": 2324}, {"name": "Geometry", "hex": "#12939A", "value": 10993}, { "name": "heap", "children": [ {"name": "FibonacciHeap", "hex": "#12939A", "value": 9354}, {"name": "HeapNode", "hex": "#12939A", "value": 1233} ] }, {"name": "IEvaluable", "hex": "#12939A", "value": 335}, {"name": "IPredicate", "hex": "#12939A", "value": 383}, {"name": "IValueProxy", "hex": "#12939A", "value": 874}, { "name": "math", "children": [ {"name": "DenseMatrix", "hex": "#12939A", "value": 3165}, {"name": "IMatrix", "hex": "#12939A", "value": 2815}, {"name": "SparseMatrix", "hex": "#12939A", "value": 3366} ] }, {"name": "Maths", "hex": "#12939A", "value": 17705}, {"name": "Orientation", "hex": "#12939A", "value": 1486}, { "name": "palette", "children": [ {"name": "hexPalette", "hex": "#12939A", "value": 6367}, {"name": "Palette", "hex": "#12939A", "value": 1229}, {"name": "ShapePalette", "hex": "#12939A", "value": 2059}, {"name": "valuePalette", "hex": "#12939A", "value": 2291} ] }, {"name": "Property", "hex": "#12939A", "value": 5559}, {"name": "Shapes", "hex": "#12939A", "value": 19118}, {"name": "Sort", "hex": "#12939A", "value": 6887}, {"name": "Stats", "hex": "#12939A", "value": 6557}, {"name": "Strings", "hex": "#12939A", "value": 22026} ] }, { "name": "vis", "children": [ { "name": "axis", "children": [ {"name": "Axes", "hex": "#12939A", "value": 1302}, {"name": "Axis", "hex": "#12939A", "value": 24593}, {"name": "AxisGridLine", "hex": "#12939A", "value": 652}, {"name": "AxisLabel", "hex": "#12939A", "value": 636}, {"name": "CartesianAxes", "hex": "#12939A", "value": 6703} ] }, { "name": "controls", "children": [ {"name": "AnchorControl", "hex": "#12939A", "value": 2138}, {"name": "ClickControl", "hex": "#12939A", "value": 3824}, {"name": "Control", "hex": "#12939A", "value": 1353}, {"name": "ControlList", "hex": "#12939A", "value": 4665}, {"name": "DragControl", "hex": "#12939A", "value": 2649}, {"name": "ExpandControl", "hex": "#12939A", "value": 2832}, {"name": "HoverControl", "hex": "#12939A", "value": 4896}, {"name": "IControl", "hex": "#12939A", "value": 763}, {"name": "PanZoomControl", "hex": "#12939A", "value": 5222}, {"name": "SelectionControl", "hex": "#12939A", "value": 7862}, {"name": "TooltipControl", "hex": "#12939A", "value": 8435} ] }, { "name": "data", "children": [ {"name": "Data", "hex": "#12939A", "value": 20544}, {"name": "DataList", "hex": "#12939A", "value": 19788}, {"name": "DataSprite", "hex": "#12939A", "value": 10349}, {"name": "EdgeSprite", "hex": "#12939A", "value": 3301}, {"name": "NodeSprite", "hex": "#12939A", "value": 19382}, { "name": "render", "children": [ {"name": "ArrowType", "hex": "#12939A", "value": 698}, {"name": "EdgeRenderer", "hex": "#12939A", "value": 5569}, {"name": "IRenderer", "hex": "#12939A", "value": 353}, {"name": "ShapeRenderer", "hex": "#12939A", "value": 2247} ] }, {"name": "ScaleBinding", "hex": "#12939A", "value": 11275}, {"name": "Tree", "hex": "#12939A", "value": 7147}, {"name": "TreeBuilder", "hex": "#12939A", "value": 9930} ] }, { "name": "events", "children": [ {"name": "DataEvent", "hex": "#12939A", "value": 2313}, {"name": "SelectionEvent", "hex": "#12939A", "value": 1880}, {"name": "TooltipEvent", "hex": "#12939A", "value": 1701}, {"name": "VisualizationEvent", "hex": "#12939A", "value": 1117} ] }, { "name": "legend", "children": [ {"name": "Legend", "hex": "#12939A", "value": 20859}, {"name": "LegendItem", "hex": "#12939A", "value": 4614}, {"name": "LegendRange", "hex": "#12939A", "value": 10530} ] }, { "name": "operator", "children": [ { "name": "distortion", "children": [ {"name": "BifocalDistortion", "hex": "#12939A", "value": 4461}, {"name": "Distortion", "hex": "#12939A", "value": 6314}, {"name": "FisheyeDistortion", "hex": "#12939A", "value": 3444} ] }, { "name": "encoder", "children": [ {"name": "hexEncoder", "hex": "#12939A", "value": 3179}, {"name": "Encoder", "hex": "#12939A", "value": 4060}, {"name": "PropertyEncoder", "hex": "#12939A", "value": 4138}, {"name": "ShapeEncoder", "hex": "#12939A", "value": 1690}, {"name": "valueEncoder", "hex": "#12939A", "value": 1830} ] }, { "name": "filter", "children": [ {"name": "FisheyeTreeFilter", "hex": "#12939A", "value": 5219}, {"name": "GraphDistanceFilter", "hex": "#12939A", "value": 3165}, {"name": "VisibilityFilter", "hex": "#12939A", "value": 3509} ] }, {"name": "IOperator", "hex": "#12939A", "value": 1286}, { "name": "label", "children": [ {"name": "Labeler", "hex": "#12939A", "value": 9956}, {"name": "RadialLabeler", "hex": "#12939A", "value": 3899}, {"name": "StackedAreaLabeler", "hex": "#12939A", "value": 3202} ] }, { "name": "layout", "children": [ {"name": "AxisLayout", "hex": "#12939A", "value": 6725}, {"name": "BundledEdgeRouter", "hex": "#12939A", "value": 3727}, {"name": "CircleLayout", "hex": "#12939A", "value": 9317}, {"name": "CirclePackingLayout", "hex": "#12939A", "value": 12003}, {"name": "DendrogramLayout", "hex": "#12939A", "value": 4853}, {"name": "ForceDirectedLayout", "hex": "#12939A", "value": 8411}, {"name": "IcicleTreeLayout", "hex": "#12939A", "value": 4864}, {"name": "IndentedTreeLayout", "hex": "#12939A", "value": 3174}, {"name": "Layout", "hex": "#12939A", "value": 7881}, {"name": "NodeLinkTreeLayout", "hex": "#12939A", "value": 12870}, {"name": "PieLayout", "hex": "#12939A", "value": 2728}, {"name": "RadialTreeLayout", "hex": "#12939A", "value": 12348}, {"name": "RandomLayout", "hex": "#12939A", "value": 870}, {"name": "StackedAreaLayout", "hex": "#12939A", "value": 9121}, {"name": "TreeMapLayout", "hex": "#12939A", "value": 9191} ] }, {"name": "Operator", "hex": "#12939A", "value": 2490}, {"name": "OperatorList", "hex": "#12939A", "value": 5248}, {"name": "OperatorSequence", "hex": "#12939A", "value": 4190}, {"name": "OperatorSwitch", "hex": "#12939A", "value": 2581}, {"name": "SortOperator", "hex": "#12939A", "value": 2023} ] }, {"name": "Visualization", "hex": "#12939A", "value": 16540} ] } ] } ================================================ FILE: packages/showcase/datasets/energy.json ================================================ { "nodes": [ {"name": "Agricultural 'waste'"}, {"name": "Bio-conversion"}, {"name": "Liquid"}, {"name": "Losses"}, {"name": "Solid"}, {"name": "Gas"}, {"name": "Biofuel imports"}, {"name": "Biomass imports"}, {"name": "Coal imports"}, {"name": "Coal"}, {"name": "Coal reserves"}, {"name": "District heating"}, {"name": "Industry"}, {"name": "Heating and cooling - commercial"}, {"name": "Heating and cooling - homes"}, {"name": "Electricity grid"}, {"name": "Over generation / exports"}, {"name": "H2 conversion"}, {"name": "Road transport"}, {"name": "Agriculture"}, {"name": "Rail transport"}, {"name": "Lighting & appliances - commercial"}, {"name": "Lighting & appliances - homes"}, {"name": "Gas imports"}, {"name": "Ngas"}, {"name": "Gas reserves"}, {"name": "Thermal generation"}, {"name": "Geothermal"}, {"name": "H2"}, {"name": "Hydro"}, {"name": "International shipping"}, {"name": "Domestic aviation"}, {"name": "International aviation"}, {"name": "National navigation"}, {"name": "Marine algae"}, {"name": "Nuclear"}, {"name": "Oil imports"}, {"name": "Oil"}, {"name": "Oil reserves"}, {"name": "Other waste"}, {"name": "Pumped heat"}, {"name": "Solar PV"}, {"name": "Solar Thermal"}, {"name": "Solar"}, {"name": "Tidal"}, {"name": "UK land based bioenergy"}, {"name": "Wave"}, {"name": "Wind"} ], "links": [ {"source": 0,"target": 1,"value": 124.729}, {"source": 1,"target": 2,"value": 0.597}, {"source": 1,"target": 3,"value": 26.862}, {"source": 1,"target": 4,"value": 280.322}, {"source": 1,"target": 5,"value": 81.144}, {"source": 6,"target": 2,"value": 35}, {"source": 7,"target": 4,"value": 35}, {"source": 8,"target": 9,"value": 11.606}, {"source": 10,"target": 9,"value": 63.965}, {"source": 9,"target": 4,"value": 75.571}, {"source": 11,"target": 12,"value": 10.639}, {"source": 11,"target": 13,"value": 22.505}, {"source": 11,"target": 14,"value": 46.184}, {"source": 15,"target": 16,"value": 104.453}, {"source": 15,"target": 14,"value": 113.726}, {"source": 15,"target": 17,"value": 27.14}, {"source": 15,"target": 12,"value": 342.165}, {"source": 15,"target": 18,"value": 37.797}, {"source": 15,"target": 19,"value": 4.412}, {"source": 15,"target": 13,"value": 40.858}, {"source": 15,"target": 3,"value": 56.691}, {"source": 15,"target": 20,"value": 7.863}, {"source": 15,"target": 21,"value": 90.008}, {"source": 15,"target": 22,"value": 93.494}, {"source": 23,"target": 24,"value": 40.719}, {"source": 25,"target": 24,"value": 82.233}, {"source": 5,"target": 13,"value": 0.129}, {"source": 5,"target": 3,"value": 1.401}, {"source": 5,"target": 26,"value": 151.891}, {"source": 5,"target": 19,"value": 2.096}, {"source": 5,"target": 12,"value": 48.58}, {"source": 27,"target": 15,"value": 7.013}, {"source": 17,"target": 28,"value": 20.897}, {"source": 17,"target": 3,"value": 6.242}, {"source": 28,"target": 18,"value": 20.897}, {"source": 29,"target": 15,"value": 6.995}, {"source": 2,"target": 12,"value": 121.066}, {"source": 2,"target": 30,"value": 128.69}, {"source": 2,"target": 18,"value": 135.835}, {"source": 2,"target": 31,"value": 14.458}, {"source": 2,"target": 32,"value": 206.267}, {"source": 2,"target": 19,"value": 3.64}, {"source": 2,"target": 33,"value": 33.218}, {"source": 2,"target": 20,"value": 4.413}, {"source": 34,"target": 1,"value": 4.375}, {"source": 24,"target": 5,"value": 122.952}, {"source": 35,"target": 26,"value": 839.978}, {"source": 36,"target": 37,"value": 504.287}, {"source": 38,"target": 37,"value": 107.703}, {"source": 37,"target": 2,"value": 611.99}, {"source": 39,"target": 4,"value": 56.587}, {"source": 39,"target": 1,"value": 77.81}, {"source": 40,"target": 14,"value": 193.026}, {"source": 40,"target": 13,"value": 70.672}, {"source": 41,"target": 15,"value": 59.901}, {"source": 42,"target": 14,"value": 19.263}, {"source": 43,"target": 42,"value": 19.263}, {"source": 43,"target": 41,"value": 59.901}, {"source": 4,"target": 19,"value": 0.882}, {"source": 4,"target": 26,"value": 400.12}, {"source": 4,"target": 12,"value": 46.477}, {"source": 26,"target": 15,"value": 525.531}, {"source": 26,"target": 3,"value": 787.129}, {"source": 26,"target": 11,"value": 79.329}, {"source": 44,"target": 15,"value": 9.452}, {"source": 45,"target": 1,"value": 182.01}, {"source": 46,"target": 15,"value": 19.013}, {"source": 47,"target": 15,"value": 289.366} ] } ================================================ FILE: packages/showcase/datasets/iris.json ================================================ [ { "sepal length": 5.1, "sepal width": 3.5, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.9, "sepal width": 3.0, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.7, "sepal width": 3.2, "petal length": 1.3, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.6, "sepal width": 3.1, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.6, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.4, "sepal width": 3.9, "petal length": 1.7, "petal width": 0.4, "species": "setosa" }, { "sepal length": 4.6, "sepal width": 3.4, "petal length": 1.4, "petal width": 0.3, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.4, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.4, "sepal width": 2.9, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.9, "sepal width": 3.1, "petal length": 1.5, "petal width": 0.1, "species": "setosa" }, { "sepal length": 5.4, "sepal width": 3.7, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.8, "sepal width": 3.4, "petal length": 1.6, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.8, "sepal width": 3.0, "petal length": 1.4, "petal width": 0.1, "species": "setosa" }, { "sepal length": 4.3, "sepal width": 3.0, "petal length": 1.1, "petal width": 0.1, "species": "setosa" }, { "sepal length": 5.8, "sepal width": 4.0, "petal length": 1.2, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.7, "sepal width": 4.4, "petal length": 1.5, "petal width": 0.4, "species": "setosa" }, { "sepal length": 5.4, "sepal width": 3.9, "petal length": 1.3, "petal width": 0.4, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.5, "petal length": 1.4, "petal width": 0.3, "species": "setosa" }, { "sepal length": 5.7, "sepal width": 3.8, "petal length": 1.7, "petal width": 0.3, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.8, "petal length": 1.5, "petal width": 0.3, "species": "setosa" }, { "sepal length": 5.4, "sepal width": 3.4, "petal length": 1.7, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.7, "petal length": 1.5, "petal width": 0.4, "species": "setosa" }, { "sepal length": 4.6, "sepal width": 3.6, "petal length": 1.0, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.3, "petal length": 1.7, "petal width": 0.5, "species": "setosa" }, { "sepal length": 4.8, "sepal width": 3.4, "petal length": 1.9, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.0, "petal length": 1.6, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.4, "petal length": 1.6, "petal width": 0.4, "species": "setosa" }, { "sepal length": 5.2, "sepal width": 3.5, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.2, "sepal width": 3.4, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.7, "sepal width": 3.2, "petal length": 1.6, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.8, "sepal width": 3.1, "petal length": 1.6, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.4, "sepal width": 3.4, "petal length": 1.5, "petal width": 0.4, "species": "setosa" }, { "sepal length": 5.2, "sepal width": 4.1, "petal length": 1.5, "petal width": 0.1, "species": "setosa" }, { "sepal length": 5.5, "sepal width": 4.2, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.9, "sepal width": 3.1, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.2, "petal length": 1.2, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.5, "sepal width": 3.5, "petal length": 1.3, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.9, "sepal width": 3.6, "petal length": 1.4, "petal width": 0.1, "species": "setosa" }, { "sepal length": 4.4, "sepal width": 3.0, "petal length": 1.3, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.4, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.5, "petal length": 1.3, "petal width": 0.3, "species": "setosa" }, { "sepal length": 4.5, "sepal width": 2.3, "petal length": 1.3, "petal width": 0.3, "species": "setosa" }, { "sepal length": 4.4, "sepal width": 3.2, "petal length": 1.3, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.5, "petal length": 1.6, "petal width": 0.6, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.8, "petal length": 1.9, "petal width": 0.4, "species": "setosa" }, { "sepal length": 4.8, "sepal width": 3.0, "petal length": 1.4, "petal width": 0.3, "species": "setosa" }, { "sepal length": 5.1, "sepal width": 3.8, "petal length": 1.6, "petal width": 0.2, "species": "setosa" }, { "sepal length": 4.6, "sepal width": 3.2, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.3, "sepal width": 3.7, "petal length": 1.5, "petal width": 0.2, "species": "setosa" }, { "sepal length": 5.0, "sepal width": 3.3, "petal length": 1.4, "petal width": 0.2, "species": "setosa" }, { "sepal length": 7.0, "sepal width": 3.2, "petal length": 4.7, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 6.4, "sepal width": 3.2, "petal length": 4.5, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 6.9, "sepal width": 3.1, "petal length": 4.9, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 5.5, "sepal width": 2.3, "petal length": 4.0, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.5, "sepal width": 2.8, "petal length": 4.6, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 5.7, "sepal width": 2.8, "petal length": 4.5, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.3, "sepal width": 3.3, "petal length": 4.7, "petal width": 1.6, "species": "versicolor" }, { "sepal length": 4.9, "sepal width": 2.4, "petal length": 3.3, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 6.6, "sepal width": 2.9, "petal length": 4.6, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.2, "sepal width": 2.7, "petal length": 3.9, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 5.0, "sepal width": 2.0, "petal length": 3.5, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 5.9, "sepal width": 3.0, "petal length": 4.2, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 6.0, "sepal width": 2.2, "petal length": 4.0, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 6.1, "sepal width": 2.9, "petal length": 4.7, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 5.6, "sepal width": 2.9, "petal length": 3.6, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.7, "sepal width": 3.1, "petal length": 4.4, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 5.6, "sepal width": 3.0, "petal length": 4.5, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 5.8, "sepal width": 2.7, "petal length": 4.1, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 6.2, "sepal width": 2.2, "petal length": 4.5, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 5.6, "sepal width": 2.5, "petal length": 3.9, "petal width": 1.1, "species": "versicolor" }, { "sepal length": 5.9, "sepal width": 3.2, "petal length": 4.8, "petal width": 1.8, "species": "versicolor" }, { "sepal length": 6.1, "sepal width": 2.8, "petal length": 4.0, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.3, "sepal width": 2.5, "petal length": 4.9, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 6.1, "sepal width": 2.8, "petal length": 4.7, "petal width": 1.2, "species": "versicolor" }, { "sepal length": 6.4, "sepal width": 2.9, "petal length": 4.3, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.6, "sepal width": 3.0, "petal length": 4.4, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 6.8, "sepal width": 2.8, "petal length": 4.8, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 6.7, "sepal width": 3.0, "petal length": 5.0, "petal width": 1.7, "species": "versicolor" }, { "sepal length": 6.0, "sepal width": 2.9, "petal length": 4.5, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 5.7, "sepal width": 2.6, "petal length": 3.5, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 5.5, "sepal width": 2.4, "petal length": 3.8, "petal width": 1.1, "species": "versicolor" }, { "sepal length": 5.5, "sepal width": 2.4, "petal length": 3.7, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 5.8, "sepal width": 2.7, "petal length": 3.9, "petal width": 1.2, "species": "versicolor" }, { "sepal length": 6.0, "sepal width": 2.7, "petal length": 5.1, "petal width": 1.6, "species": "versicolor" }, { "sepal length": 5.4, "sepal width": 3.0, "petal length": 4.5, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 6.0, "sepal width": 3.4, "petal length": 4.5, "petal width": 1.6, "species": "versicolor" }, { "sepal length": 6.7, "sepal width": 3.1, "petal length": 4.7, "petal width": 1.5, "species": "versicolor" }, { "sepal length": 6.3, "sepal width": 2.3, "petal length": 4.4, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.6, "sepal width": 3.0, "petal length": 4.1, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.5, "sepal width": 2.5, "petal length": 4.0, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.5, "sepal width": 2.6, "petal length": 4.4, "petal width": 1.2, "species": "versicolor" }, { "sepal length": 6.1, "sepal width": 3.0, "petal length": 4.6, "petal width": 1.4, "species": "versicolor" }, { "sepal length": 5.8, "sepal width": 2.6, "petal length": 4.0, "petal width": 1.2, "species": "versicolor" }, { "sepal length": 5.0, "sepal width": 2.3, "petal length": 3.3, "petal width": 1.0, "species": "versicolor" }, { "sepal length": 5.6, "sepal width": 2.7, "petal length": 4.2, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.7, "sepal width": 3.0, "petal length": 4.2, "petal width": 1.2, "species": "versicolor" }, { "sepal length": 5.7, "sepal width": 2.9, "petal length": 4.2, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.2, "sepal width": 2.9, "petal length": 4.3, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 5.1, "sepal width": 2.5, "petal length": 3.0, "petal width": 1.1, "species": "versicolor" }, { "sepal length": 5.7, "sepal width": 2.8, "petal length": 4.1, "petal width": 1.3, "species": "versicolor" }, { "sepal length": 6.3, "sepal width": 3.3, "petal length": 6.0, "petal width": 2.5, "species": "virginica" }, { "sepal length": 5.8, "sepal width": 2.7, "petal length": 5.1, "petal width": 1.9, "species": "virginica" }, { "sepal length": 7.1, "sepal width": 3.0, "petal length": 5.9, "petal width": 2.1, "species": "virginica" }, { "sepal length": 6.3, "sepal width": 2.9, "petal length": 5.6, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.5, "sepal width": 3.0, "petal length": 5.8, "petal width": 2.2, "species": "virginica" }, { "sepal length": 7.6, "sepal width": 3.0, "petal length": 6.6, "petal width": 2.1, "species": "virginica" }, { "sepal length": 4.9, "sepal width": 2.5, "petal length": 4.5, "petal width": 1.7, "species": "virginica" }, { "sepal length": 7.3, "sepal width": 2.9, "petal length": 6.3, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.7, "sepal width": 2.5, "petal length": 5.8, "petal width": 1.8, "species": "virginica" }, { "sepal length": 7.2, "sepal width": 3.6, "petal length": 6.1, "petal width": 2.5, "species": "virginica" }, { "sepal length": 6.5, "sepal width": 3.2, "petal length": 5.1, "petal width": 2.0, "species": "virginica" }, { "sepal length": 6.4, "sepal width": 2.7, "petal length": 5.3, "petal width": 1.9, "species": "virginica" }, { "sepal length": 6.8, "sepal width": 3.0, "petal length": 5.5, "petal width": 2.1, "species": "virginica" }, { "sepal length": 5.7, "sepal width": 2.5, "petal length": 5.0, "petal width": 2.0, "species": "virginica" }, { "sepal length": 5.8, "sepal width": 2.8, "petal length": 5.1, "petal width": 2.4, "species": "virginica" }, { "sepal length": 6.4, "sepal width": 3.2, "petal length": 5.3, "petal width": 2.3, "species": "virginica" }, { "sepal length": 6.5, "sepal width": 3.0, "petal length": 5.5, "petal width": 1.8, "species": "virginica" }, { "sepal length": 7.7, "sepal width": 3.8, "petal length": 6.7, "petal width": 2.2, "species": "virginica" }, { "sepal length": 7.7, "sepal width": 2.6, "petal length": 6.9, "petal width": 2.3, "species": "virginica" }, { "sepal length": 6.0, "sepal width": 2.2, "petal length": 5.0, "petal width": 1.5, "species": "virginica" }, { "sepal length": 6.9, "sepal width": 3.2, "petal length": 5.7, "petal width": 2.3, "species": "virginica" }, { "sepal length": 5.6, "sepal width": 2.8, "petal length": 4.9, "petal width": 2.0, "species": "virginica" }, { "sepal length": 7.7, "sepal width": 2.8, "petal length": 6.7, "petal width": 2.0, "species": "virginica" }, { "sepal length": 6.3, "sepal width": 2.7, "petal length": 4.9, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.7, "sepal width": 3.3, "petal length": 5.7, "petal width": 2.1, "species": "virginica" }, { "sepal length": 7.2, "sepal width": 3.2, "petal length": 6.0, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.2, "sepal width": 2.8, "petal length": 4.8, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.1, "sepal width": 3.0, "petal length": 4.9, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.4, "sepal width": 2.8, "petal length": 5.6, "petal width": 2.1, "species": "virginica" }, { "sepal length": 7.2, "sepal width": 3.0, "petal length": 5.8, "petal width": 1.6, "species": "virginica" }, { "sepal length": 7.4, "sepal width": 2.8, "petal length": 6.1, "petal width": 1.9, "species": "virginica" }, { "sepal length": 7.9, "sepal width": 3.8, "petal length": 6.4, "petal width": 2.0, "species": "virginica" }, { "sepal length": 6.4, "sepal width": 2.8, "petal length": 5.6, "petal width": 2.2, "species": "virginica" }, { "sepal length": 6.3, "sepal width": 2.8, "petal length": 5.1, "petal width": 1.5, "species": "virginica" }, { "sepal length": 6.1, "sepal width": 2.6, "petal length": 5.6, "petal width": 1.4, "species": "virginica" }, { "sepal length": 7.7, "sepal width": 3.0, "petal length": 6.1, "petal width": 2.3, "species": "virginica" }, { "sepal length": 6.3, "sepal width": 3.4, "petal length": 5.6, "petal width": 2.4, "species": "virginica" }, { "sepal length": 6.4, "sepal width": 3.1, "petal length": 5.5, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.0, "sepal width": 3.0, "petal length": 4.8, "petal width": 1.8, "species": "virginica" }, { "sepal length": 6.9, "sepal width": 3.1, "petal length": 5.4, "petal width": 2.1, "species": "virginica" }, { "sepal length": 6.7, "sepal width": 3.1, "petal length": 5.6, "petal width": 2.4, "species": "virginica" }, { "sepal length": 6.9, "sepal width": 3.1, "petal length": 5.1, "petal width": 2.3, "species": "virginica" }, { "sepal length": 5.8, "sepal width": 2.7, "petal length": 5.1, "petal width": 1.9, "species": "virginica" }, { "sepal length": 6.8, "sepal width": 3.2, "petal length": 5.9, "petal width": 2.3, "species": "virginica" }, { "sepal length": 6.7, "sepal width": 3.3, "petal length": 5.7, "petal width": 2.5, "species": "virginica" }, { "sepal length": 6.7, "sepal width": 3.0, "petal length": 5.2, "petal width": 2.3, "species": "virginica" }, { "sepal length": 6.3, "sepal width": 2.5, "petal length": 5.0, "petal width": 1.9, "species": "virginica" }, { "sepal length": 6.5, "sepal width": 3.0, "petal length": 5.2, "petal width": 2.0, "species": "virginica" }, { "sepal length": 6.2, "sepal width": 3.4, "petal length": 5.4, "petal width": 2.3, "species": "virginica" }, { "sepal length": 5.9, "sepal width": 3.0, "petal length": 5.1, "petal width": 1.8, "species": "virginica" } ] ================================================ FILE: packages/showcase/datasets/old-faithful.json ================================================ [ {"eruptions": 3.6, "waiting": 79}, {"eruptions": 1.8, "waiting": 54}, {"eruptions": 3.333, "waiting": 74}, {"eruptions": 2.283, "waiting": 62}, {"eruptions": 4.533, "waiting": 85}, {"eruptions": 2.883, "waiting": 55}, {"eruptions": 4.7, "waiting": 88}, {"eruptions": 3.6, "waiting": 85}, {"eruptions": 1.95, "waiting": 51}, {"eruptions": 4.35, "waiting": 85}, {"eruptions": 1.833, "waiting": 54}, {"eruptions": 3.917, "waiting": 84}, {"eruptions": 4.2, "waiting": 78}, {"eruptions": 1.75, "waiting": 47}, {"eruptions": 4.7, "waiting": 83}, {"eruptions": 2.167, "waiting": 52}, {"eruptions": 1.75, "waiting": 62}, {"eruptions": 4.8, "waiting": 84}, {"eruptions": 1.6, "waiting": 52}, {"eruptions": 4.25, "waiting": 79}, {"eruptions": 1.8, "waiting": 51}, {"eruptions": 1.75, "waiting": 47}, {"eruptions": 3.45, "waiting": 78}, {"eruptions": 3.067, "waiting": 69}, {"eruptions": 4.533, "waiting": 74}, {"eruptions": 3.6, "waiting": 83}, {"eruptions": 1.967, "waiting": 55}, {"eruptions": 4.083, "waiting": 76}, {"eruptions": 3.85, "waiting": 78}, {"eruptions": 4.433, "waiting": 79}, {"eruptions": 4.3, "waiting": 73}, {"eruptions": 4.467, "waiting": 77}, {"eruptions": 3.367, "waiting": 66}, {"eruptions": 4.033, "waiting": 80}, {"eruptions": 3.833, "waiting": 74}, {"eruptions": 2.017, "waiting": 52}, {"eruptions": 1.867, "waiting": 48}, {"eruptions": 4.833, "waiting": 80}, {"eruptions": 1.833, "waiting": 59}, {"eruptions": 4.783, "waiting": 90}, {"eruptions": 4.35, "waiting": 80}, {"eruptions": 1.883, "waiting": 58}, {"eruptions": 4.567, "waiting": 84}, {"eruptions": 1.75, "waiting": 58}, {"eruptions": 4.533, "waiting": 73}, {"eruptions": 3.317, "waiting": 83}, {"eruptions": 3.833, "waiting": 64}, {"eruptions": 2.1, "waiting": 53}, {"eruptions": 4.633, "waiting": 82}, {"eruptions": 2, "waiting": 59}, {"eruptions": 4.8, "waiting": 75}, {"eruptions": 4.716, "waiting": 90}, {"eruptions": 1.833, "waiting": 54}, {"eruptions": 4.833, "waiting": 80}, {"eruptions": 1.733, "waiting": 54}, {"eruptions": 4.883, "waiting": 83}, {"eruptions": 3.717, "waiting": 71}, {"eruptions": 1.667, "waiting": 64}, {"eruptions": 4.567, "waiting": 77}, {"eruptions": 4.317, "waiting": 81}, {"eruptions": 2.233, "waiting": 59}, {"eruptions": 4.5, "waiting": 84}, {"eruptions": 1.75, "waiting": 48}, {"eruptions": 4.8, "waiting": 82}, {"eruptions": 1.817, "waiting": 60}, {"eruptions": 4.4, "waiting": 92}, {"eruptions": 4.167, "waiting": 78}, {"eruptions": 4.7, "waiting": 78}, {"eruptions": 2.067, "waiting": 65}, {"eruptions": 4.7, "waiting": 73}, {"eruptions": 4.033, "waiting": 82}, {"eruptions": 1.967, "waiting": 56}, {"eruptions": 4.5, "waiting": 79}, {"eruptions": 4, "waiting": 71}, {"eruptions": 1.983, "waiting": 62}, {"eruptions": 5.067, "waiting": 76}, {"eruptions": 2.017, "waiting": 60}, {"eruptions": 4.567, "waiting": 78}, {"eruptions": 3.883, "waiting": 76}, {"eruptions": 3.6, "waiting": 83}, {"eruptions": 4.133, "waiting": 75}, {"eruptions": 4.333, "waiting": 82}, {"eruptions": 4.1, "waiting": 70}, {"eruptions": 2.633, "waiting": 65}, {"eruptions": 4.067, "waiting": 73}, {"eruptions": 4.933, "waiting": 88}, {"eruptions": 3.95, "waiting": 76}, {"eruptions": 4.517, "waiting": 80}, {"eruptions": 2.167, "waiting": 48}, {"eruptions": 4, "waiting": 86}, {"eruptions": 2.2, "waiting": 60}, {"eruptions": 4.333, "waiting": 90}, {"eruptions": 1.867, "waiting": 50}, {"eruptions": 4.817, "waiting": 78}, {"eruptions": 1.833, "waiting": 63}, {"eruptions": 4.3, "waiting": 72}, {"eruptions": 4.667, "waiting": 84}, {"eruptions": 3.75, "waiting": 75}, {"eruptions": 1.867, "waiting": 51}, {"eruptions": 4.9, "waiting": 82}, {"eruptions": 2.483, "waiting": 62}, {"eruptions": 4.367, "waiting": 88}, {"eruptions": 2.1, "waiting": 49}, {"eruptions": 4.5, "waiting": 83}, {"eruptions": 4.05, "waiting": 81}, {"eruptions": 1.867, "waiting": 47}, {"eruptions": 4.7, "waiting": 84}, {"eruptions": 1.783, "waiting": 52}, {"eruptions": 4.85, "waiting": 86}, {"eruptions": 3.683, "waiting": 81}, {"eruptions": 4.733, "waiting": 75}, {"eruptions": 2.3, "waiting": 59}, {"eruptions": 4.9, "waiting": 89}, {"eruptions": 4.417, "waiting": 79}, {"eruptions": 1.7, "waiting": 59}, {"eruptions": 4.633, "waiting": 81}, {"eruptions": 2.317, "waiting": 50}, {"eruptions": 4.6, "waiting": 85}, {"eruptions": 1.817, "waiting": 59}, {"eruptions": 4.417, "waiting": 87}, {"eruptions": 2.617, "waiting": 53}, {"eruptions": 4.067, "waiting": 69}, {"eruptions": 4.25, "waiting": 77}, {"eruptions": 1.967, "waiting": 56}, {"eruptions": 4.6, "waiting": 88}, {"eruptions": 3.767, "waiting": 81}, {"eruptions": 1.917, "waiting": 45}, {"eruptions": 4.5, "waiting": 82}, {"eruptions": 2.267, "waiting": 55}, {"eruptions": 4.65, "waiting": 90}, {"eruptions": 1.867, "waiting": 45}, {"eruptions": 4.167, "waiting": 83}, {"eruptions": 2.8, "waiting": 56}, {"eruptions": 4.333, "waiting": 89}, {"eruptions": 1.833, "waiting": 46}, {"eruptions": 4.383, "waiting": 82}, {"eruptions": 1.883, "waiting": 51}, {"eruptions": 4.933, "waiting": 86}, {"eruptions": 2.033, "waiting": 53}, {"eruptions": 3.733, "waiting": 79}, {"eruptions": 4.233, "waiting": 81}, {"eruptions": 2.233, "waiting": 60}, {"eruptions": 4.533, "waiting": 82}, {"eruptions": 4.817, "waiting": 77}, {"eruptions": 4.333, "waiting": 76}, {"eruptions": 1.983, "waiting": 59}, {"eruptions": 4.633, "waiting": 80}, {"eruptions": 2.017, "waiting": 49}, {"eruptions": 5.1, "waiting": 96}, {"eruptions": 1.8, "waiting": 53}, {"eruptions": 5.033, "waiting": 77}, {"eruptions": 4, "waiting": 77}, {"eruptions": 2.4, "waiting": 65}, {"eruptions": 4.6, "waiting": 81}, {"eruptions": 3.567, "waiting": 71}, {"eruptions": 4, "waiting": 70}, {"eruptions": 4.5, "waiting": 81}, {"eruptions": 4.083, "waiting": 93}, {"eruptions": 1.8, "waiting": 53}, {"eruptions": 3.967, "waiting": 89}, {"eruptions": 2.2, "waiting": 45}, {"eruptions": 4.15, "waiting": 86}, {"eruptions": 2, "waiting": 58}, {"eruptions": 3.833, "waiting": 78}, {"eruptions": 3.5, "waiting": 66}, {"eruptions": 4.583, "waiting": 76}, {"eruptions": 2.367, "waiting": 63}, {"eruptions": 5, "waiting": 88}, {"eruptions": 1.933, "waiting": 52}, {"eruptions": 4.617, "waiting": 93}, {"eruptions": 1.917, "waiting": 49}, {"eruptions": 2.083, "waiting": 57}, {"eruptions": 4.583, "waiting": 77}, {"eruptions": 3.333, "waiting": 68}, {"eruptions": 4.167, "waiting": 81}, {"eruptions": 4.333, "waiting": 81}, {"eruptions": 4.5, "waiting": 73}, {"eruptions": 2.417, "waiting": 50}, {"eruptions": 4, "waiting": 85}, {"eruptions": 4.167, "waiting": 74}, {"eruptions": 1.883, "waiting": 55}, {"eruptions": 4.583, "waiting": 77}, {"eruptions": 4.25, "waiting": 83}, {"eruptions": 3.767, "waiting": 83}, {"eruptions": 2.033, "waiting": 51}, {"eruptions": 4.433, "waiting": 78}, {"eruptions": 4.083, "waiting": 84}, {"eruptions": 1.833, "waiting": 46}, {"eruptions": 4.417, "waiting": 83}, {"eruptions": 2.183, "waiting": 55}, {"eruptions": 4.8, "waiting": 81}, {"eruptions": 1.833, "waiting": 57}, {"eruptions": 4.8, "waiting": 76}, {"eruptions": 4.1, "waiting": 84}, {"eruptions": 3.966, "waiting": 77}, {"eruptions": 4.233, "waiting": 81}, {"eruptions": 3.5, "waiting": 87}, {"eruptions": 4.366, "waiting": 77}, {"eruptions": 2.25, "waiting": 51}, {"eruptions": 4.667, "waiting": 78}, {"eruptions": 2.1, "waiting": 60}, {"eruptions": 4.35, "waiting": 82}, {"eruptions": 4.133, "waiting": 91}, {"eruptions": 1.867, "waiting": 53}, {"eruptions": 4.6, "waiting": 78}, {"eruptions": 1.783, "waiting": 46}, {"eruptions": 4.367, "waiting": 77}, {"eruptions": 3.85, "waiting": 84}, {"eruptions": 1.933, "waiting": 49}, {"eruptions": 4.5, "waiting": 83}, {"eruptions": 2.383, "waiting": 71}, {"eruptions": 4.7, "waiting": 80}, {"eruptions": 1.867, "waiting": 49}, {"eruptions": 3.833, "waiting": 75}, {"eruptions": 3.417, "waiting": 64}, {"eruptions": 4.233, "waiting": 76}, {"eruptions": 2.4, "waiting": 53}, {"eruptions": 4.8, "waiting": 94}, {"eruptions": 2, "waiting": 55}, {"eruptions": 4.15, "waiting": 76}, {"eruptions": 1.867, "waiting": 50}, {"eruptions": 4.267, "waiting": 82}, {"eruptions": 1.75, "waiting": 54}, {"eruptions": 4.483, "waiting": 75}, {"eruptions": 4, "waiting": 78}, {"eruptions": 4.117, "waiting": 79}, {"eruptions": 4.083, "waiting": 78}, {"eruptions": 4.267, "waiting": 78}, {"eruptions": 3.917, "waiting": 70}, {"eruptions": 4.55, "waiting": 79}, {"eruptions": 4.083, "waiting": 70}, {"eruptions": 2.417, "waiting": 54}, {"eruptions": 4.183, "waiting": 86}, {"eruptions": 2.217, "waiting": 50}, {"eruptions": 4.45, "waiting": 90}, {"eruptions": 1.883, "waiting": 54}, {"eruptions": 1.85, "waiting": 54}, {"eruptions": 4.283, "waiting": 77}, {"eruptions": 3.95, "waiting": 79}, {"eruptions": 2.333, "waiting": 64}, {"eruptions": 4.15, "waiting": 75}, {"eruptions": 2.35, "waiting": 47}, {"eruptions": 4.933, "waiting": 86}, {"eruptions": 2.9, "waiting": 63}, {"eruptions": 4.583, "waiting": 85}, {"eruptions": 3.833, "waiting": 82}, {"eruptions": 2.083, "waiting": 57}, {"eruptions": 4.367, "waiting": 82}, {"eruptions": 2.133, "waiting": 67}, {"eruptions": 4.35, "waiting": 74}, {"eruptions": 2.2, "waiting": 54}, {"eruptions": 4.45, "waiting": 83}, {"eruptions": 3.567, "waiting": 73}, {"eruptions": 4.5, "waiting": 73}, {"eruptions": 4.15, "waiting": 88}, {"eruptions": 3.817, "waiting": 80}, {"eruptions": 3.917, "waiting": 71}, {"eruptions": 4.45, "waiting": 83}, {"eruptions": 2, "waiting": 56}, {"eruptions": 4.283, "waiting": 79}, {"eruptions": 4.767, "waiting": 78}, {"eruptions": 4.533, "waiting": 84}, {"eruptions": 1.85, "waiting": 58}, {"eruptions": 4.25, "waiting": 83}, {"eruptions": 1.983, "waiting": 43}, {"eruptions": 2.25, "waiting": 60}, {"eruptions": 4.75, "waiting": 75}, {"eruptions": 4.117, "waiting": 81}, {"eruptions": 2.15, "waiting": 46}, {"eruptions": 4.417, "waiting": 90}, {"eruptions": 1.817, "waiting": 46}, {"eruptions": 4.467, "waiting": 74} ] ================================================ FILE: packages/showcase/examples/candlestick/candlestick-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XAxis, YAxis, LineSeries, FlexibleWidthXYPlot} from 'react-vis'; import Candlestick from './candlestick'; import './candlestick.scss'; /** * Generate random random for candle stick chart * @param {number} total - Total number of values. * @returns {Array} Array of data. */ function buildRandomBinnedData(total) { const result = Array(total) .fill(0) .map((x, i) => { const values = [ Math.random(), Math.random(), Math.random(), Math.random() ] .sort() .map(d => Math.floor(d * 100)); const y = (values[2] + values[1]) / 2; return { x: i, y, yHigh: values[3], yOpen: values[2], yClose: values[1], yLow: values[0], color: y < 25 ? '#EF5D28' : '#12939A', opacity: y > 75 ? 0.7 : 1 }; }); return result; } export default class CandlestickExample extends React.Component { state = { data: buildRandomBinnedData(30) }; render() { const {data} = this.state; return (
); } } ================================================ FILE: packages/showcase/examples/candlestick/candlestick.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {AbstractSeries} from 'react-vis'; const predefinedClassName = 'rv-xy-plot__series rv-xy-plot__series--candlestick'; class CandlestickSeries extends AbstractSeries { render() { const {className, data, marginLeft, marginTop} = this.props; if (!data) { return null; } const xFunctor = this._getAttributeFunctor('x'); const yFunctor = this._getAttributeFunctor('y'); const strokeFunctor = this._getAttributeFunctor('stroke') || this._getAttributeFunctor('color'); const fillFunctor = this._getAttributeFunctor('fill') || this._getAttributeFunctor('color'); const opacityFunctor = this._getAttributeFunctor('opacity'); const distance = Math.abs(xFunctor(data[1]) - xFunctor(data[0])) * 0.2; return ( {data.map((d, i) => { const xTrans = xFunctor(d); // Names of values borrowed from here https://en.wikipedia.org/wiki/Candlestick_chart const yHigh = yFunctor({...d, y: d.yHigh}); const yOpen = yFunctor({...d, y: d.yOpen}); const yClose = yFunctor({...d, y: d.yClose}); const yLow = yFunctor({...d, y: d.yLow}); const lineAttrs = { stroke: strokeFunctor && strokeFunctor(d) }; const xWidth = distance * 2; return ( this._valueClickHandler(d, e)} onMouseOver={e => this._valueMouseOverHandler(d, e)} onMouseOut={e => this._valueMouseOutHandler(d, e)} > ); })} ); } } CandlestickSeries.displayName = 'CandlestickSeries'; export default CandlestickSeries; ================================================ FILE: packages/showcase/examples/candlestick/candlestick.scss ================================================ .candlestick-example { width: 100%; .chart { width: 100%; } .dashed-example-line { stroke-dasharray: 2, 2; } } ================================================ FILE: packages/showcase/examples/force-directed-graph/force-directed-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {useState} from 'react'; import LesMisData from './les-mis-data.json'; import './force-directed.scss'; import ForceDirectedGraph from './force-directed-graph'; const makeStrength = () => Math.random() * 60 - 30; export default function ForceDirectedExample() { const [strength, setStrength] = useState(makeStrength); return (
); } ================================================ FILE: packages/showcase/examples/force-directed-graph/force-directed-graph.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {useState, useEffect} from 'react'; import {forceSimulation, forceLink, forceManyBody, forceCenter} from 'd3-force'; import {XYPlot, MarkSeriesCanvas, LineSeriesCanvas} from 'react-vis'; const colors = [ '#19CDD7', '#DDB27C', '#88572C', '#FF991F', '#F15C17', '#223F9A', '#DA70BF', '#4DC19C', '#12939A', '#B7885E', '#FFCB99', '#F89570', '#E79FD5', '#89DAC1' ]; /** * Create the list of nodes to render. * @returns {Array} Array of nodes. */ function generateSimulation(props) { const {data, height, width, maxSteps, strength} = props; if (!data) { return {nodes: [], links: []}; } // copy the data const nodes = data.nodes.map(d => ({...d})); const links = data.links.map(d => ({...d})); // build the simulation const simulation = forceSimulation(nodes) .force( 'link', forceLink().id(d => d.id) ) .force('charge', forceManyBody().strength(strength)) .force('center', forceCenter(width / 2, height / 2)) .stop(); simulation.force('link').links(links); const upperBound = Math.ceil( Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()) ); for (let i = 0; i < Math.min(maxSteps, upperBound); ++i) { simulation.tick(); } return {nodes, links}; } export default function ForceDirectedGraph(props) { const { className = '', height, width, animation, data = { nodes: [], links: [] }, maxSteps = 50, strength } = props; const [{nodes, links}, setData] = useState({ nodes: [], links: [] }); useEffect(() => { setData(generateSimulation({data, height, width, maxSteps, strength})); }, [data, height, width, maxSteps, strength]); return ( {links.map(({source, target}, index) => { return ( ); })} ); } ================================================ FILE: packages/showcase/examples/force-directed-graph/force-directed.scss ================================================ .candlestick-example { width: 100%; .chart { width: 100%; } .dashed-example-line { stroke-dasharray: 2, 2; } } ================================================ FILE: packages/showcase/examples/force-directed-graph/les-mis-data.json ================================================ { "nodes": [ {"id": "Myriel", "group": 1, "color": 1}, {"id": "Napoleon", "group": 1, "color": 1}, {"id": "Mlle.Baptistine", "group": 1, "color": 1}, {"id": "Mme.Magloire", "group": 1, "color": 1}, {"id": "CountessdeLo", "group": 1, "color": 1}, {"id": "Geborand", "group": 1, "color": 1}, {"id": "Champtercier", "group": 1, "color": 1}, {"id": "Cravatte", "group": 1, "color": 1}, {"id": "Count", "group": 1, "color": 1}, {"id": "OldMan", "group": 1, "color": 1}, {"id": "Labarre", "group": 2, "color": 2}, {"id": "Valjean", "group": 2, "color": 2}, {"id": "Marguerite", "group": 3, "color": 3}, {"id": "Mme.deR", "group": 2, "color": 2}, {"id": "Isabeau", "group": 2, "color": 2}, {"id": "Gervais", "group": 2, "color": 2}, {"id": "Tholomyes", "group": 3, "color": 3}, {"id": "Listolier", "group": 3, "color": 3}, {"id": "Fameuil", "group": 3, "color": 3}, {"id": "Blacheville", "group": 3, "color": 3}, {"id": "Favourite", "group": 3, "color": 3}, {"id": "Dahlia", "group": 3, "color": 3}, {"id": "Zephine", "group": 3, "color": 3}, {"id": "Fantine", "group": 3, "color": 3}, {"id": "Mme.Thenardier", "group": 4, "color": 4}, {"id": "Thenardier", "group": 4, "color": 4}, {"id": "Cosette", "group": 5, "color": 5}, {"id": "Javert", "group": 4, "color": 4}, {"id": "Fauchelevent", "group": 0, "color": 0}, {"id": "Bamatabois", "group": 2, "color": 2}, {"id": "Perpetue", "group": 3, "color": 3}, {"id": "Simplice", "group": 2, "color": 2}, {"id": "Scaufflaire", "group": 2, "color": 2}, {"id": "Woman1", "group": 2, "color": 2}, {"id": "Judge", "group": 2, "color": 2}, {"id": "Champmathieu", "group": 2, "color": 2}, {"id": "Brevet", "group": 2, "color": 2}, {"id": "Chenildieu", "group": 2, "color": 2}, {"id": "Cochepaille", "group": 2, "color": 2}, {"id": "Pontmercy", "group": 4, "color": 4}, {"id": "Boulatruelle", "group": 6, "color": 6}, {"id": "Eponine", "group": 4, "color": 4}, {"id": "Anzelma", "group": 4, "color": 4}, {"id": "Woman2", "group": 5, "color": 5}, {"id": "MotherInnocent", "group": 0, "color": 0}, {"id": "Gribier", "group": 0, "color": 0}, {"id": "Jondrette", "group": 7, "color": 7}, {"id": "Mme.Burgon", "group": 7, "color": 7}, {"id": "Gavroche", "group": 8, "color": 8}, {"id": "Gillenormand", "group": 5, "color": 5}, {"id": "Magnon", "group": 5, "color": 5}, {"id": "Mlle.Gillenormand", "group": 5, "color": 5}, {"id": "Mme.Pontmercy", "group": 5, "color": 5}, {"id": "Mlle.Vaubois", "group": 5, "color": 5}, {"id": "Lt.Gillenormand", "group": 5, "color": 5}, {"id": "Marius", "group": 8, "color": 8}, {"id": "BaronessT", "group": 5, "color": 5}, {"id": "Mabeuf", "group": 8, "color": 8}, {"id": "Enjolras", "group": 8, "color": 8}, {"id": "Combeferre", "group": 8, "color": 8}, {"id": "Prouvaire", "group": 8, "color": 8}, {"id": "Feuilly", "group": 8, "color": 8}, {"id": "Courfeyrac", "group": 8, "color": 8}, {"id": "Bahorel", "group": 8, "color": 8}, {"id": "Bossuet", "group": 8, "color": 8}, {"id": "Joly", "group": 8, "color": 8}, {"id": "Grantaire", "group": 8, "color": 8}, {"id": "MotherPlutarch", "group": 9, "color": 9}, {"id": "Gueulemer", "group": 4, "color": 4}, {"id": "Babet", "group": 4, "color": 4}, {"id": "Claquesous", "group": 4, "color": 4}, {"id": "Montparnasse", "group": 4, "color": 4}, {"id": "Toussaint", "group": 5, "color": 5}, {"id": "Child1", "group": 10, "color": 10}, {"id": "Child2", "group": 10, "color": 10}, {"id": "Brujon", "group": 4, "color": 4}, {"id": "Mme.Hucheloup", "group": 8, "color": 8} ], "links": [ {"source": "Napoleon", "target": "Myriel", "value": 1}, {"source": "Mlle.Baptistine", "target": "Myriel", "value": 8}, {"source": "Mme.Magloire", "target": "Myriel", "value": 10}, {"source": "Mme.Magloire", "target": "Mlle.Baptistine", "value": 6}, {"source": "CountessdeLo", "target": "Myriel", "value": 1}, {"source": "Geborand", "target": "Myriel", "value": 1}, {"source": "Champtercier", "target": "Myriel", "value": 1}, {"source": "Cravatte", "target": "Myriel", "value": 1}, {"source": "Count", "target": "Myriel", "value": 2}, {"source": "OldMan", "target": "Myriel", "value": 1}, {"source": "Valjean", "target": "Labarre", "value": 1}, {"source": "Valjean", "target": "Mme.Magloire", "value": 3}, {"source": "Valjean", "target": "Mlle.Baptistine", "value": 3}, {"source": "Valjean", "target": "Myriel", "value": 5}, {"source": "Marguerite", "target": "Valjean", "value": 1}, {"source": "Mme.deR", "target": "Valjean", "value": 1}, {"source": "Isabeau", "target": "Valjean", "value": 1}, {"source": "Gervais", "target": "Valjean", "value": 1}, {"source": "Listolier", "target": "Tholomyes", "value": 4}, {"source": "Fameuil", "target": "Tholomyes", "value": 4}, {"source": "Fameuil", "target": "Listolier", "value": 4}, {"source": "Blacheville", "target": "Tholomyes", "value": 4}, {"source": "Blacheville", "target": "Listolier", "value": 4}, {"source": "Blacheville", "target": "Fameuil", "value": 4}, {"source": "Favourite", "target": "Tholomyes", "value": 3}, {"source": "Favourite", "target": "Listolier", "value": 3}, {"source": "Favourite", "target": "Fameuil", "value": 3}, {"source": "Favourite", "target": "Blacheville", "value": 4}, {"source": "Dahlia", "target": "Tholomyes", "value": 3}, {"source": "Dahlia", "target": "Listolier", "value": 3}, {"source": "Dahlia", "target": "Fameuil", "value": 3}, {"source": "Dahlia", "target": "Blacheville", "value": 3}, {"source": "Dahlia", "target": "Favourite", "value": 5}, {"source": "Zephine", "target": "Tholomyes", "value": 3}, {"source": "Zephine", "target": "Listolier", "value": 3}, {"source": "Zephine", "target": "Fameuil", "value": 3}, {"source": "Zephine", "target": "Blacheville", "value": 3}, {"source": "Zephine", "target": "Favourite", "value": 4}, {"source": "Zephine", "target": "Dahlia", "value": 4}, {"source": "Fantine", "target": "Tholomyes", "value": 3}, {"source": "Fantine", "target": "Listolier", "value": 3}, {"source": "Fantine", "target": "Fameuil", "value": 3}, {"source": "Fantine", "target": "Blacheville", "value": 3}, {"source": "Fantine", "target": "Favourite", "value": 4}, {"source": "Fantine", "target": "Dahlia", "value": 4}, {"source": "Fantine", "target": "Zephine", "value": 4}, {"source": "Fantine", "target": "Marguerite", "value": 2}, {"source": "Fantine", "target": "Valjean", "value": 9}, {"source": "Mme.Thenardier", "target": "Fantine", "value": 2}, {"source": "Mme.Thenardier", "target": "Valjean", "value": 7}, {"source": "Thenardier", "target": "Mme.Thenardier", "value": 13}, {"source": "Thenardier", "target": "Fantine", "value": 1}, {"source": "Thenardier", "target": "Valjean", "value": 12}, {"source": "Cosette", "target": "Mme.Thenardier", "value": 4}, {"source": "Cosette", "target": "Valjean", "value": 31}, {"source": "Cosette", "target": "Tholomyes", "value": 1}, {"source": "Cosette", "target": "Thenardier", "value": 1}, {"source": "Javert", "target": "Valjean", "value": 17}, {"source": "Javert", "target": "Fantine", "value": 5}, {"source": "Javert", "target": "Thenardier", "value": 5}, {"source": "Javert", "target": "Mme.Thenardier", "value": 1}, {"source": "Javert", "target": "Cosette", "value": 1}, {"source": "Fauchelevent", "target": "Valjean", "value": 8}, {"source": "Fauchelevent", "target": "Javert", "value": 1}, {"source": "Bamatabois", "target": "Fantine", "value": 1}, {"source": "Bamatabois", "target": "Javert", "value": 1}, {"source": "Bamatabois", "target": "Valjean", "value": 2}, {"source": "Perpetue", "target": "Fantine", "value": 1}, {"source": "Simplice", "target": "Perpetue", "value": 2}, {"source": "Simplice", "target": "Valjean", "value": 3}, {"source": "Simplice", "target": "Fantine", "value": 2}, {"source": "Simplice", "target": "Javert", "value": 1}, {"source": "Scaufflaire", "target": "Valjean", "value": 1}, {"source": "Woman1", "target": "Valjean", "value": 2}, {"source": "Woman1", "target": "Javert", "value": 1}, {"source": "Judge", "target": "Valjean", "value": 3}, {"source": "Judge", "target": "Bamatabois", "value": 2}, {"source": "Champmathieu", "target": "Valjean", "value": 3}, {"source": "Champmathieu", "target": "Judge", "value": 3}, {"source": "Champmathieu", "target": "Bamatabois", "value": 2}, {"source": "Brevet", "target": "Judge", "value": 2}, {"source": "Brevet", "target": "Champmathieu", "value": 2}, {"source": "Brevet", "target": "Valjean", "value": 2}, {"source": "Brevet", "target": "Bamatabois", "value": 1}, {"source": "Chenildieu", "target": "Judge", "value": 2}, {"source": "Chenildieu", "target": "Champmathieu", "value": 2}, {"source": "Chenildieu", "target": "Brevet", "value": 2}, {"source": "Chenildieu", "target": "Valjean", "value": 2}, {"source": "Chenildieu", "target": "Bamatabois", "value": 1}, {"source": "Cochepaille", "target": "Judge", "value": 2}, {"source": "Cochepaille", "target": "Champmathieu", "value": 2}, {"source": "Cochepaille", "target": "Brevet", "value": 2}, {"source": "Cochepaille", "target": "Chenildieu", "value": 2}, {"source": "Cochepaille", "target": "Valjean", "value": 2}, {"source": "Cochepaille", "target": "Bamatabois", "value": 1}, {"source": "Pontmercy", "target": "Thenardier", "value": 1}, {"source": "Boulatruelle", "target": "Thenardier", "value": 1}, {"source": "Eponine", "target": "Mme.Thenardier", "value": 2}, {"source": "Eponine", "target": "Thenardier", "value": 3}, {"source": "Anzelma", "target": "Eponine", "value": 2}, {"source": "Anzelma", "target": "Thenardier", "value": 2}, {"source": "Anzelma", "target": "Mme.Thenardier", "value": 1}, {"source": "Woman2", "target": "Valjean", "value": 3}, {"source": "Woman2", "target": "Cosette", "value": 1}, {"source": "Woman2", "target": "Javert", "value": 1}, {"source": "MotherInnocent", "target": "Fauchelevent", "value": 3}, {"source": "MotherInnocent", "target": "Valjean", "value": 1}, {"source": "Gribier", "target": "Fauchelevent", "value": 2}, {"source": "Mme.Burgon", "target": "Jondrette", "value": 1}, {"source": "Gavroche", "target": "Mme.Burgon", "value": 2}, {"source": "Gavroche", "target": "Thenardier", "value": 1}, {"source": "Gavroche", "target": "Javert", "value": 1}, {"source": "Gavroche", "target": "Valjean", "value": 1}, {"source": "Gillenormand", "target": "Cosette", "value": 3}, {"source": "Gillenormand", "target": "Valjean", "value": 2}, {"source": "Magnon", "target": "Gillenormand", "value": 1}, {"source": "Magnon", "target": "Mme.Thenardier", "value": 1}, {"source": "Mlle.Gillenormand", "target": "Gillenormand", "value": 9}, {"source": "Mlle.Gillenormand", "target": "Cosette", "value": 2}, {"source": "Mlle.Gillenormand", "target": "Valjean", "value": 2}, {"source": "Mme.Pontmercy", "target": "Mlle.Gillenormand", "value": 1}, {"source": "Mme.Pontmercy", "target": "Pontmercy", "value": 1}, {"source": "Mlle.Vaubois", "target": "Mlle.Gillenormand", "value": 1}, {"source": "Lt.Gillenormand", "target": "Mlle.Gillenormand", "value": 2}, {"source": "Lt.Gillenormand", "target": "Gillenormand", "value": 1}, {"source": "Lt.Gillenormand", "target": "Cosette", "value": 1}, {"source": "Marius", "target": "Mlle.Gillenormand", "value": 6}, {"source": "Marius", "target": "Gillenormand", "value": 12}, {"source": "Marius", "target": "Pontmercy", "value": 1}, {"source": "Marius", "target": "Lt.Gillenormand", "value": 1}, {"source": "Marius", "target": "Cosette", "value": 21}, {"source": "Marius", "target": "Valjean", "value": 19}, {"source": "Marius", "target": "Tholomyes", "value": 1}, {"source": "Marius", "target": "Thenardier", "value": 2}, {"source": "Marius", "target": "Eponine", "value": 5}, {"source": "Marius", "target": "Gavroche", "value": 4}, {"source": "BaronessT", "target": "Gillenormand", "value": 1}, {"source": "BaronessT", "target": "Marius", "value": 1}, {"source": "Mabeuf", "target": "Marius", "value": 1}, {"source": "Mabeuf", "target": "Eponine", "value": 1}, {"source": "Mabeuf", "target": "Gavroche", "value": 1}, {"source": "Enjolras", "target": "Marius", "value": 7}, {"source": "Enjolras", "target": "Gavroche", "value": 7}, {"source": "Enjolras", "target": "Javert", "value": 6}, {"source": "Enjolras", "target": "Mabeuf", "value": 1}, {"source": "Enjolras", "target": "Valjean", "value": 4}, {"source": "Combeferre", "target": "Enjolras", "value": 15}, {"source": "Combeferre", "target": "Marius", "value": 5}, {"source": "Combeferre", "target": "Gavroche", "value": 6}, {"source": "Combeferre", "target": "Mabeuf", "value": 2}, {"source": "Prouvaire", "target": "Gavroche", "value": 1}, {"source": "Prouvaire", "target": "Enjolras", "value": 4}, {"source": "Prouvaire", "target": "Combeferre", "value": 2}, {"source": "Feuilly", "target": "Gavroche", "value": 2}, {"source": "Feuilly", "target": "Enjolras", "value": 6}, {"source": "Feuilly", "target": "Prouvaire", "value": 2}, {"source": "Feuilly", "target": "Combeferre", "value": 5}, {"source": "Feuilly", "target": "Mabeuf", "value": 1}, {"source": "Feuilly", "target": "Marius", "value": 1}, {"source": "Courfeyrac", "target": "Marius", "value": 9}, {"source": "Courfeyrac", "target": "Enjolras", "value": 17}, {"source": "Courfeyrac", "target": "Combeferre", "value": 13}, {"source": "Courfeyrac", "target": "Gavroche", "value": 7}, {"source": "Courfeyrac", "target": "Mabeuf", "value": 2}, {"source": "Courfeyrac", "target": "Eponine", "value": 1}, {"source": "Courfeyrac", "target": "Feuilly", "value": 6}, {"source": "Courfeyrac", "target": "Prouvaire", "value": 3}, {"source": "Bahorel", "target": "Combeferre", "value": 5}, {"source": "Bahorel", "target": "Gavroche", "value": 5}, {"source": "Bahorel", "target": "Courfeyrac", "value": 6}, {"source": "Bahorel", "target": "Mabeuf", "value": 2}, {"source": "Bahorel", "target": "Enjolras", "value": 4}, {"source": "Bahorel", "target": "Feuilly", "value": 3}, {"source": "Bahorel", "target": "Prouvaire", "value": 2}, {"source": "Bahorel", "target": "Marius", "value": 1}, {"source": "Bossuet", "target": "Marius", "value": 5}, {"source": "Bossuet", "target": "Courfeyrac", "value": 12}, {"source": "Bossuet", "target": "Gavroche", "value": 5}, {"source": "Bossuet", "target": "Bahorel", "value": 4}, {"source": "Bossuet", "target": "Enjolras", "value": 10}, {"source": "Bossuet", "target": "Feuilly", "value": 6}, {"source": "Bossuet", "target": "Prouvaire", "value": 2}, {"source": "Bossuet", "target": "Combeferre", "value": 9}, {"source": "Bossuet", "target": "Mabeuf", "value": 1}, {"source": "Bossuet", "target": "Valjean", "value": 1}, {"source": "Joly", "target": "Bahorel", "value": 5}, {"source": "Joly", "target": "Bossuet", "value": 7}, {"source": "Joly", "target": "Gavroche", "value": 3}, {"source": "Joly", "target": "Courfeyrac", "value": 5}, {"source": "Joly", "target": "Enjolras", "value": 5}, {"source": "Joly", "target": "Feuilly", "value": 5}, {"source": "Joly", "target": "Prouvaire", "value": 2}, {"source": "Joly", "target": "Combeferre", "value": 5}, {"source": "Joly", "target": "Mabeuf", "value": 1}, {"source": "Joly", "target": "Marius", "value": 2}, {"source": "Grantaire", "target": "Bossuet", "value": 3}, {"source": "Grantaire", "target": "Enjolras", "value": 3}, {"source": "Grantaire", "target": "Combeferre", "value": 1}, {"source": "Grantaire", "target": "Courfeyrac", "value": 2}, {"source": "Grantaire", "target": "Joly", "value": 2}, {"source": "Grantaire", "target": "Gavroche", "value": 1}, {"source": "Grantaire", "target": "Bahorel", "value": 1}, {"source": "Grantaire", "target": "Feuilly", "value": 1}, {"source": "Grantaire", "target": "Prouvaire", "value": 1}, {"source": "MotherPlutarch", "target": "Mabeuf", "value": 3}, {"source": "Gueulemer", "target": "Thenardier", "value": 5}, {"source": "Gueulemer", "target": "Valjean", "value": 1}, {"source": "Gueulemer", "target": "Mme.Thenardier", "value": 1}, {"source": "Gueulemer", "target": "Javert", "value": 1}, {"source": "Gueulemer", "target": "Gavroche", "value": 1}, {"source": "Gueulemer", "target": "Eponine", "value": 1}, {"source": "Babet", "target": "Thenardier", "value": 6}, {"source": "Babet", "target": "Gueulemer", "value": 6}, {"source": "Babet", "target": "Valjean", "value": 1}, {"source": "Babet", "target": "Mme.Thenardier", "value": 1}, {"source": "Babet", "target": "Javert", "value": 2}, {"source": "Babet", "target": "Gavroche", "value": 1}, {"source": "Babet", "target": "Eponine", "value": 1}, {"source": "Claquesous", "target": "Thenardier", "value": 4}, {"source": "Claquesous", "target": "Babet", "value": 4}, {"source": "Claquesous", "target": "Gueulemer", "value": 4}, {"source": "Claquesous", "target": "Valjean", "value": 1}, {"source": "Claquesous", "target": "Mme.Thenardier", "value": 1}, {"source": "Claquesous", "target": "Javert", "value": 1}, {"source": "Claquesous", "target": "Eponine", "value": 1}, {"source": "Claquesous", "target": "Enjolras", "value": 1}, {"source": "Montparnasse", "target": "Javert", "value": 1}, {"source": "Montparnasse", "target": "Babet", "value": 2}, {"source": "Montparnasse", "target": "Gueulemer", "value": 2}, {"source": "Montparnasse", "target": "Claquesous", "value": 2}, {"source": "Montparnasse", "target": "Valjean", "value": 1}, {"source": "Montparnasse", "target": "Gavroche", "value": 1}, {"source": "Montparnasse", "target": "Eponine", "value": 1}, {"source": "Montparnasse", "target": "Thenardier", "value": 1}, {"source": "Toussaint", "target": "Cosette", "value": 2}, {"source": "Toussaint", "target": "Javert", "value": 1}, {"source": "Toussaint", "target": "Valjean", "value": 1}, {"source": "Child1", "target": "Gavroche", "value": 2}, {"source": "Child2", "target": "Gavroche", "value": 2}, {"source": "Child2", "target": "Child1", "value": 3}, {"source": "Brujon", "target": "Babet", "value": 3}, {"source": "Brujon", "target": "Gueulemer", "value": 3}, {"source": "Brujon", "target": "Thenardier", "value": 3}, {"source": "Brujon", "target": "Gavroche", "value": 1}, {"source": "Brujon", "target": "Eponine", "value": 1}, {"source": "Brujon", "target": "Claquesous", "value": 1}, {"source": "Brujon", "target": "Montparnasse", "value": 1}, {"source": "Mme.Hucheloup", "target": "Bossuet", "value": 1}, {"source": "Mme.Hucheloup", "target": "Joly", "value": 1}, {"source": "Mme.Hucheloup", "target": "Grantaire", "value": 1}, {"source": "Mme.Hucheloup", "target": "Bahorel", "value": 1}, {"source": "Mme.Hucheloup", "target": "Courfeyrac", "value": 1}, {"source": "Mme.Hucheloup", "target": "Gavroche", "value": 1}, {"source": "Mme.Hucheloup", "target": "Enjolras", "value": 1} ] } ================================================ FILE: packages/showcase/examples/iris-dashboard/iris-dashboard.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XAxis, YAxis, XYPlot, MarkSeriesCanvas, Borders, Highlight } from 'react-vis'; import Iris from '../../datasets/iris.json'; import './iris-dashboard.scss'; const AXES = ['sepal length', 'sepal width', 'petal length', 'petal width']; const SPECIES = ['setosa', 'versicolor', 'virginica']; const SIZE = 200; export default class IrisDashboard extends React.Component { state = { filters: AXES.reduce((acc, axis) => { acc[axis] = {min: null, max: null}; return acc; }, {}) }; render() { const {filters} = this.state; const data = Iris.map(d => { const unselected = AXES.some(key => { const filter = filters[key]; return ( filter.min !== filter.max && (filter.min > d[key] || filter.max < d[key]) ); }); return {...d, selected: !unselected}; }); return (
{AXES.map(yAxis => { return (
{AXES.map(xAxis => { if (xAxis === yAxis) { return (

{xAxis}

); } const updateFilter = area => { if (!area) { filters[xAxis] = {min: null, max: null}; filters[yAxis] = {min: null, max: null}; this.setState({filters}); } else { const {left, right, top, bottom} = area; filters[xAxis] = {min: left, max: right}; filters[yAxis] = {min: bottom, max: top}; } this.setState({filters}); }; return ( ({ x: Number(d[xAxis]), y: Number(d[yAxis]), color: d.species, selected: d.selected }))} colorType="category" colorDomain={SPECIES} colorRange={['#19CDD7', 'red', '#88572C']} getOpacity={d => (d.selected ? 1 : 0.1)} size={2} /> ); })}
); })}
); } } ================================================ FILE: packages/showcase/examples/iris-dashboard/iris-dashboard.scss ================================================ .iris-dasboard-example { align-items: center; display: flex; flex-direction: column; width: 100%; .chart-row { display: flex; } .rv-xy-plot__axis__title text { font-size: 8px; } .axis-label { align-items: center; display: flex; justify-content: center; } } ================================================ FILE: packages/showcase/examples/responsive-vis/responsive-bar-chart.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {AreaSeries, HorizontalBarSeries, XAxis, XYPlot, YAxis} from 'react-vis'; import {filterFeatures, getPPP} from './responsive-vis-utils'; // range constants const VERY_LOW_RANGE = [0, 0.08]; const LOW_RANGE = [0, 0.7]; const HIGH_RANGE = [0.7, Infinity]; export const BARCHART_FEATURES = [ {min: -Infinity, max: Infinity, name: 'xaxis', group: 0}, {min: VERY_LOW_RANGE[0], max: VERY_LOW_RANGE[1], name: 'yaxis', group: 1}, {min: LOW_RANGE[0], max: LOW_RANGE[1], name: 'bars', group: 2}, {min: HIGH_RANGE[0], max: HIGH_RANGE[1], name: 'area', group: 2} ]; function updateDataForArea(data, ppp) { // Use the PPP ratio as the step to sample the data const step = Math.round(ppp); const sample = []; let index = data.length - 1; while (index >= 0) { const dataPoint = data[index]; sample.unshift({...dataPoint, y: sample.length - 1}); index -= step; } return sample; } export function getFeatures(props) { const {data, height, margin, width} = props; const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; const ppp = getPPP(innerWidth, innerHeight, data, 'HEIGHT'); return filterFeatures(BARCHART_FEATURES, ppp); } export default function ResponsiveBarChart(props) { const {data, height, margin, width} = props; const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; const ppp = getPPP(innerWidth, innerHeight, data, 'HEIGHT'); const featuresToRender = filterFeatures(BARCHART_FEATURES, ppp); const updatedData = featuresToRender.area ? updateDataForArea(data, ppp) : data; return (
{featuresToRender.xaxis && } {featuresToRender.yaxis && } {featuresToRender.bars && ( )} {featuresToRender.area && ( )}
); } ================================================ FILE: packages/showcase/examples/responsive-vis/responsive-scatterplot.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React, {useState, useEffect} from 'react'; import { XYPlot, MarkSeries, HeatmapSeries, XAxis, YAxis, Hint, LabelSeries } from 'react-vis'; import { filterFeatures, computeRadius, getPPP, transformToBinData, manicureData } from './responsive-vis-utils'; // range constants const SUPER_LOW_RANGE = [0, 1e-4]; const VERY_LOW_RANGE = [0, 8e-4]; const LOW_RANGE = [0, 5e-3]; const MED_LOW_RANGE = [1e-4, 5e-3]; const MED_RANGE = [5e-3, 1e-2]; const HIGH_RANGE = [1e-2, Infinity]; const MED_HIGH_RANGE = [MED_RANGE[0], HIGH_RANGE[1]]; export const SCATTERPLOT_FEATURES = [ {min: -Infinity, max: Infinity, name: 'axes', group: 0}, {min: SUPER_LOW_RANGE[0], max: SUPER_LOW_RANGE[1], name: 'labels', group: 1}, { min: VERY_LOW_RANGE[0], max: VERY_LOW_RANGE[1], name: 'pointSelection', group: 1 }, {min: LOW_RANGE[0], max: LOW_RANGE[1], name: 'points', group: 3}, {min: MED_LOW_RANGE[0], max: MED_LOW_RANGE[1], name: 'tooltips', group: 2}, {min: MED_HIGH_RANGE[0], max: MED_HIGH_RANGE[1], name: 'bins', group: 3}, {min: MED_HIGH_RANGE[0], max: MED_HIGH_RANGE[1], name: 'bintips', group: 2}, { min: MED_HIGH_RANGE[0], max: MED_HIGH_RANGE[1], name: 'binSelection', group: 1 } ]; export function getFeatures(props) { const {data, height, margin, width} = props; const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; const ppp = getPPP(innerWidth, innerHeight, data, 'HEIGHT'); return filterFeatures(SCATTERPLOT_FEATURES, ppp); } export default function ResponsiveScatterplot(props) { const {data, height, margin, width} = props; const [binData, setBinData] = useState([]); const [hoveredPoint, setHoveredPoint] = useState(false); const [selectedPoints, setSelectedPoints] = useState([]); useEffect(() => { setBinData(transformToBinData(data, width, height)); }, [data, width, height]); // const {binData, hoveredPoint, selectedPoints} = this.state; function select(accessor) { return (value, e) => { e.event.stopPropagation(); let foundValue = false; const updatedSelectedPoints = selectedPoints.filter(row => { if (accessor(row) === accessor(value)) { foundValue = true; } return accessor(row) !== accessor(value); }); if (!foundValue) { updatedSelectedPoints.push(value); } setSelectedPoints(updatedSelectedPoints); }; } const innerWidth = width - margin.left - margin.right; const innerHeight = height - margin.top - margin.bottom; const ppp = getPPP(innerWidth, innerHeight, data, 'TWOD'); const featuresToRender = filterFeatures(SCATTERPLOT_FEATURES, ppp); const rememberVal = featuresToRender.pointSelection || featuresToRender.tooltips; const rememberBin = featuresToRender.bintips || featuresToRender.binSelection; const pointRadii = computeRadius(data, innerWidth, innerHeight); const showHint = (featuresToRender.tooltips || featuresToRender.bintips) && hoveredPoint; return (
{featuresToRender.axes && } {featuresToRender.axes && } {featuresToRender.bins && ( setHoveredPoint(value) : null } onValueMouseOut={rememberBin ? () => setHoveredPoint(null) : null} onValueClick={ featuresToRender.binSelection ? select(d => `${d.x}-${d.y}`) : null } data={manicureData(binData, hoveredPoint, selectedPoints, true)} /> )} {featuresToRender.points && ( setHoveredPoint(value) : null } onValueMouseOut={rememberVal ? () => setHoveredPoint(null) : null} onValueClick={ featuresToRender.pointSelection ? select(d => d.label) : null } data={manicureData(data, hoveredPoint, selectedPoints, false)} /> )} {showHint && } {featuresToRender.labels && ( )}
); } ================================================ FILE: packages/showcase/examples/responsive-vis/responsive-vis-example.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React, {useState} from 'react'; import {createData, getPPP} from './responsive-vis-utils'; import ResponsiveScatterplot, { getFeatures as getScatterplotFeatures } from './responsive-scatterplot'; import ResponsiveBarChart, { getFeatures as getBarFeatures } from './responsive-bar-chart'; import 'styles/examples.scss'; import './responsive-vis.scss'; const ASPECT_RATIO = 1.2; const EXAMPLE_MARGIN = {left: 60, top: 60, bottom: 50, right: 50}; export default function ResponsiveVisDemo() { const [chartType, setChartType] = useState('barChart'); const [data, setData] = useState(() => createData(5, true)); const [visSize, setVisSize] = useState(400); const [dataSize, setDataSize] = useState(1); const ResponsiveChartType = chartType === 'barChart' ? ResponsiveBarChart : ResponsiveScatterplot; const handleTypeClick = chartType => () => { setChartType(chartType); setData(createData(~~Math.pow(10, dataSize), chartType === 'barChart')); }; const width = visSize; const height = visSize * ASPECT_RATIO; const ppp = getPPP(width, height, data, 'TWOD'); const featuresProps = {width, height, data, margin: EXAMPLE_MARGIN}; const featuresToRender = (chartType === 'barChart' ? getBarFeatures : getScatterplotFeatures)(featuresProps); return (
{`Points Per Pixel: ${ppp}`}
{`Features: ${Object.keys(featuresToRender).join(', ')}`}
Scatterplot
BarChart
{`Data Size: ${~~Math.pow(10, dataSize)}`} { setDataSize(e.target.value); setData( createData( ~~Math.pow(10, e.target.value), chartType === 'barChart' ) ); }} type="range" min={1} max={6} step={0.1} value={dataSize} /> {`Visualization size: ${visSize}`} setVisSize(~~e.target.value)} type="range" min={100} max={1000} value={visSize} />
); } ================================================ FILE: packages/showcase/examples/responsive-vis/responsive-vis-utils.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import {randomNormal} from 'd3-random'; import {scaleLinear, scaleQuantize, scaleSqrt} from 'd3-scale'; import {max, range} from 'd3-array'; // create fake data export const createData = (dataSize, barChart = true) => { const gen1 = randomNormal(70, 15); const gen2 = randomNormal(50, 8); const gen = i => (i % 3 ? gen1() : gen2()); const data = new Array(dataSize).fill(0).map((d, index) => ({ x: gen(index), y: barChart ? `Point-${index}` : gen(index), label: `Point ${index}`, color: '#12939A' })); if (barChart) { data.sort((a, b) => b.x - a.x); } return data; }; export function filterFeatures(features, ppp) { return features.reduce((res, feature) => { if (ppp < feature.min || ppp > feature.max) { return res; } res[feature.name] = true; return res; }, {}); } export function computeRadius(data, width, height) { const targetPPP = 2e-2; const radiusDomain = [2, 12]; const r = Math.sqrt((targetPPP * width * height) / data.length); return Math.min(radiusDomain[1], Math.max(radiusDomain[0], r)); } export function getPPP(w, h, data, dimensionality) { // what is TWOD? const pixels = dimensionality === 'TWOD' ? w * h : dimensionality === 'WIDTH' ? w : h; return data.length / pixels; } function generateDomain(data) { return data.reduce( (res, row) => ({ xMin: Math.min(res.xMin, row.x), xMax: Math.max(res.xMax, row.x), yMin: Math.min(res.yMin, row.y), yMax: Math.max(res.yMax, row.y) }), {xMin: Infinity, xMax: -Infinity, yMin: Infinity, yMax: -Infinity} ); } export function transformToBinData(data, width, height) { const targetPPP = 5e-3; const binCount = ~~Math.sqrt(width * height * targetPPP); const domains = generateDomain(data); const xBin = scaleQuantize() .domain([domains.xMin, domains.xMax]) .range(range(binCount)); const yBin = scaleQuantize() .domain([domains.yMin, domains.yMax]) .range(range(binCount)); const binData = new Array(binCount * binCount).fill(0); data.forEach(d => { // compute the "address" of the bin in the binData hash const index = xBin(d.x) + binCount * yBin(d.y); binData[index]++; }); const maxCount = max(binData); const color = scaleSqrt() .domain([0, maxCount]) .range(['#fff', '#12939A']); return binData.map((d, index) => { const x = index % binCount; const y = ~~(index / binCount); return {x, y, color: color(d), count: d, maxCount}; }); } function transformColor(data, hovered = [], useRange = false) { const samplePoint = data[0]; const color = useRange ? scaleLinear() .domain([0, samplePoint.maxCount]) .range(['#fff', '#EF5D28']) : () => '#EF5D28'; const hoveredPoints = hovered.reduce((res, row) => { res[useRange ? `${row.x}-${row.y}` : row.label] = true; return res; }, {}); return data.map(row => { if (hoveredPoints[useRange ? `${row.x}-${row.y}` : row.label]) { return {...row, color: color(row.count)}; } return row; }); } export function manicureData(data, hoveredPoint, selectedPoints, useRange) { if (!hoveredPoint && selectedPoints.length === 0) { return data; } const concatedData = !hoveredPoint ? selectedPoints : selectedPoints.concat([hoveredPoint]); return transformColor(data, concatedData, useRange); } ================================================ FILE: packages/showcase/examples/responsive-vis/responsive-vis.scss ================================================ .responsive-vis-example { display: flex; flex-direction: column; .responsive-controls { display: flex; flex-direction: column; max-width: 400px; } .chart-type-selector { display: flex; justify-content: space-around; * { cursor: pointer; } } .selected-chart-type { font-weight: 600; text-decoration: underline; } .responsive-vis { display: flex; justify-content: center; } .responsive-bar-chart { display: flex; justify-content: center; .rv-hint { color: #000; } } .points-per-pixel-label { font-size: 32px; white-space: nowrap; } .features-label { font-size: 24px; white-space: nowrap; } .controls-wrapper { align-items: center; display: flex; justify-content: center; width: 100%; } } ================================================ FILE: packages/showcase/examples/streamgraph/streamgraph-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; import {stack as d3Stack, stackOffsetWiggle} from 'd3-shape'; import {range, transpose} from 'd3-array'; import {FlexibleWidthXYPlot, AreaSeries} from 'react-vis'; import './streamgraph-example.scss'; const NUMBER_OF_LAYERS = 20; const SAMPLES_PER_LAYER = 200; const BUMPS_PER_LAYER = 10; const bump = (aggregatingData, samplesPerLayer) => { const x = 1 / (0.1 + Math.random()); const y = 2 * Math.random() - 0.5; const z = 10 / (0.1 + Math.random()); return aggregatingData.map((v, i) => { const w = (i / samplesPerLayer - y) * z; return v + x * Math.exp(-w * w); }); }; // Inspired by Bostock's version of Lee Byron’s test data generator. function bumps(samplesPerLayer, bumpsPerLayer) { const dataOutline = new Array(samplesPerLayer).fill(0); return range(bumpsPerLayer).reduce( res => bump(res, samplesPerLayer), dataOutline ); } function generateData() { const stack = d3Stack() .keys(range(NUMBER_OF_LAYERS)) .offset(stackOffsetWiggle); const transposed = transpose( range(NUMBER_OF_LAYERS).map(() => bumps(SAMPLES_PER_LAYER, BUMPS_PER_LAYER)) ); return stack(transposed).map(series => series.map((row, x) => ({x, y0: row[0], y: row[1]})) ); } class StreamgraphExample extends React.Component { state = { data: generateData(), hoveredIndex: false }; render() { const {forFrontPage} = this.props; const {data, hoveredIndex} = this.state; return (
{!forFrontPage && ( )}
this.setState({hoveredIndex: false})} height={300} > {data.map((series, index) => ( this.setState({hoveredIndex: index})} data={series} /> ))}
); } } StreamgraphExample.propTypes = { forFrontPage: PropTypes.bool }; export default StreamgraphExample; ================================================ FILE: packages/showcase/examples/streamgraph/streamgraph-example.scss ================================================ .highlighted-stream { stroke: #fff !important; stroke-width: 5px; } .streamgraph .rv-xy-plot__series--line { cursor: pointer; } .markdown-example .streamgraph-example { display: flex; flex-direction: column; width: 100%; } .streamgraph-example { min-width: 400px; width: 100%; } ================================================ FILE: packages/showcase/flexible/flexible-examples.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { VerticalBarSeries, FlexibleWidthXYPlot, FlexibleHeightXYPlot, FlexibleXYPlot } from 'react-vis'; const data = [ {x: 0, y: 8}, {x: 1, y: 5}, {x: 2, y: 4}, {x: 3, y: 9}, {x: 4, y: 1}, {x: 5, y: 7}, {x: 6, y: 6}, {x: 7, y: 3}, {x: 8, y: 2} ]; const defaultProps = { margin: {top: 10, left: 10, right: 10, bottom: 10} }; export const FlexibleCharts = ({height, width}) => (
Flexible width - fixed height
Flexible height - fixed width
Flexible width and height
); ================================================ FILE: packages/showcase/index.html ================================================ react-vis examples ================================================ FILE: packages/showcase/index.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import sourceLinker from './showcase-components/source-linker'; import {SHOWCASE_LINKS} from './showcase-links'; import ComplexChart from './plot/complex-chart'; import LineChart from './plot/line-chart'; import LineChartManyColors from './color/line-chart-many-colors'; import LineChartCanvas from './plot/line-chart-canvas'; import LineChartWithStyle from './plot/line-chart-with-style'; import LineMarkChart from './plot/linemark-chart'; import LineSeriesCanvasNearestXYExample from './plot/line-series-canvas-nearest-xy-example'; import BarChart from './plot/bar-chart'; import BigBaseBarChart from './plot/big-base-bar-chart'; import DifferenceChart from './plot/difference-chart'; import StackedVerticalBarChart from './plot/stacked-vertical-bar-chart'; import LabeledStackedVerticalBarChart from './plot/labeled-stacked-vertical-bar-chart'; import StackedHorizontalBarChart from './plot/stacked-horizontal-bar-chart'; import ClusteredStackedVerticalBarChart from './plot/clustered-stacked-bar-chart'; import StackedHistogram from './plot/stacked-histogram'; import Histogram from './plot/histogram'; import AreaChart from './plot/area-chart'; import AreaChartElevated from './plot/area-chart-elevated'; import ScatterplotChart from './plot/scatterplot'; import WhiskerChart from './plot/whisker-chart.js'; import CustomSVGExample from './plot/custom-svg-example'; import CustomSVGRootLevel from './plot/custom-svg-root-level'; import CustomSVGAllTheMarks from './plot/custom-svg-all-the-marks'; import FauxScatterplotChart from './plot/faux-radial-scatterplot'; import ScatterplotCanvas from './plot/scatterplot-canvas'; import HeatmapChart from './plot/heatmap-chart'; import HexHeatmap from './plot/hex-heatmap'; import HexbinSizeExample from './plot/hexbin-size-example'; import LabeledHeatmap from './plot/labeled-heatmap'; import ContourSeriesExample from './plot/contour-series-example'; import WidthHeightMarginChart from './plot/width-height-margin'; import CustomScales from './plot/custom-scales'; import AxisWithTurnedLabels from './plot/axis-with-turned-labels'; import MixedStackedChart from './plot/mixed-stacked-chart'; import GridLinesChart from './plot/grid'; import EnergySankey from './sankey/energy-sankey'; import VornoiSankey from './sankey/voronoi'; import BasicSankey from './sankey/basic'; import { SensibleDefaults, ColorInXYPlot, ColorSpecificity, CategoryColorAtMarkLevel, CategoryColorAtMarkLevelCustomPalette, CategoryColorAtMarkLevelFixedStroke, GradientCharts, LinearColorAtMarkLevel, LinearColorAtMarkLevelNoPalette, LineSeriesMarkSeries, LiteralColorAtMarkLevel, CategoryColorAtSeriesLevel, LinearColorAtSeriesLevel, LiteralColorAtSeriesLevel, ReactVis5, ReactVis20, Continuous, CustomPalette } from './color/mini-color-examples'; import {MiniCharts} from './data/mini-data-examples'; import { LineChartMouseOverSeries, LineChartMouseOverXY, LinkedCharts, ScatterPlotOnNearestXY } from './interaction/interaction-examples'; import {FlexibleCharts} from './flexible/flexible-examples'; import AxisOn0 from './axes/axis-on-0'; import CustomAxesOrientation from './axes/custom-axes-orientation'; import CustomAxisChart from './axes/custom-axis'; import CustomAxisTickFormat from './axes/custom-axis-tick-format'; import CustomAxisTickElement from './axes/custom-axis-tick-element'; import CustomAxes from './axes/custom-axes'; import DecorativeAxisCrissCross from './axes/decorative-axes-criss-cross'; import EmptyChart from './axes/empty-chart'; import StaticHints from './axes/static-hints'; import DynamicHints from './axes/dynamic-hints'; import DynamicComplexEdgeHints from './axes/dynamic-complex-edge-hints'; import DynamicSimpleEdgeHints from './axes/dynamic-simple-edge-hints'; import DynamicSimpleTopEdgeHints from './axes/dynamic-simple-topedge-hints'; import DynamicProgrammaticRightEdgeHints from './axes/dynamic-programmatic-rightedge-hints'; import DynamicCrosshair from './axes/dynamic-crosshair'; import DynamicCrosshairScatterplot from './axes/dynamic-crosshair-scatterplot'; import PaddedAxis from './axes/padded-axis'; import ParallelCoordinatesExample from './axes/parallel-coordinates-example'; import StaticCrosshair from './axes/static-crosshair'; import VerticalDiscreteColorLegendExample from './legends/vertical-discrete-color'; import HorizontalDiscreteColorLegendExample from './legends/horizontal-discrete-color'; import SearchableDiscreteColorLegendExample from './legends/searchable-discrete-color'; import SearchableDiscreteColorLegendHoverExample from './legends/searchable-discrete-color-hover'; import ContinuousColorLegendExample from './legends/continuous-color'; import ContinuousSizeLegendExample from './legends/continuous-size'; import HorizontalDiscreteCustomPalette from './legends/horizontal-discrete-custom-palette'; import AnimationExample from './misc/animation-example'; import LabelSeriesExample from './misc/label-series-example'; import GradientExample from './misc/gradient-example'; import ClipExample from './misc/clip-example'; import NullDataExample from './misc/null-data-example'; import SyncedCharts from './misc/synced-charts'; import TimeChart from './misc/time-chart'; import TriangleExample from './misc/triangle-example'; import VoronoiLineChart from './misc/voronoi-line-chart'; import ZoomableChartExample from './misc/zoomable-chart-example'; import SelectionPlotExample from './misc/selection-plot-example'; import DragableChartExample from './misc/dragable-chart-example'; import BidirectionDragChart from './misc/2d-dragable-plot'; import SimpleRadialChart from './radial-chart/simple-radial-chart'; import DonutChartExample from './radial-chart/donut-chart'; import CustomRadiusRadialChart from './radial-chart/custom-radius-radial-chart'; import GradientPie from './radial-chart/gradient-pie'; import ArcSeriesExample from './radial-chart/arc-series-example'; import BasicRadarChart from './radar-chart/basic-radar-chart'; import AnimatedRadarChart from './radar-chart/animated-radar-chart'; import FourQuadrantRadarChart from './radar-chart/four-quadrant-radar-chart'; import RadarChartWithTooltips from './radar-chart/radar-chart-with-tooltips'; import RadarChartSeriesTooltips from './radar-chart/radar-chart-series-tooltips'; import BasicParallelCoordinates from './parallel-coordinates/basic-parallel-coordinates'; import AnimatedParallelCoordinates from './parallel-coordinates/animated-parallel-coordinates'; import BrushedParallelCoordinates from './parallel-coordinates/brushed-parallel-coordinates'; import BasicSunburst from './sunbursts/basic-sunburst'; import ClockExample from './sunbursts/clock-example'; import AnimatedSunburst from './sunbursts/animated-sunburst'; import SunburstWithTooltips from './sunbursts/sunburst-with-tooltips'; import BasicSankeyExample from './sankey/basic'; import VoronoiSankeyExample from './sankey/voronoi'; import EnergySankeyExample from './sankey/energy-sankey'; import LinkEventSankeyExample from './sankey/link-event'; import LinkHintSankeyExample from './sankey/link-hint'; import SimpleTreemap from './treemap/simple-treemap'; import TreemapExample from './treemap/dynamic-treemap'; const mainShowCase = { AxisOn0, ComplexChart, LineChart, LineChartManyColors, LineChartCanvas, LineChartWithStyle, LineMarkChart, LineSeriesCanvasNearestXYExample, BarChart, BigBaseBarChart, DifferenceChart, StackedVerticalBarChart, LabeledStackedVerticalBarChart, MixedStackedChart, StackedHorizontalBarChart, ClusteredStackedVerticalBarChart, StackedHistogram, Histogram, AnimationExample, AreaChart, AreaChartElevated, FauxScatterplotChart, CustomSVGExample, CustomSVGRootLevel, CustomSVGAllTheMarks, ScatterplotChart, ScatterplotCanvas, WhiskerChart, HeatmapChart, HexHeatmap, HexbinSizeExample, LabeledHeatmap, ContourSeriesExample, WidthHeightMarginChart, CustomScales, CustomAxesOrientation, CustomAxisChart, CustomAxisTickFormat, CustomAxisTickElement, AxisWithTurnedLabels, GridLinesChart, StaticHints, DecorativeAxisCrissCross, DynamicHints, DynamicComplexEdgeHints, DynamicSimpleEdgeHints, DynamicSimpleTopEdgeHints, DynamicProgrammaticRightEdgeHints, EmptyChart, StaticCrosshair, DynamicCrosshair, DynamicCrosshairScatterplot, PaddedAxis, ParallelCoordinatesExample, SyncedCharts, TimeChart, TriangleExample, VoronoiLineChart, CustomAxes, LabelSeriesExample, GradientExample, ClipExample, NullDataExample, ZoomableChartExample, SelectionPlotExample, DragableChartExample, BidirectionDragChart, SensibleDefaults, ColorInXYPlot, ColorSpecificity, CategoryColorAtMarkLevel, CategoryColorAtMarkLevelCustomPalette, CategoryColorAtMarkLevelFixedStroke, GradientCharts, LinearColorAtMarkLevel, LinearColorAtMarkLevelNoPalette, LineSeriesMarkSeries, LiteralColorAtMarkLevel, CategoryColorAtSeriesLevel, LinearColorAtSeriesLevel, LiteralColorAtSeriesLevel, ReactVis5, ReactVis20, Continuous, CustomPalette, MiniCharts, FlexibleCharts, LineChartMouseOverSeries, LineChartMouseOverXY, LinkedCharts, ScatterPlotOnNearestXY, SimpleTreemap, TreemapExample, AnimatedSunburst, BasicSunburst, ClockExample, SunburstWithTooltips, SimpleRadialChart, DonutChartExample, CustomRadiusRadialChart, GradientPie, ArcSeriesExample, AnimatedRadarChart, BasicRadarChart, FourQuadrantRadarChart, RadarChartWithTooltips, RadarChartSeriesTooltips, BasicParallelCoordinates, AnimatedParallelCoordinates, BrushedParallelCoordinates, BasicSankeyExample, VoronoiSankeyExample, EnergySankeyExample, LinkEventSankeyExample, LinkHintSankeyExample, VerticalDiscreteColorLegendExample, HorizontalDiscreteColorLegendExample, HorizontalDiscreteCustomPalette, SearchableDiscreteColorLegendExample, SearchableDiscreteColorLegendHoverExample, ContinuousColorLegendExample, ContinuousSizeLegendExample, EnergySankey, BasicSankey, VornoiSankey }; const showCaseWithLinks = Object.keys(mainShowCase).reduce( (acc, showCaseExample) => { const link = SHOWCASE_LINKS[showCaseExample]; acc[`${showCaseExample}WithLink`] = sourceLinker( mainShowCase[showCaseExample], link ); return acc; }, {} ); export const showCase = {...mainShowCase, ...showCaseWithLinks}; ================================================ FILE: packages/showcase/interaction/interaction-examples.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {XYPlot, LineSeries, MarkSeries} from 'react-vis'; const scatterPlotData = [...Array(30).keys()].map(() => ({ x: Math.random() * 10, y: Math.random() * 10 })); const lineData = [...Array(3).keys()].map(() => [...Array(10).keys()].map(x => ({x, y: Math.random() * 10})) ); const allData = lineData.reduce((prev, curr, i) => { return [...prev, ...curr.map(d => ({...d, seriesNb: i}))]; }, []); const defaultProps = { width: 400, height: 200, margin: {top: 5, left: 5, right: 5, bottom: 5} }; export class ScatterPlotOnNearestXY extends Component { constructor() { super(); this.state = {index: null}; } render() { const {index} = this.state; const data = scatterPlotData.map((d, i) => ({ ...d, color: i === index ? 1 : 0 })); return ( this.setState({index: null})} > this.setState({index: e.index})} /> ); } } export class LineChartMouseOverSeries extends Component { constructor() { super(); this.state = {index: null}; } render() { const {index} = this.state; return ( this.setState({index: null})} > {lineData.map((d, i) => ( ))} {lineData.map((d, i) => ( this.setState({index: null})} onSeriesMouseOver={() => this.setState({index: i})} strokeWidth={10} stroke={index === i ? 'rgba(0,0,0,0.2)' : 'transparent'} /> ))} ); } } export class LineChartMouseOverXY extends Component { constructor() { super(); this.state = {highlightedSeries: null, pointUsed: null}; } render() { const {pointUsed, highlightedSeries} = this.state; const data = allData.map((d, i) => ({ ...d, color: i === pointUsed ? 'rgba(0,0,0,0.2)' : 'transparent' })); return ( this.setState({ highlightedSeries: null, pointUsed: null }) } > {lineData.map((d, i) => ( ))} this.setState({ highlightedSeries: seriesNb, pointUsed: index }) } /> ); } } export class LinkedCharts extends Component { constructor() { super(); this.state = {index: null}; } handleMouseOver = index => { this.setState({index}); }; render() { const {index} = this.state; return (
{lineData.map((d, i) => (
))}
); } } function LineChart({data, index, handleMouseOver}) { return ( handleMouseOver(null)} > handleMouseOver(e.index)} /> {index === null ? null : ( )} {index === null ? null : ( )} ); } ================================================ FILE: packages/showcase/legends/continuous-color.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ContinuousColorLegend from 'react-vis/legends/continuous-color-legend'; export default class Example extends React.Component { constructor(props) { super(props); } render() { return ( ); } } ================================================ FILE: packages/showcase/legends/continuous-size.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ContinuousSizeLegend from 'react-vis/legends/continuous-size-legend'; export default class Example extends React.Component { constructor(props) { super(props); } render() { return ; } } ================================================ FILE: packages/showcase/legends/horizontal-discrete-color.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import DiscreteColorLegend from 'react-vis/legends/discrete-color-legend'; import GradientDefs from 'react-vis/plot/gradient-defs'; const ITEMS = [ {title: 'Dashed', color: '#45aeb1', strokeStyle: 'dashed'}, {title: 'Dasharray', color: '#f93', strokeDasharray: '1 2 3 4 5 6 7'}, {title: 'Dots', color: 'url(#circles)', strokeWidth: 9}, {title: 'Stripes', color: 'url(#stripes)'}, {title: 'Wide stripes', color: 'url(#stripes)', strokeWidth: 13}, {title: 'Normal', color: 'purple'}, {title: 'Wide', color: 'powderblue', strokeWidth: 6} ]; export default function DiscreteColorExample() { return (
); } ================================================ FILE: packages/showcase/legends/horizontal-discrete-custom-palette.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import DiscreteColorLegend from 'react-vis/legends/discrete-color-legend'; const ITEMS = [ 'Options', 'Buttons', 'Select boxes', 'Date inputs', 'Password inputs', 'Forms', 'Other' ]; const COLORS = [ '#6588cd', '#66b046', '#a361c7', '#ad953f', '#c75a87', '#55a47b', '#cb6141' ]; export default class HorizontalDiscreteColorPalette extends Component { state = { hoveredItem: false }; render() { const {hoveredItem} = this.state; return ( this.setState({hoveredItem: i})} onItemMouseLeave={() => this.setState({hoveredItem: false})} orientation="horizontal" width={300} items={ITEMS.map((item, key) => hoveredItem === item ? (
{item}
{'SELECTED'}
) : ( item ) )} /> ); } } ================================================ FILE: packages/showcase/legends/searchable-discrete-color-hover.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import SearchableDiscreteColorLegend from 'react-vis/legends/searchable-discrete-color-legend'; export default class SearchableDiscreteColorLegendHoverExample extends Component { constructor(props) { super(props); this.state = { hoveredItem: false, items: [ {title: 'Apples', color: '#3a3'}, {title: 'Bananas', color: '#fc0'}, {title: 'Blueberries', color: '#337'}, {title: 'Carrots', color: '#f93'}, {title: 'Eggplants', color: '#337'}, {title: 'Limes', color: '#cf3'}, {title: 'Potatoes', color: '#766'} ], searchText: '' }; } _clickHandler = (item, i) => { const {items} = this.state; items[i].disabled = !items[i].disabled; this.setState({items}); }; _searchChangeHandler = searchText => { this.setState({searchText}); }; render() { const {items, hoveredItem, searchText} = this.state; return ( this.setState({ hoveredItem: { ...i, title: `${i.title}:SELECTED` } }) } onItemMouseLeave={() => this.setState({hoveredItem: false})} onSearchChange={this._searchChangeHandler} searchText={searchText} onItemClick={this._clickHandler} items={items.map(item => hoveredItem && hoveredItem.title.includes(item.title) ? hoveredItem : item )} /> ); } } ================================================ FILE: packages/showcase/legends/searchable-discrete-color.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import SearchableDiscreteColorLegend from 'react-vis/legends/searchable-discrete-color-legend'; export default class Example extends React.Component { constructor(props) { super(props); this.state = { items: [ {title: 'Apples', color: '#3a3'}, {title: 'Bananas', color: '#fc0'}, {title: 'Blueberries', color: '#337'}, {title: 'Carrots', color: '#f93'}, {title: 'Eggplants', color: '#337'}, {title: 'Limes', color: '#cf3'}, {title: 'Potatoes', color: '#766'} ], searchText: '' }; } _clickHandler = item => { const {items} = this.state; item.disabled = !item.disabled; this.setState({items}); }; _searchChangeHandler = searchText => { this.setState({searchText}); }; render() { const {items, searchText} = this.state; return ( ); } } ================================================ FILE: packages/showcase/legends/vertical-discrete-color.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import DiscreteColorLegend from 'react-vis/legends/discrete-color-legend'; const ITEMS = [ 'Options', 'Buttons', 'Select boxes', 'Date inputs', 'Password inputs', 'Forms', 'Other' ]; export default function DiscreteColorExample() { return ; } ================================================ FILE: packages/showcase/misc/2d-dragable-plot.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries, Highlight, Hint } from 'react-vis'; import {generateSeededRandom} from '../showcase-utils'; const seededRandom = generateSeededRandom(3); // randomly generated data const data = [...new Array(30)].map(() => ({ x: seededRandom() * 5, y: seededRandom() * 10 })); export default class BidirectionDragChart extends React.Component { state = { filter: null, hovered: null, highlighting: false }; render() { const {filter, hovered, highlighting} = this.state; const highlightPoint = d => { if (!filter) { return false; } const leftRight = d.x <= filter.right && d.x >= filter.left; const upDown = d.y <= filter.top && d.y >= filter.bottom; return leftRight && upDown; }; const numSelectedPoints = filter ? data.filter(highlightPoint).length : 0; return (
this.setState({highlighting: true})} onBrush={area => this.setState({filter: area})} onBrushEnd={area => this.setState({highlighting: false, filter: area}) } onDragStart={() => this.setState({highlighting: true})} onDrag={area => this.setState({filter: area})} onDragEnd={area => this.setState({highlighting: false, filter: area}) } /> (highlightPoint(d) ? '#EF5D28' : '#12939A')} onValueMouseOver={d => this.setState({hovered: d})} onValueMouseOut={() => this.setState({hovered: false})} data={data} /> {hovered && }

{`There are ${numSelectedPoints} selected points`}

); } } ================================================ FILE: packages/showcase/misc/animation-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries } from 'react-vis'; function generateData() { return [...new Array(10)].map(() => ({ x: Math.random() * 5, y: Math.random() * 10 })); } const MODE = ['noWobble', 'gentle', 'wobbly', 'stiff']; export default class Example extends React.Component { state = { data: generateData(), modeIndex: 0 }; updateModeIndex = increment => () => { const newIndex = this.state.modeIndex + (increment ? 1 : -1); const modeIndex = newIndex < 0 ? MODE.length - 1 : newIndex >= MODE.length ? 0 : newIndex; this.setState({ modeIndex }); }; render() { const {modeIndex, data} = this.state; return (
{`ANIMATION TECHNIQUE: ${MODE[modeIndex]}`}
this.setState({data: generateData()})} buttonContent={'UPDATE DATA'} />
); } } ================================================ FILE: packages/showcase/misc/clip-example.js ================================================ import React, {useState} from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, AreaSeries, ContentClipPath } from 'react-vis'; export default function ClipExample() { const [clip, setClip] = useState(true); return ( <> {clip && } ); } ================================================ FILE: packages/showcase/misc/dragable-chart-example.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, XAxis, YAxis, VerticalRectSeries, Highlight} from 'react-vis'; const DATA = [ {x0: 0, x: 1, y: 1}, {x0: 1, x: 2, y: 2}, {x0: 2, x: 3, y: 10}, {x0: 3, x: 4, y: 6}, {x0: 4, x: 5, y: 5}, {x0: 5, x: 6, y: 3}, {x0: 6, x: 7, y: 1} ]; class DragableChartExample extends React.Component { state = { selectionStart: null, selectionEnd: null }; render() { const {selectionStart, selectionEnd} = this.state; const updateDragState = area => this.setState({ selectionStart: area && area.left, selectionEnd: area && area.right }); return (
{ if (selectionStart === null || selectionEnd === null) { return '#1E96BE'; } const inX = d.x >= selectionStart && d.x <= selectionEnd; const inX0 = d.x0 >= selectionStart && d.x0 <= selectionEnd; const inStart = selectionStart >= d.x0 && selectionStart <= d.x; const inEnd = selectionEnd >= d.x0 && selectionEnd <= d.x; return inStart || inEnd || inX || inX0 ? '#12939A' : '#1E96BE'; }} />
selectionStart: {`${Math.floor(selectionStart * 100) / 100},`} selectionEnd: {`${Math.floor(selectionEnd * 100) / 100},`}
); } } export default DragableChartExample; ================================================ FILE: packages/showcase/misc/gradient-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, AreaSeries, GradientDefs, Borders } from 'react-vis'; export default function GradientExample() { return ( ); } ================================================ FILE: packages/showcase/misc/label-series-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import {XYPlot, XAxis, YAxis, MarkSeries, LabelSeries} from 'react-vis'; function generateData() { return [ { x: Math.random() * 3, y: Math.random() * 20, label: 'Wigglytuff', size: 30, style: {fontSize: 20} }, {x: Math.random() * 3, y: Math.random() * 20, label: 'Psyduck', size: 10}, {x: Math.random() * 3, y: Math.random() * 20, label: 'Geodude', size: 1}, { x: Math.random() * 3, y: Math.random() * 20, label: 'red', size: 12, rotation: 45 }, {x: Math.random() * 3, y: Math.random() * 20, label: 'blue', size: 4} ]; } export default class Example extends React.Component { state = { data: [ { x: 3, y: 7, label: 'Wigglytuff', size: 30, style: {fontSize: 20}, rotation: 45 }, {x: 2, y: 4, label: 'Psyduck', size: 10}, {x: 1, y: 20, label: 'Geodude', size: 1}, {x: 4, y: 12, label: 'Ditto', size: 12, rotation: 45}, {x: 1, y: 14, label: 'Snorlax', size: 4} ] }; render() { const {data} = this.state; return (
this.setState({data: generateData()})} buttonContent="UPDATE" />
); } } ================================================ FILE: packages/showcase/misc/null-data-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { AreaSeries, Crosshair, XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineMarkSeries } from 'react-vis'; const DATA = [ [ {x: 1, y: 10}, {x: 2, y: 10}, {x: 3, y: 13}, {x: 4, y: 7}, {x: 5, y: null} ], [ {x: 1, y: 30}, {x: 2, y: 0}, {x: 5, y: null}, {x: 4, y: 15}, {x: 5, y: null} ] ]; export default class NullDataExample extends React.Component { state = { crosshairValues: [] }; onMouseLeave = () => this.setState({crosshairValues: []}); onNearestX = (value, {index}) => this.setState({ crosshairValues: DATA.map(d => d[index].y !== null && d[index]) }); render() { return ( d.y !== null} onNearestX={this.onNearestX} data={DATA[0]} /> d.y !== null} data={DATA[1]} /> ); } } ================================================ FILE: packages/showcase/misc/selection-plot-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries, Highlight } from 'react-vis'; import {generateSeededRandom} from '../showcase-utils'; const seededRandom = generateSeededRandom(36); // randomly generated data const data = [...new Array(10)].map(() => ({ x: seededRandom() * 3, y: seededRandom() * 11 + 4, size: seededRandom() * 30 + 1 })); export default class SelectionPlotExample extends React.Component { state = { filter: null }; render() { const {filter} = this.state; const highlightPoint = d => { if (!filter) { return false; } return d.y <= filter.top && d.y >= filter.bottom; }; const numSelectedPoints = filter ? data.filter(highlightPoint).length : 0; return (
(highlightPoint(d) ? '#EF5D28' : '#12939A')} data={data} /> this.setState({filter: area})} onBrushEnd={area => this.setState({filter: area})} />

{`There are ${numSelectedPoints} selected points`}

); } } ================================================ FILE: packages/showcase/misc/synced-charts.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineSeries } from 'react-vis'; export default class Example extends React.Component { constructor(props) { super(props); this.state = { selectedIndex: null }; this._onSeriesMouseOvers = [ this._onSeriesMouseOver.bind(this, 0), this._onSeriesMouseOver.bind(this, 1) ]; } _getSeriesColor(index) { const {selectedIndex} = this.state; if (selectedIndex !== null && selectedIndex !== index) { return '#ddd'; } return null; } _onChartMouseLeave = () => { this.setState({selectedIndex: null}); }; _onSeriesMouseOver(selectedIndex) { this.setState({selectedIndex}); } render() { return (
); } } ================================================ FILE: packages/showcase/misc/time-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; const MSEC_DAILY = 86400000; export default function Example() { const timestamp = new Date('September 9 2017').getTime(); return ( ); } ================================================ FILE: packages/showcase/misc/triangle-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, PolygonSeries, XAxis, YAxis, GradientDefs} from 'react-vis'; function buildTriangle(sideWidth, lowerLeftCoord) { const {x, y} = lowerLeftCoord; const triangle = [ [ {x, y}, {x, y: y + sideWidth}, {x: x + sideWidth, y} ] ]; if (sideWidth < 0.5) { return triangle; } const newWidth = sideWidth * 0.5; const a = buildTriangle(newWidth, lowerLeftCoord); const b = buildTriangle(newWidth, {x: x + sideWidth, y}); const c = buildTriangle(newWidth, {x, y: y + sideWidth}); return triangle .concat(a) .concat(b) .concat(c); } const triangles = buildTriangle(7, {x: 0, y: 0}); export default class Example extends React.Component { state = { hoveredIndex: false }; render() { const {hoveredIndex} = this.state; return ( {triangles.map((triangle, index) => { return ( this.setState({hoveredIndex: index})} onSeriesMouseOut={() => this.setState({hoveredIndex: false})} color={index !== hoveredIndex ? 'url(#grad1)' : null} style={{ strokeWidth: 0.5, strokeOpacity: 1, opacity: 0.5, fill: index === hoveredIndex ? '#EF5D28' : null }} /> ); })} ); } } ================================================ FILE: packages/showcase/misc/voronoi-line-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries, MarkSeries, Voronoi } from 'react-vis'; const lines = [ [ {x: 1, y: 3}, {x: 2, y: 5}, {x: 3, y: 15}, {x: 4, y: 12} ], [ {x: 1, y: 10}, {x: 2, y: 4}, {x: 3, y: 2}, {x: 4, y: 15} ], [ {x: 1, y: 7}, {x: 2, y: 11}, {x: 3, y: 9}, {x: 4, y: 2} ] ].map((p, i) => p.map(d => ({...d, line: i}))); const nodes = lines.reduce((acc, d) => [...acc, ...d], []); const getDomain = (data, key) => { const {min, max} = data.reduce( (acc, row) => ({ min: Math.min(acc.min, row[key]), max: Math.max(acc.max, row[key]) }), {min: Infinity, max: -Infinity} ); return [min, max]; }; const xDomain = getDomain(nodes, 'x'); const yDomain = getDomain(nodes, 'y'); export default class Example extends React.Component { state = { hoveredNode: null, showVoronoi: false }; render() { const {hoveredNode, showVoronoi} = this.state; return (
{lines.map((d, i) => ( ))} {hoveredNode && } [...acc, ...d], [])} onHover={node => this.setState({hoveredNode: node})} onBlur={() => this.setState({hoveredNode: null})} polygonStyle={{stroke: showVoronoi ? 'rgba(0, 0, 0, .2)' : null}} />
); } } ================================================ FILE: packages/showcase/misc/zoomable-chart-example.js ================================================ // Copyright (c) 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XAxis, YAxis, HorizontalGridLines, XYPlot, LineSeries, Highlight } from 'react-vis'; import {generateSeededRandom} from '../showcase-utils'; const seededRandom = generateSeededRandom(9); const totalValues = 100; /** * Get the array of x and y pairs. * The function tries to avoid too large changes of the chart. * @param {number} total Total number of values. * @returns {Array} Array of data. * @private */ function getRandomSeriesData(total) { const result = []; let lastY = seededRandom() * 40 - 20; let y; const firstY = lastY; for (let i = 0; i < total; i++) { y = seededRandom() * firstY - firstY / 2 + lastY; result.push({ x: i, y }); lastY = y; } return result; } export default class ZoomableChartExample extends React.Component { state = { lastDrawLocation: null, series: [ { data: getRandomSeriesData(totalValues), disabled: false, title: 'Apples' }, { data: getRandomSeriesData(totalValues), disabled: false, title: 'Bananas' } ] }; render() { const {series, lastDrawLocation} = this.state; return (
{series.map(entry => ( ))} this.setState({lastDrawLocation: area})} onDrag={area => { this.setState({ lastDrawLocation: { bottom: lastDrawLocation.bottom + (area.top - area.bottom), left: lastDrawLocation.left - (area.right - area.left), right: lastDrawLocation.right - (area.right - area.left), top: lastDrawLocation.top + (area.top - area.bottom) } }); }} />

Last Draw Area

{lastDrawLocation ? (
  • Top: {lastDrawLocation.top}
  • Right: {lastDrawLocation.right}
  • Bottom: {lastDrawLocation.bottom}
  • Left: {lastDrawLocation.left}
) : ( N/A )}
); } } ================================================ FILE: packages/showcase/package.json ================================================ { "name": "react-vis-showcase", "description": "Showcase of react-vis components", "version": "0.1.0", "scripts": { "start": "webpack-dev-server --progress --hot --port 3001", "lint": "eslint .", "build": "NODE_ENV=production webpack", "build:windows": "set NODE_ENV=production&&.\\node_modules\\.bin\\webpack" }, "devDependencies": { "babel-loader": "^8.0.0", "css-loader": "^0.26.1", "mini-css-extract-plugin": "^0.9.0", "sass-loader": "^8.0.2", "style-loader": "^0.13.1", "webpack": "^4.43.0", "webpack-cli": "^3.3.11", "webpack-dev-server": "^3.11.0" }, "license": "MIT", "private": true, "dependencies": { "d3-force": "^1.0.6", "d3-random": "^1.1.0", "react": "^17.0.2", "react-dom": "^17.0.2", "react-router": "^5.2.0", "react-router-dom": "^5.2.0", "react-vis": "^1.11.7" }, "volta": { "node": "14.18.0", "yarn": "1.22.4" } } ================================================ FILE: packages/showcase/parallel-coordinates/animated-parallel-coordinates.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import {ParallelCoordinates} from 'react-vis'; const DATA = [ { explosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 } ]; const DOMAIN = [ {name: 'nice', domain: [0, 100], tickFormat: t => t}, {name: 'explosions', domain: [6.9, 7.1]}, {name: 'wow', domain: [0, 11]}, {name: 'dog', domain: [0, 16]}, {name: 'sickMoves', domain: [0, 20]} ]; function generateData() { return [ Object.keys(DATA[0]).reduce((acc, key) => { acc[key] = DATA[0][key] + 5 * (Math.random() - 0.5); return acc; }, {}) ]; } export default class AnimatedParallelCoordinates extends Component { state = { data: DATA }; render() { const {data} = this.state; return (
''} width={400} height={300} /> this.setState({data: generateData()})} buttonContent={'UPDATE DATA'} />
); } } ================================================ FILE: packages/showcase/parallel-coordinates/basic-parallel-coordinates.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {format} from 'd3-format'; import {ParallelCoordinates} from 'react-vis'; const DATA = [ { name: 'Mercedes', mileage: 7, price: 10, safety: 8, performance: 9, interior: 7, warranty: 7 }, { name: 'Honda', mileage: 8, price: 6, safety: 9, performance: 6, interior: 3, warranty: 9, style: { strokeWidth: 3, strokeDasharray: '2, 2' } }, { name: 'Chevrolet', mileage: 5, price: 4, safety: 6, performance: 4, interior: 5, warranty: 6 } ]; const basicFormat = format('.2r'); const wideFormat = format('.3r'); export default function BasicParallelCoordinates() { return ( wideFormat(t)} margin={50} colorRange={['#172d47', '#911116', '#998965']} domains={[ {name: 'mileage', domain: [0, 10]}, { name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`, getValue: d => d.price }, {name: 'safety', domain: [5, 10], getValue: d => d.safety}, {name: 'performance', domain: [0, 10], getValue: d => d.performance}, {name: 'interior', domain: [0, 7], getValue: d => d.interior}, {name: 'warranty', domain: [10, 2], getValue: d => d.warranty} ]} showMarks width={400} height={300} /> ); } ================================================ FILE: packages/showcase/parallel-coordinates/brushed-parallel-coordinates.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {ParallelCoordinates} from 'react-vis'; import IrisData from '../datasets/iris.json'; // {"sepal length": 5.1, "sepal width": 3.5, "petal length": 1.4, "petal width": 0.2, "species": "setosa"}, const SPECIES_COLORS = { setosa: '#12939A', virginica: '#79C7E3', versicolor: '#1A3177' }; const domainStructure = Object.keys(IrisData[0]) .filter(name => name !== 'species') .map(name => ({name, domain: [Infinity, -Infinity]})); const domains = IrisData.reduce((acc, row) => { return acc.map(d => { return { name: d.name, domain: [ Math.min(d.domain[0], row[d.name]), Math.max(d.domain[1], row[d.name]) ] }; }); }, domainStructure); export default function BrushedParallelCoordinates() { return ( ({...d, color: SPECIES_COLORS[d.species]}))} domains={domains} margin={60} width={600} height={400} /> ); } ================================================ FILE: packages/showcase/plot/area-chart-elevated.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineMarkSeries, AreaSeries } from 'react-vis'; export default function AreaChartElevated() { return ( ); } ================================================ FILE: packages/showcase/plot/area-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, AreaSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/axis-with-turned-labels.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries, VerticalBarSeriesCanvas, LabelSeries } from 'react-vis'; const greenData = [ {x: 'A', y: 10}, {x: 'B', y: 5}, {x: 'C', y: 15} ]; const blueData = [ {x: 'A', y: 12}, {x: 'B', y: 2}, {x: 'C', y: 11} ]; const labelData = greenData.map((d, idx) => ({ x: d.x, y: Math.max(greenData[idx].y, blueData[idx].y) })); export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} /> d.x} />
); } } ================================================ FILE: packages/showcase/plot/big-base-bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalBarSeries, VerticalBarSeriesCanvas } from 'react-vis'; const myDATA = [ {id: '00036', y: 200400, x: 1504121437}, {id: '00036', y: 200350, x: 1504121156}, {id: '00036', y: 200310, x: 1504120874}, {id: '00036', y: 200260, x: 1504120590}, {id: '00036', y: 200210, x: 1504120306}, {id: '00036', y: 200160, x: 1504120024}, {id: '00036', y: 200120, x: 1504119740}, {id: '00036', y: 200070, x: 1504119458}, {id: '00036', y: 200020, x: 1504119177}, {id: '00036', y: 199980, x: 1504118893}, {id: '00036', y: 199930, x: 1504118611}, {id: '00036', y: 199880, x: 1504118330}, {id: '00036', y: 199830, x: 1504118048}, {id: '00036', y: 199790, x: 1504117763}, {id: '00036', y: 199740, x: 1504117481} ]; const yDomain = myDATA.reduce( (res, row) => { return { max: Math.max(res.max, row.y), min: Math.min(res.min, row.y) }; }, {max: -Infinity, min: Infinity} ); export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/clustered-stacked-bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries, VerticalBarSeriesCanvas, DiscreteColorLegend } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/complex-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XAxis, YAxis, FlexibleWidthXYPlot, HorizontalGridLines, LineSeries, VerticalRectSeries, DiscreteColorLegend, Crosshair } from 'react-vis'; /** * Get the array of x and y pairs. * The function tries to avoid too large changes of the chart. * @param {number} total Total number of values. * @returns {Array} Array of data. * @private */ function getRandomSeriesData(total) { const result = []; let lastY = Math.random() * 40 - 20; let y; const firstY = lastY; for (let i = 0; i < Math.max(total, 3); i++) { y = Math.random() * firstY - firstY / 2 + lastY; result.push({ left: i, top: y }); lastY = y; } return result; } export default class Example extends React.Component { constructor(props) { super(props); const totalValues = Math.random() * 50; this.state = { crosshairValues: [], series: [ { title: 'Apples', disabled: false, data: getRandomSeriesData(totalValues) }, { title: 'Bananas', disabled: false, data: getRandomSeriesData(totalValues) } ] }; } /** * A callback to format the crosshair items. * @param {Object} values Array of values. * @returns {Array} Array of objects with titles and values. * @private */ _formatCrosshairItems = values => { const {series} = this.state; return values.map((v, i) => { return { title: series[i].title, value: v.top }; }); }; /** * Format the title line of the crosshair. * @param {Array} values Array of values. * @returns {Object} The caption and the value of the title. * @private */ _formatCrosshairTitle = values => { return { title: 'X', value: values[0].left }; }; /** * Click handler for the legend. * @param {Object} item Clicked item of the legend. * @param {number} i Index of the legend. * @private */ _legendClickHandler = (item, i) => { const {series} = this.state; series[i].disabled = !series[i].disabled; this.setState({series}); }; /** * Event handler for onMouseLeave. * @private */ _mouseLeaveHandler = () => { this.setState({crosshairValues: []}); }; /** * Event handler for onNearestX. * @param {Object} value Selected value. * @param {number} index Index of the series. * @private */ _nearestXHandler = (value, {index}) => { const {series} = this.state; this.setState({ crosshairValues: series.map(s => s.data[index]) }); }; _updateButtonClicked = () => { const {series} = this.state; const totalValues = Math.random() * 50; series.forEach(s => { s.data = getRandomSeriesData(totalValues); }); this.setState({series}); }; render() { const {series, crosshairValues} = this.state; return (
d.left} getY={d => d.top} onMouseLeave={this._mouseLeaveHandler} xDomain={[-0.5, series[0].data.length - 1]} height={300} > ({ x0: left - 0.5, left: left + 0.5, top }))} stroke="white" onNearestX={this._nearestXHandler} {...(series[0].disabled ? {opacity: 0.2} : null)} />
); } } ================================================ FILE: packages/showcase/plot/contour-series-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, ContourSeries, MarkSeriesCanvas, Borders } from 'react-vis'; import DATA from '../datasets/old-faithful.json'; function updateData() { return DATA.map(row => ({ waiting: row.waiting + (Math.random() - 0.5) * 10, eruptions: row.eruptions + (Math.random() - 0.5) * 2 })); } export default class ContourSeriesExample extends Component { state = { data: DATA }; render() { const {data} = this.state; return (
d.waiting} getY={d => d.eruptions} height={300} > this.setState({data: updateData()})} buttonContent={'UPDATE'} />
); } } ================================================ FILE: packages/showcase/plot/custom-scales.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/custom-svg-all-the-marks.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, CustomSVGSeries, Hint } from 'react-vis'; import ShowcaseButton from '../showcase-components/showcase-button'; function generateData(reversed) { return ['star', 'square', 'circle', 'diamond'].reduce( (acc, row, rowIndex) => { const cellsInRow = [...new Array(5)].map((cell, index) => { return { x: index, y: reversed ? (5 - rowIndex) * 5 : rowIndex * 5, size: (index + 1) * 3, customComponent: row }; }); return acc.concat(cellsInRow); }, [] ); } const DATA = generateData(false); const REVERSED_DATA = generateData(true); const tipStyle = { display: 'flex', color: '#fff', background: '#000', alignItems: 'center', padding: '5px' }; export default class Example extends React.Component { state = { reverse: false }; render() { const {reverse, hoveredCell} = this.state; return (
this.setState({reverse: !reverse})} /> { this.setState({hoveredCell: v}); }} onValueMouseOut={() => this.setState({hoveredCell: false})} /> {hoveredCell && (
{hoveredCell.customComponent}
)}
); } } ================================================ FILE: packages/showcase/plot/custom-svg-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, CustomSVGSeries } from 'react-vis'; export default function Example() { return ( { return ( {`x: ${positionInPixels.x}`} {`y: ${positionInPixels.y}`} ); } } ]} /> ); } ================================================ FILE: packages/showcase/plot/custom-svg-root-level.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, CustomSVGSeries } from 'react-vis'; export default function CustomSVGRootLevelComponent() { return ( { return ( {`x: ${positionInPixels.x}`} {`y: ${positionInPixels.y}`} ); }} data={[ {x: 1, y: 10, size: 3}, {x: 1.7, y: 12, size: 20, style: {stroke: 'red', fill: 'orange'}}, {x: 2, y: 5}, {x: 3, y: 15}, {x: 2.5, y: 7} ]} /> ); } ================================================ FILE: packages/showcase/plot/difference-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalBarSeries, VerticalBarSeriesCanvas } from 'react-vis'; // When making a difference chart you are specifying in coordinates // where you want your bars to start and stop const myDATA = [...new Array(15)].map((x, idx) => ({ x: idx, // if the bars are above zero then we start them at zero y0: idx - 4 < 0 ? idx - 4 : 0, // if the bars are below zero then we stop them at zero y: idx < 5 ? 0 : Math.abs(idx - 4) })); const yDomain = myDATA.reduce( (res, row) => ({ max: Math.max(res.max, row.y, row.y0), min: Math.min(res.min, row.y, row.y0) }), {max: -Infinity, min: Infinity} ); export default class DifferenceChart extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} /> { return d.y0 < 0 ? '#EF5D28' : '#1A3177'; }} />
); } } ================================================ FILE: packages/showcase/plot/faux-radial-scatterplot.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, XAxis, YAxis, MarkSeries, CircularGridLines} from 'react-vis'; const data = [ {r: 1, theta: Math.PI / 3, size: 30}, {r: 1.7, theta: (2 * Math.PI) / 3, size: 10}, {r: 2, theta: Math.PI, size: 1}, {r: 3, theta: (3 * Math.PI) / 2, size: 12}, {r: 2.5, theta: Math.PI / 4, size: 4}, {r: 0, theta: Math.PI / 4, size: 1} ]; const margin = { top: 10, bottom: 10, left: 10, right: 10 }; const WIDTH = 300; const HEIGHT = 300; export default function Example() { return ( ({ ...row, x: Math.cos(row.theta) * row.r, y: Math.sin(row.theta) * row.r }))} /> ); } ================================================ FILE: packages/showcase/plot/grid.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, XAxis, YAxis, VerticalGridLines, LineSeries} from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/heatmap-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {XYPlot, XAxis, YAxis, HeatmapSeries, Hint} from 'react-vis'; export default class HeatmapChart extends Component { state = { value: false }; render() { const {value} = this.state; return ( this.setState({value: v})} onSeriesMouseOut={() => this.setState({value: false})} data={[ {x: 1, y: 0, color: 10}, {x: 1, y: 5, color: 10}, {x: 1, y: 10, color: 6}, {x: 1, y: 15, color: 7}, {x: 2, y: 0, color: 12}, {x: 2, y: 5, color: 2}, {x: 2, y: 10, color: 1}, {x: 2, y: 15, color: 12}, {x: 3, y: 0, color: 9}, {x: 3, y: 5, color: 2}, {x: 3, y: 10, color: 6}, {x: 3, y: 15, color: 12} ]} /> {value !== false && } ); } } ================================================ FILE: packages/showcase/plot/hex-heatmap.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import {XYPlot, XAxis, YAxis, HexbinSeries, Borders, Hint} from 'react-vis'; import DATA from '../datasets/old-faithful.json'; function updateData() { return DATA.map(row => ({ waiting: row.waiting + (Math.random() - 0.5) * 10, eruptions: row.eruptions + (Math.random() - 0.5) * 2 })); } export default class HexHeatmap extends Component { state = { data: DATA, hoveredNode: null, radius: 10, offset: 0 }; render() { const {data, radius, hoveredNode, offset} = this.state; return (
d.waiting} getY={d => d.eruptions} onMouseLeave={() => this.setState({hoveredNode: null})} height={300} > this.setState({hoveredNode: d})} xOffset={offset} yOffset={offset} colorRange={['orange', 'cyan']} radius={radius} data={data} /> {hoveredNode && ( d.x} getY={d => d.y} value={{ x: hoveredNode.x, y: hoveredNode.y, value: hoveredNode.length }} /> )} this.setState({data: updateData()})} buttonContent={'UPDATE DATA'} /> this.setState({radius: (Math.random() - 0.5) * 10 + 10}) } buttonContent={'UPDATE RADIUS'} /> this.setState({offset: (Math.random() - 0.5) * 10 + 10}) } buttonContent={'UPDATE OFFSET'} />
); } } ================================================ FILE: packages/showcase/plot/hexbin-size-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import {XYPlot, XAxis, YAxis, HexbinSeries, ChartLabel} from 'react-vis'; import DATA from '../datasets/car-data.json'; const DIMENSIONS = [ 'economy (mpg)', 'cylinders', 'displacement (cc)', 'power (hp)', 'weight (lb)', '0-60 mph (s)', 'year' ]; export default class HexbinSizeExample extends Component { state = { xAxis: 0, yAxis: 3 }; updateX(increment) { this.setState({ xAxis: (this.state.xAxis + (increment ? 1 : -1)) % DIMENSIONS.length }); } updateY(increment) { this.setState({ yAxis: (this.state.yAxis + (increment ? 1 : -1)) % DIMENSIONS.length }); } render() { const {xAxis, yAxis} = this.state; const data = DATA.map(d => ({ x: Number(d[DIMENSIONS[xAxis]]), y: Number(d[DIMENSIONS[yAxis]]) })); return (
this.updateX(false)} buttonContent={'PREV X'} />
{`X AXIS ${DIMENSIONS[xAxis]}`}
this.updateX(true)} buttonContent={'NEXT X'} />
this.updateY(false)} buttonContent={'PREV Y'} />
{`Y AXIS ${DIMENSIONS[yAxis]}`}
this.updateY(true)} buttonContent={'NEXT Y'} />
this.setState({hoveredNode: null})} height={300} margin={50} >
); } } ================================================ FILE: packages/showcase/plot/histogram.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalRectSeries } from 'react-vis'; const timestamp = new Date('May 23 2017').getTime(); const ONE_DAY = 86400000; const DATA = [ {x0: ONE_DAY * 2, x: ONE_DAY * 3, y: 1}, {x0: ONE_DAY * 7, x: ONE_DAY * 8, y: 1}, {x0: ONE_DAY * 8, x: ONE_DAY * 9, y: 1}, {x0: ONE_DAY * 9, x: ONE_DAY * 10, y: 2}, {x0: ONE_DAY * 10, x: ONE_DAY * 11, y: 2.2}, {x0: ONE_DAY * 19, x: ONE_DAY * 20, y: 1}, {x0: ONE_DAY * 20, x: ONE_DAY * 21, y: 2.5}, {x0: ONE_DAY * 21, x: ONE_DAY * 24, y: 1} ].map(el => ({x0: el.x0 + timestamp, x: el.x + timestamp, y: el.y})); export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/labeled-heatmap.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {scaleLinear} from 'd3-scale'; import { XYPlot, XAxis, YAxis, HeatmapSeries, LabelSeries, Hint } from 'react-vis'; const alphabet = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J']; const data = alphabet.reduce((acc, letter1, idx) => { return acc.concat( alphabet.map((letter2, jdx) => ({ x: `${letter1}1`, y: `${letter2}2`, color: (idx + jdx) % Math.floor(jdx / idx) || idx })) ); }, []); const {min, max} = data.reduce( (acc, row) => ({ min: Math.min(acc.min, row.color), max: Math.max(acc.max, row.color) }), {min: Infinity, max: -Infinity} ); export default class LabeledHeatmap extends Component { state = { value: false }; render() { const {value} = this.state; const exampleColorScale = scaleLinear() .domain([min, (min + max) / 2, max]) .range(['orange', 'white', 'cyan']); return ( `${letter}1`)} yType="ordinal" yDomain={alphabet.map(letter => `${letter}2`).reverse()} margin={50} width={500} height={500} > exampleColorScale(d.color)} style={{ stroke: 'white', strokeWidth: '2px', rectStyle: { rx: 10, ry: 10 } }} className="heatmap-series-example" data={data} onValueMouseOver={v => this.setState({value: v})} onSeriesMouseOut={() => this.setState({value: false})} /> `${d.color}`} /> {value !== false && } ); } } ================================================ FILE: packages/showcase/plot/labeled-stacked-vertical-bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries, VerticalBarSeriesCanvas, LabelSeries } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; const data = [ [ {x: 1, y: 8}, {x: 3, y: 5}, {x: 4, y: 15} ], [ {x: 3, y: 9}, {x: 4, y: 2}, {x: 5, y: 7} ], [ {x: 2, y: 11}, {x: 3, y: 7}, {x: 4, y: 9} ] ]; const labelsData = data.map(value => value.map(tuple => ({x: tuple.x, y: tuple.y, label: tuple.y.toString()})) ); const bars = data.map((value, index) => ( )); const labels = labelsData.map((value, index) => ( )); return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} /> {bars} {labels}
); } } ================================================ FILE: packages/showcase/plot/line-chart-canvas.js ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineMarkSeriesCanvas, LineMarkSeries, LineSeriesCanvas, LineSeries, Crosshair } from 'react-vis'; function getRandomData() { return new Array(1000).fill(0).map((row, i) => ({ x: i, y: Math.random() * 20, color: Math.random() * 10 })); } const randomData = getRandomData(); const colorRanges = { typeA: ['#59E4EC', '#0D676C'], typeB: ['#EFC1E3', '#B52F93'] }; const nextType = { typeA: 'typeB', typeB: 'typeA' }; const nextModeContent = { canvas: 'SWITCH TO SVG', svg: 'SWITCH TO CANVAS' }; const drawModes = ['canvas', 'svg']; export default class Example extends React.Component { state = { drawMode: 0, data: randomData, colorType: 'typeA', strokeWidth: 1, showMarks: true, value: false, hideComponent: false }; render() { const { colorType, drawMode, data, hideComponent, strokeWidth, showMarks, value } = this.state; const lineSeriesProps = { animation: true, className: 'mark-series-example', sizeRange: [5, 15], color: colorType === 'typeA' ? '#0D676C' : '#B52F93', colorRange: colorRanges[colorType], opacityType: 'literal', strokeWidth, data, onNearestX: d => this.setState({value: d}) }; const SVGComponent = showMarks ? LineMarkSeries : LineSeries; const CanvasComponent = showMarks ? LineMarkSeriesCanvas : LineSeriesCanvas; const mode = drawModes[drawMode]; return (
{`Mode: ${mode}`}
this.setState({drawMode: (drawMode + 1) % 2})} buttonContent={nextModeContent[mode]} /> this.setState({showMarks: !showMarks})} buttonContent={showMarks ? 'HIDE MARKS' : 'SHOW MARKS'} /> this.setState({data: getRandomData()})} buttonContent={'UPDATE DATA'} /> this.setState({colorType: nextType[colorType]})} buttonContent={`TOGGLE COLOR to ${nextType[colorType]}`} /> this.setState({strokeWidth: strokeWidth === 1 ? 2 : 1}) } buttonContent={'TOGGLE STROKEWIDTH'} /> this.setState({hideComponent: !hideComponent})} buttonContent={hideComponent ? 'SHOW' : 'HIDE'} />
{!hideComponent && ( this.setState({value: false})} width={600} height={300} > {mode === 'canvas' && } {mode === 'svg' && } {value && } )}
); } } ================================================ FILE: packages/showcase/plot/line-chart-with-style.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {curveCatmullRom} from 'd3-shape'; import { XYPlot, XAxis, YAxis, HorizontalGridLines, VerticalGridLines, LineSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/line-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {curveCatmullRom} from 'd3-shape'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, ChartLabel, HorizontalGridLines, VerticalGridLines, LineSeries, LineSeriesCanvas } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; const Line = useCanvas ? LineSeriesCanvas : LineSeries; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/line-series-canvas-nearest-xy-example.js ================================================ // Copyright (c) 2016-2018 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, LineSeriesCanvas, MarkSeriesCanvas} from 'react-vis'; const k = 100; const data = Array(k) .fill(0) .map((n, x) => ({x, y: x % 2 ? 180 : -180})); export default class LineSeriesCanvasNearestXYExample extends React.Component { state = { nearestXY: data[0] }; render() { const {nearestXY} = this.state; return ( { this.setState({nearestXY: point})} data={data} /> } { } ); } } ================================================ FILE: packages/showcase/plot/linemark-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, LineMarkSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/mixed-stacked-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries, VerticalBarSeriesCanvas, LineSeries } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/scatterplot-canvas.js ================================================ // Copyright (c) 2016 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries, MarkSeriesCanvas, Hint } from 'react-vis'; function getRandomData() { return new Array(100).fill(0).map(() => ({ x: Math.random() * 10, y: Math.random() * 20, size: Math.random() * 10, color: Math.random() * 10, opacity: Math.random() * 0.5 + 0.5 })); } const colorRanges = { typeA: ['#59E4EC', '#0D676C'], typeB: ['#EFC1E3', '#B52F93'] }; const randomData = getRandomData(); const nextType = { typeA: 'typeB', typeB: 'typeA' }; const nextModeContent = { canvas: 'SWITCH TO SVG', svg: 'SWITCH TO CANVAS' }; const drawModes = ['canvas', 'svg']; export default class Example extends React.Component { state = { drawMode: 0, data: randomData, colorType: 'typeA', value: false }; render() { const {drawMode, data, colorType} = this.state; const markSeriesProps = { animation: true, className: 'mark-series-example', sizeRange: [5, 15], seriesId: 'my-example-scatterplot', colorRange: colorRanges[colorType], opacityType: 'literal', data, onNearestXY: value => this.setState({value}) }; const mode = drawModes[drawMode]; return (
{`MODE: ${mode}`}
this.setState({drawMode: (drawMode + 1) % 2})} buttonContent={nextModeContent[mode]} /> this.setState({data: getRandomData()})} buttonContent={'UPDATE DATA'} /> this.setState({colorType: nextType[colorType]})} buttonContent={'UPDATE COLOR'} />
this.setState({value: false})} width={600} height={300} > {mode === 'canvas' && } {mode === 'svg' && } {this.state.value ? : null}
); } } ================================================ FILE: packages/showcase/plot/scatterplot.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, MarkSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/stacked-histogram.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalRectSeries, VerticalRectSeriesCanvas } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const RectSeries = useCanvas ? VerticalRectSeriesCanvas : VerticalRectSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/stacked-horizontal-bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, HorizontalBarSeries, HorizontalBarSeriesCanvas } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const BarSeries = useCanvas ? HorizontalBarSeriesCanvas : HorizontalBarSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/stacked-vertical-bar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, VerticalBarSeries, VerticalBarSeriesCanvas } from 'react-vis'; export default class Example extends React.Component { state = { useCanvas: false }; render() { const {useCanvas} = this.state; const BarSeries = useCanvas ? VerticalBarSeriesCanvas : VerticalBarSeries; const content = useCanvas ? 'TOGGLE TO SVG' : 'TOGGLE TO CANVAS'; return (
this.setState({useCanvas: !useCanvas})} buttonContent={content} />
); } } ================================================ FILE: packages/showcase/plot/whisker-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import { XYPlot, XAxis, YAxis, VerticalGridLines, HorizontalGridLines, WhiskerSeries } from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/plot/width-height-margin.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {XYPlot, XAxis, YAxis, VerticalGridLines, LineSeries} from 'react-vis'; export default function Example() { return ( ); } ================================================ FILE: packages/showcase/radar-chart/animated-radar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import RadarChart from 'react-vis/radar-chart'; import CircularGridLines from 'react-vis/plot/circular-grid-lines'; const DATA = [ { explosions: 7, wow: 10, dog: 8, sickMoves: 9, nice: 7 } ]; const DOMAIN = [ {name: 'nice', domain: [0, 100], tickFormat: t => t}, {name: 'explosions', domain: [6.9, 7.1]}, {name: 'wow', domain: [0, 11]}, {name: 'dog', domain: [0, 16]}, {name: 'sickMoves', domain: [0, 20]} ]; function generateData() { return [ Object.keys(DATA[0]).reduce((acc, key) => { acc[key] = DATA[0][key] + 5 * (Math.random() - 0.5); return acc; }, {}) ]; } export default class AnimatedRadar extends Component { state = { data: DATA }; render() { const {data} = this.state; return (
''} width={400} height={300} > i / 10 - 1)} /> this.setState({data: generateData()})} buttonContent={'UPDATE DATA'} />
); } } ================================================ FILE: packages/showcase/radar-chart/basic-radar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {format} from 'd3-format'; import RadarChart from 'react-vis/radar-chart'; const DATA = [ { name: 'Mercedes', mileage: 7, price: 10, safety: 8, performance: 9, interior: 7, warranty: 7 }, { name: 'Honda', mileage: 8, price: 6, safety: 9, performance: 6, interior: 3, warranty: 9 }, { name: 'Chevrolet', mileage: 5, price: 4, safety: 6, performance: 4, interior: 5, warranty: 6 } ]; const basicFormat = format('.2r'); const wideFormat = format('.3r'); export default function BasicRadarChart() { return ( wideFormat(t)} startingAngle={0} domains={[ {name: 'mileage', domain: [0, 10]}, { name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`, getValue: d => d.price }, {name: 'safety', domain: [5, 10], getValue: d => d.safety}, {name: 'performance', domain: [0, 10], getValue: d => d.performance}, {name: 'interior', domain: [0, 7], getValue: d => d.interior}, {name: 'warranty', domain: [10, 2], getValue: d => d.warranty} ]} width={400} height={300} /> ); } ================================================ FILE: packages/showcase/radar-chart/four-quadrant-radar-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import RadarChart from 'react-vis/radar-chart'; const RADAR_PROPS = { data: [ { C: 30, VisualBasics: 60, Excel: 40, Access: 40 } ], domains: [ {name: 'C', domain: [0, 100]}, {name: 'VisualBasics', domain: [0, 100]}, {name: 'Excel', domain: [0, 100]}, {name: 'Access', domain: [0, 100]} ], height: 300, width: 400 }; export default function FourQuadrantRadarChart() { return ( ); } ================================================ FILE: packages/showcase/radar-chart/radar-chart-series-tooltips.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {format} from 'd3-format'; import RadarChart from 'react-vis/radar-chart'; import {Hint} from 'react-vis'; const DATA = [ { name: 'Mercedes', mileage: 7, price: 10, safety: 8, performance: 9, interior: 7, warranty: 7 }, { name: 'Honda', mileage: 8, price: 6, safety: 9, performance: 6, interior: 3, warranty: 9 }, { name: 'Chevrolet', mileage: 5, price: 4, safety: 6, performance: 4, interior: 5, warranty: 6 } ]; const basicFormat = format('.2r'); const wideFormat = format('.3r'); const tipStyle = { display: 'flex', color: '#fff', background: '#000', alignItems: 'center', padding: '5px' }; export default class BasicRadarChart extends Component { state = { hoveredCell: false }; render() { const {hoveredCell} = this.state; return ( wideFormat(t)} startingAngle={0} domains={[ {name: 'mileage', domain: [0, 10]}, { name: 'price', domain: [2, 16], tickFormat: t => `$${basicFormat(t)}`, getValue: d => d.price }, {name: 'safety', domain: [5, 10], getValue: d => d.safety}, {name: 'performance', domain: [0, 10], getValue: d => d.performance}, {name: 'interior', domain: [0, 7], getValue: d => d.interior}, {name: 'warranty', domain: [10, 2], getValue: d => d.warranty} ]} width={400} height={300} onSeriesMouseOver={data => { this.setState({hoveredCell: data.event[0]}); }} onSeriesMouseOut={() => this.setState({hoveredCell: false})} > {hoveredCell && (
{hoveredCell.name}
)}
); } } ================================================ FILE: packages/showcase/radar-chart/radar-chart-with-tooltips.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import RadarChart from 'react-vis/radar-chart'; import {Hint} from 'react-vis'; // The first 6 data elements here are to simulate a 'spider' type of radar chart - // similar to CircularGridLines, but straight edges instead. const DATA = [ { name: 'Spider5', mileage: 5, price: 5, safety: 5, performance: 5, interior: 5, warranty: 5, fill: '#f8f8f8', stroke: '#cccccc' }, { name: 'Spider4', mileage: 4, price: 4, safety: 4, performance: 4, interior: 4, warranty: 4, fill: 'white', stroke: '#cccccc' }, { name: 'Spider3', mileage: 3, price: 3, safety: 3, performance: 3, interior: 3, warranty: 3, fill: '#f8f8f8', stroke: '#cccccc' }, { name: 'Spider2', mileage: 2, price: 2, safety: 2, performance: 2, interior: 2, warranty: 2, fill: 'white', stroke: '#cccccc' }, { name: 'Spider1', mileage: 1, price: 1, safety: 1, performance: 1, interior: 1, warranty: 1, fill: '#f8f8f8', stroke: '#cccccc' }, { name: 'Spider0', mileage: 0.1, price: 0.1, safety: 0.1, performance: 0.1, interior: 0.1, warranty: 0.1, fill: '#f8f8f8', stroke: '#cccccc' }, { name: 'Mercedes', mileage: 3, price: 4, safety: 5, performance: 1.5, interior: 4, warranty: 4.5, fill: 'rgba(114,172,240,0.5)', stroke: 'rgba(114,172,240,0.2)' } ]; const tipStyle = { display: 'flex', color: '#fff', background: '#000', alignItems: 'center', padding: '5px' }; export default class RadarChartWithTooltips extends Component { state = { hoveredCell: false }; render() { const {hoveredCell} = this.state; return ( { return ''; }} domains={[ { name: 'mileage', domain: [0, 5], tickFormat: t => { if (t < 5 && t > 0) { return Math.round(t); } return ''; } }, { name: 'price', domain: [0, 5], getValue: d => d.price }, {name: 'safety', domain: [0, 5], getValue: d => d.safety}, {name: 'performance', domain: [0, 5], getValue: d => d.performance}, {name: 'interior', domain: [0, 5], getValue: d => d.interior}, {name: 'warranty', domain: [0, 5], getValue: d => d.warranty} ]} width={300} height={300} onValueMouseOver={v => { this.setState({hoveredCell: v}); }} onValueMouseOut={() => this.setState({hoveredCell: false})} style={{ polygons: { strokeWidth: 1, strokeOpacity: 0.8, fillOpacity: 0.8 }, labels: { textAnchor: 'middle' }, axes: { line: { fillOpacity: 0.8, strokeWidth: 0.5, strokeOpacity: 0.8 }, ticks: { fillOpacity: 0, strokeOpacity: 0 }, text: {} } }} colorRange={['transparent']} hideInnerMostValues={false} renderAxesOverPolygons={true} > {hoveredCell && hoveredCell.dataName === 'Mercedes' && (
{hoveredCell.domain}: {hoveredCell.value}
)}
); } } ================================================ FILE: packages/showcase/radial-chart/arc-series-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import ShowcaseButton from '../showcase-components/showcase-button'; import {XYPlot, ArcSeries, XAxis, YAxis} from 'react-vis'; import {EXTENDED_DISCRETE_COLOR_RANGE as COLORS} from 'react-vis/theme'; const PI = Math.PI; function updateData() { const divider = Math.floor(Math.random() * 8 + 3); const newData = [...new Array(5)].map((row, index) => { return { color: index, radius0: Math.random() > 0.8 ? Math.random() + 1 : 0, radius: Math.random() * 3 + 1, angle: ((index + 1) * PI) / divider, angle0: (index * PI) / divider }; }); return newData.concat([ {angle0: 0, angle: PI * 2 * Math.random(), radius: 1.1, radius0: 0.8} ]); } function updateLittleData() { const portion = Math.random(); return [ { angle0: 0, angle: portion * PI * 2, radius0: 0, radius: 10, color: COLORS[13] }, { angle0: portion * PI * 2, angle: 2 * PI, radius0: 0, radius: 10, color: COLORS[12] } ]; } export default class Example extends React.Component { state = { data: updateData(), littleData: updateLittleData(), value: false }; render() { return (
this.setState({ data: updateData(), littleData: updateLittleData() }) } buttonContent={'UPDATE'} /> { if (this.state.value && this.state.value.color === row.color) { return {...row, style: {stroke: 'black', strokeWidth: '5px'}}; } return row; })} colorRange={COLORS} onValueMouseOver={row => this.setState({value: row})} onSeriesMouseOut={() => this.setState({value: false})} colorType={'category'} />
); } } ================================================ FILE: packages/showcase/radial-chart/custom-radius-radial-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {CircularGridLines, RadialChart} from 'react-vis'; const DATA = [ { angle: 1, id: 1, radius: 10 }, { angle: 2, label: 'Super Custom label', subLabel: 'With annotation', id: 2, radius: 20 }, { angle: 5, id: 3, radius: 5, label: 'Alt Label' }, { angle: 3, id: 4, radius: 14 }, { angle: 5, id: 5, radius: 12, subLabel: 'Sub Label only' } ]; function mapData(hoveredSection) { return DATA.map((row, index) => { return { ...row, innerRadius: hoveredSection === index + 1 ? row.radius - 1 : null, opacity: !hoveredSection || hoveredSection === index + 1 ? 1 : 0.6 }; }); } export default class SimpleRadialChart extends Component { state = { hoveredSection: false }; render() { const {hoveredSection} = this.state; return ( this.setState({hoveredSection: row.id})} onMouseLeave={() => this.setState({hoveredSection: false})} width={600} height={300} > ); } } ================================================ FILE: packages/showcase/radial-chart/donut-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {Component} from 'react'; import {RadialChart, Hint} from 'react-vis'; export default class SimpleRadialChart extends Component { state = { value: false }; render() { const {value} = this.state; return ( d.theta} data={[ {theta: 2, className: 'custom-class'}, {theta: 6}, {theta: 2}, {theta: 3}, {theta: 1} ]} onValueMouseOver={v => this.setState({value: v})} onSeriesMouseOut={() => this.setState({value: false})} width={300} height={300} padAngle={0.04} > {value !== false && } ); } } ================================================ FILE: packages/showcase/radial-chart/gradient-pie.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {GradientDefs, RadialChart} from 'react-vis'; export default function GradientPie() { return ( `url(#${d.gradientLabel})`} data={[ {angle: 1, gradientLabel: 'grad1'}, {angle: 2, gradientLabel: 'grad2'}, {angle: 5, gradientLabel: 'grad3'} ]} labelsRadiusMultiplier={1.1} labelsStyle={{fontSize: 16, fill: '#222'}} showLabels style={{stroke: '#fff', strokeWidth: 2}} width={400} height={300} > ); } ================================================ FILE: packages/showcase/radial-chart/simple-radial-chart.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import RadialChart from 'react-vis/radial-chart'; export default function SimpleRadialChart() { return ( d.name} data={[ {angle: 1, color: '#89DAC1', name: 'green', opacity: 0.2}, {angle: 2, color: '#F6D18A', name: 'yellow'}, {angle: 5, color: '#1E96BE', name: 'cyan'}, {angle: 3, color: '#DA70BF', name: 'magenta'}, {angle: 5, color: '#F6D18A', name: 'yellow again'} ]} labelsRadiusMultiplier={1.1} labelsStyle={{fontSize: 16, fill: '#222'}} showLabels style={{stroke: '#fff', strokeWidth: 2}} width={400} height={300} /> ); } ================================================ FILE: packages/showcase/sankey/basic.js ================================================ import React from 'react'; import Sankey from 'react-vis/sankey'; const nodes = [{name: 'a', rotation: 0}, {name: 'b'}, {name: 'c'}]; const links = [ {source: 0, target: 1, value: 10, opacity: 0.2}, {source: 0, target: 2, value: 20}, {source: 1, target: 2, value: 20} ]; export default function BasicSankeyExample() { return ( ); } ================================================ FILE: packages/showcase/sankey/energy-sankey.js ================================================ import React from 'react'; import Sankey from 'react-vis/sankey'; import Energy from '../datasets/energy.json'; import ShowcaseButton from '../showcase-components/showcase-button'; const MODE = ['justify', 'center', 'left', 'right']; export default class EnergySankey extends React.Component { state = { modeIndex: 0 }; updateModeIndex = increment => () => { const newIndex = this.state.modeIndex + (increment ? 1 : -1); const modeIndex = newIndex < 0 ? MODE.length - 1 : newIndex >= MODE.length ? 0 : newIndex; this.setState({modeIndex}); }; render() { const {modeIndex} = this.state; return (
{MODE[modeIndex]}
); } } ================================================ FILE: packages/showcase/sankey/link-event.js ================================================ import React from 'react'; import Sankey from 'react-vis/sankey'; const BLURRED_LINK_OPACITY = 0.3; const FOCUSED_LINK_OPACITY = 0.6; const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; const links = [ {source: 0, target: 1, value: 10}, {source: 0, target: 2, value: 20}, {source: 1, target: 2, value: 20} ]; export default class LinkEventSankeyExample extends React.Component { state = { activeLink: null }; render() { const {activeLink} = this.state; // Note: d3.sankey currently mutates the `nodes` and `links` arrays, which doesn't play nice // with React's single-direction data flow. We create a copy of each before we pass to the sankey // component, just to be sure. return (
{`${ activeLink ? `${nodes[activeLink.source.index].name} -> ${ nodes[activeLink.target.index].name }` : 'None' } selected`}
({...d}))} links={links.map((d, i) => ({ ...d, opacity: activeLink && i === activeLink.index ? FOCUSED_LINK_OPACITY : BLURRED_LINK_OPACITY }))} width={200} height={200} onLinkMouseOver={node => this.setState({activeLink: node})} onLinkMouseOut={() => this.setState({activeLink: null})} />
); } } ================================================ FILE: packages/showcase/sankey/link-hint.js ================================================ import React from 'react'; import Sankey from 'react-vis/sankey'; import Hint from 'react-vis/plot/hint'; const BLURRED_LINK_OPACITY = 0.3; const FOCUSED_LINK_OPACITY = 0.6; const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; const links = [ {source: 0, target: 1, value: 10}, {source: 0, target: 2, value: 20}, {source: 1, target: 2, value: 20} ]; export default class LinkHintSankeyExample extends React.Component { state = { activeLink: null }; _renderHint() { const {activeLink} = this.state; // calculate center x,y position of link for positioning of hint const x = activeLink.source.x1 + (activeLink.target.x0 - activeLink.source.x1) / 2; const y = activeLink.y0 - (activeLink.y0 - activeLink.y1) / 2; const hintValue = { [`${activeLink.source.name} ➞ ${activeLink.target.name}`]: activeLink.value }; return ; } render() { const {activeLink} = this.state; // Note: d3.sankey currently mutates the `nodes` and `links` arrays, which doesn't play nice // with React's single-direction data flow. We create a copy of each before we pass to the sankey // component, just to be sure. return (
({...d}))} links={links.map((d, i) => ({ ...d, opacity: activeLink && i === activeLink.index ? FOCUSED_LINK_OPACITY : BLURRED_LINK_OPACITY }))} width={200} height={200} // do not use voronoi in combination with link mouse over hasVoronoi={false} onLinkMouseOver={node => this.setState({activeLink: node})} onLinkMouseOut={() => this.setState({activeLink: null})} > {activeLink && this._renderHint()}
); } } ================================================ FILE: packages/showcase/sankey/voronoi.js ================================================ import React from 'react'; import Sankey from 'react-vis/sankey'; const BLURRED_NODE_OPACITY = 0.8; const FOCUSED_NODE_OPACITY = 1; const nodes = [{name: 'a'}, {name: 'b'}, {name: 'c'}]; const links = [ {source: 0, target: 1, value: 10}, {source: 0, target: 2, value: 20}, {source: 1, target: 2, value: 20} ]; export default class VoronoiSankeyExample extends React.Component { state = { activeNode: null }; render() { const {activeNode} = this.state; // Note: d3.sankey currently mutates the `nodes` and `links` arrays, which doesn't play nice // with React's single-direction data flow. We create a copy of each before we pass to the sankey // component, just to be sure. return (
{`${activeNode ? activeNode.name : 'None'} selected`}
{ const isActiveNode = activeNode && d.name === activeNode.name; return { ...d, opacity: isActiveNode ? FOCUSED_NODE_OPACITY : BLURRED_NODE_OPACITY, name: isActiveNode ? `!${d.name}!` : d.name }; })} links={links.map(d => ({...d}))} width={200} height={200} hasVoronoi onValueMouseOver={node => this.setState({activeNode: node})} onValueMouseOut={() => this.setState({activeNode: null})} />
); } } ================================================ FILE: packages/showcase/showcase-app.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import ShowcaseDropdown from './showcase-components/showcase-dropdown'; import { AxesShowcase, PlotsShowcase, SunburstSection, RadialShowcase, RadarShowcase, LegendsShowcase, SankeysShowcase, TreemapShowcase, ParallelCoordinatesShowcase, MiscShowcase, Candlestick, ForceDirectedGraph, ResponsiveVis, StreamgraphExample, IrisDashboard } from './showcase-index'; const sectionNames = [ {root: true, link: '', name: 'RETURN TO ROOT'}, // basic examples {label: true, name: 'BASIC EXAMPLES'}, {showByDefault: true, link: 'plots', name: 'Plots', showcase: PlotsShowcase}, {showByDefault: true, link: 'axes', name: 'Axes', showcase: AxesShowcase}, { showByDefault: true, link: 'radial-charts', name: 'Radial Charts', showcase: RadialShowcase }, { showByDefault: true, link: 'radar-charts', name: 'Radar Charts', showcase: RadarShowcase }, { showByDefault: true, link: 'treemaps', name: 'Treemaps', showcase: TreemapShowcase }, { showByDefault: true, link: 'legends', name: 'Legends', showcase: LegendsShowcase }, { showByDefault: true, link: 'sunbursts', name: 'Sunbursts', showcase: SunburstSection }, { showByDefault: true, link: 'sankeys', name: 'Sankeys', showcase: SankeysShowcase }, { showByDefault: true, link: 'parallel-coordinates', name: 'Parallel Coordinates', showcase: ParallelCoordinatesShowcase }, {showByDefault: true, link: 'misc', name: 'Misc', showcase: MiscShowcase}, // in depth examples {label: true, name: 'ADVANCED EXAMPLES'}, { showByDefault: false, link: 'candlestick', name: 'Candlestick', showcase: Candlestick }, { showByDefault: false, link: 'force-directed', name: 'ForceDirectedGraph', showcase: ForceDirectedGraph }, { showByDefault: false, link: 'streamgraph', name: 'Streamgraph', showcase: StreamgraphExample }, { showByDefault: false, link: 'irisdashboard', name: 'IrisDashboard', showcase: IrisDashboard }, { showByDefault: false, link: 'responsive', name: 'ResponsiveVis', showcase: ResponsiveVis } ]; function App(props) { const {forExample} = props; const linkedSection = location.href.split('/#')[1]; const foundSection = sectionNames.find( section => section.link === linkedSection ); const filteredSections = sectionNames .filter(section => { // if at http://localhost:3001/, just return everything if (!linkedSection) { return section.showByDefault; } const showThisSection = foundSection && section.link === foundSection.link; const showDefaultSections = (!foundSection || foundSection.root) && section.showByDefault; return showThisSection || showDefaultSections; }) .map(section => { if (section.label || section.root) { return
; } return ; }); return (
{!forExample && (
react-vis { const {label, link, name} = section; const content = label ? (
{name}
) : ( {name} ); return
  • {content}
  • ; })} />
    )} {filteredSections}
    ); } App.propTypes = { forExample: PropTypes.bool }; export default App; ================================================ FILE: packages/showcase/showcase-components/showcase-button.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; function ShowcaseButton(props) { const {buttonContent, onClick} = props; return ( ); } ShowcaseButton.propTypes = { buttonContent: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired }; export default ShowcaseButton; ================================================ FILE: packages/showcase/showcase-components/showcase-dropdown.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import PropTypes from 'prop-types'; class ShowcaseDropdown extends React.Component { constructor(props) { super(props); this.state = { open: false }; } toggleState = () => { this.setState({open: !this.state.open}); }; render() { const {items} = this.props; const {open} = this.state; return (
    {'SELECT SECTION'}
    {open && (
    )} {open &&
      {items}
    }
    ); } } ShowcaseDropdown.propTypes = { items: PropTypes.arrayOf(PropTypes.component) }; export default ShowcaseDropdown; ================================================ FILE: packages/showcase/showcase-components/showcase-utils.js ================================================ import React from 'react'; import {SHOWCASE_LINKS} from '../showcase-links'; export function mapSection(section) { const {docsLink, comment, name, componentName} = section; const SectionComponent = section.component; const linkProps = { className: 'docs-link', target: '_blank', rel: 'noopener noreferrer' }; const exampleLink = SHOWCASE_LINKS[componentName]; return (

    {name}

    {exampleLink && ( {' '} View Code )} {docsLink && ( {' '} Documentation{' '} )}
    {comment &&

    {comment}

    }
    ); } ================================================ FILE: packages/showcase/showcase-components/source-linker.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; export default function sourceLinker(ShowcaseComponent, link) { return function renderExample() { return (
    {ShowcaseComponent && } {link && ( {'> View Code'} )}
    ); }; } ================================================ FILE: packages/showcase/showcase-index.js ================================================ export AxesShowcase from './showcase-sections/axes-showcase'; export PlotsShowcase from './showcase-sections/plots-showcase'; export SunburstSection from './showcase-sections/sunburst-showcase'; export RadialShowcase from './showcase-sections/radial-showcase'; export RadarShowcase from './showcase-sections/radar-showcase'; export LegendsShowcase from './showcase-sections/legends-showcase'; export SankeysShowcase from './showcase-sections/sankeys-showcase'; export TreemapShowcase from './showcase-sections/treemap-showcase'; export ParallelCoordinatesShowcase from './showcase-sections/parallel-coordinates-showcase'; export MiscShowcase from './showcase-sections/misc-showcase'; export Candlestick from './examples/candlestick/candlestick-example'; export ForceDirectedGraph from './examples/force-directed-graph/force-directed-example'; export ResponsiveVis from './examples/responsive-vis/responsive-vis-example'; export StreamgraphExample from './examples/streamgraph/streamgraph-example'; export IrisDashboard from './examples/iris-dashboard/iris-dashboard'; ================================================ FILE: packages/showcase/showcase-links.js ================================================ export const REACTVIS_BASE_URL = 'https://github.com/uber/react-vis/blob/master/packages/react-vis'; export const SHOWCASE_BASE_URL = 'https://github.com/uber/react-vis/blob/master/packages/showcase'; export const SHOWCASE_LINKS = { // AXES SHOW CASE LINKS AxisOn0: `${SHOWCASE_BASE_URL}/axes/axis-on-0.js`, CustomAxesOrientation: `${SHOWCASE_BASE_URL}/axes/custom-axes-orientation.js`, CustomAxisChart: `${SHOWCASE_BASE_URL}/axes/custom-axis.js`, CustomAxes: `${SHOWCASE_BASE_URL}/axes/custom-axes.js`, AxisWithTurnedLabels: `${SHOWCASE_BASE_URL}/plot/axis-with-turned-labels.js`, PaddedAxis: `${SHOWCASE_BASE_URL}/axes/padded-axis.js`, // AXES - TOOLTIPS SHOW CASE LINKS StaticHints: `${SHOWCASE_BASE_URL}/axes/static-hints.js`, DynamicHints: `${SHOWCASE_BASE_URL}/axes/dynamic-hints.js`, DynamicSimpleEdgeHints: `${SHOWCASE_BASE_URL}/axes/dynamic-simple-edge-hints.js`, DynamicSimpleTopEdgeHints: `${SHOWCASE_BASE_URL}/axes/dynamic-simple-topedge-hints.js`, DynamicProgrammaticRightEdgeHints: `${SHOWCASE_BASE_URL}/axes/dynamic-programmatic-rightedge-hints.js`, DynamicComplexEdgeHints: `${SHOWCASE_BASE_URL}/axes/dynamic-complex-edge-hints.js`, StaticCrosshair: `${SHOWCASE_BASE_URL}/axes/static-crosshair.js`, DynamicCrosshair: `${SHOWCASE_BASE_URL}/axes/dynamic-crosshair.js`, DynamicCrosshairScatterplot: `${SHOWCASE_BASE_URL}/axes/dynamic-crosshair-scatterplot.js`, // AXES - DECORATIVE_AXES SHOWCASE LINKS DecorativeAxisCrissCross: `${SHOWCASE_BASE_URL}/axes/decorative-axes-criss-cross.js`, ParallelCoordinatesExample: `${SHOWCASE_BASE_URL}/axes/parallel-coordinates-example.js`, // MISC SHOWCASE LINKS SyncedCharts: `${SHOWCASE_BASE_URL}/misc/synced-charts.js`, TimeChart: `${SHOWCASE_BASE_URL}/misc/time-chart.js`, TriangleExample: `${SHOWCASE_BASE_URL}/misc/triangle-example.js`, VoronoiLineChart: `${SHOWCASE_BASE_URL}/misc/voronoi-line-chart.js`, GradientExample: `${SHOWCASE_BASE_URL}/misc/gradient-example.js`, ClipExample: `${SHOWCASE_BASE_URL}/misc/clip-example.js`, AnimationExample: `${SHOWCASE_BASE_URL}/misc/animation-example.js`, LabelSeriesExample: `${SHOWCASE_BASE_URL}/misc/label-series-example.js`, NullDataExample: `${SHOWCASE_BASE_URL}/misc/null-data-example.js`, ZoomableChartExample: `${SHOWCASE_BASE_URL}/misc/zoomable-chart-example.js`, SelectionPlotExample: `${SHOWCASE_BASE_URL}/misc/selection-plot-example.js`, DragableChartExample: `${SHOWCASE_BASE_URL}/misc/dragable-chart-example.js`, BidirectionDragChart: `${SHOWCASE_BASE_URL}/misc/2d-dragable-plot.js`, // PARALLEL COORDS LINKS BasicParallelCoordinates: `${SHOWCASE_BASE_URL}/parallel-coordinates/animated-parallel-coordinates.js`, AnimatedParallelCoordinates: `${SHOWCASE_BASE_URL}/parallel-coordinates/basic-parallel-coordinates.js`, // PLOTS SHOWCASE LINKES LineChart: `${SHOWCASE_BASE_URL}/plot/line-chart.js`, LineChartWithStyle: `${SHOWCASE_BASE_URL}/plot/line-chart-with-style.js`, LineMarkChart: `${SHOWCASE_BASE_URL}/plot/linemark-chart.js`, LineChartCanvas: `${SHOWCASE_BASE_URL}/plot/line-chart-canvas.js`, LineChartManyColors: `${SHOWCASE_BASE_URL}/color/line-chart-many-colors.js`, ScatterplotChart: `${SHOWCASE_BASE_URL}/plot/scatterplot.js`, ScatterplotCanvas: `${SHOWCASE_BASE_URL}/plot/scatterplot-canvas.js`, WhiskerChart: `${SHOWCASE_BASE_URL}/plot/whisker-chart.js`, AreaChart: `${SHOWCASE_BASE_URL}/plot/area-chart.js`, AreaChartElevated: `${SHOWCASE_BASE_URL}/plot/area-chart-elevated.js`, BarChart: `${SHOWCASE_BASE_URL}/plot/bar-chart.js`, BigBaseBarChart: `${SHOWCASE_BASE_URL}/plot/big-base-bar-chart.js`, StackedHorizontalBarChart: `${SHOWCASE_BASE_URL}/plot/stacked-horizontal-bar-chart.js`, StackedVerticalBarChart: `${SHOWCASE_BASE_URL}/plot/stacked-vertical-bar-chart.js`, ClusteredStackedVerticalBarChart: `${SHOWCASE_BASE_URL}/plot/clustered-stacked-bar-chart.js`, StackedHistogram: `${SHOWCASE_BASE_URL}/plot/stacked-histogram.js`, Histogram: `${SHOWCASE_BASE_URL}/plot/histogram.js`, HeatmapChart: `${SHOWCASE_BASE_URL}/plot/heatmap-chart.js`, HexHeatmap: `${SHOWCASE_BASE_URL}/plot/hex-heatmap.js`, HexbinSizeExample: `${SHOWCASE_BASE_URL}/plot/hexbin-size-example.js`, LabeledHeatmap: `${SHOWCASE_BASE_URL}/plot/labeled-heatmap.js`, ContourSeriesExample: `${SHOWCASE_BASE_URL}/plot/contour-series-example.js`, CustomSVGExample: `${SHOWCASE_BASE_URL}/plot/custom-svg-example.js`, CustomSVGAllTheMarks: `${SHOWCASE_BASE_URL}/plot/custom-svg-all-the-marks.js`, CustomSVGRootLevel: `${SHOWCASE_BASE_URL}/plot/custom-svg-root-level.js`, // BASIC COMPONENTS SHOWCASE LINKS WidthHeightMarginChart: `${SHOWCASE_BASE_URL}/plot/width-height-margin.js`, CustomScales: `${SHOWCASE_BASE_URL}/plot/custom-scales.js`, GridLinesChart: `${SHOWCASE_BASE_URL}/plot/grid.js`, FauxScatterplotChart: `${SHOWCASE_BASE_URL}/plot/faux-radial-scatterplot.js`, EmptyChart: `${SHOWCASE_BASE_URL}/axes/empty-chart.js`, // RADAR CHART SHOWCASE LINKS AnimatedRadarChart: `${SHOWCASE_BASE_URL}/radar-chart/animated-radar-chart.js`, BasicRadarChart: `${SHOWCASE_BASE_URL}/radar-chart/basic-radar-chart.js`, FourQuadrantRadarChart: `${SHOWCASE_BASE_URL}/radar-chart/four-quadrant-radar-chart.js`, RadarChartWithTooltips: `${SHOWCASE_BASE_URL}/radar-chart/radar-chart-with-tooltips.js`, RadarChartSeriesTooltips: `${SHOWCASE_BASE_URL}/radar-chart/radar-chart-series-tooltips.js`, // RADIAL CHART SHOWCASE LINKS SimpleRadialChart: `${SHOWCASE_BASE_URL}/radial-chart/simple-radial-chart.js`, GradientPie: `${SHOWCASE_BASE_URL}/radial-chart/gradient-pie.js`, DonutChartExample: `${SHOWCASE_BASE_URL}/radial-chart/donut-chart.js`, CustomRadiusRadialChart: `${SHOWCASE_BASE_URL}/radial-chart/custom-radius-radial-chart.js`, // SANKEY SHOWCASE LINKS BasicSankeyExample: `${SHOWCASE_BASE_URL}/sankey/basic.js`, VoronoiSankeyExample: `${SHOWCASE_BASE_URL}/sankey/voronoi.js`, LinkEventSankeyExample: `${SHOWCASE_BASE_URL}/sankey/link-event.js`, LinkHintSankeyExample: `${SHOWCASE_BASE_URL}/sankey/link-hint.js`, EnergySankeyExample: `${SHOWCASE_BASE_URL}/sankey/energy-sankey.js`, // SUNBURST SHOWCASE LINKS ArcSeriesExample: `${SHOWCASE_BASE_URL}/radial-chart/arc-series-example.js`, BasicSunburst: `${SHOWCASE_BASE_URL}/sunbursts/basic-sunburst.js`, ClockExample: `${SHOWCASE_BASE_URL}/sunbursts/clock-example.js`, AnimatedSunburst: `${SHOWCASE_BASE_URL}/sunbursts/animated-sunburst.js`, SunburstWithTooltips: `${SHOWCASE_BASE_URL}/sunbursts/sunburst-with-tooltips.js`, // TREEMAP SHOWCASE LINKS SimpleTreemap: `${SHOWCASE_BASE_URL}/treemap/simple-treemap.js`, TreemapExample: `${SHOWCASE_BASE_URL}/treemap/dynamic-treemap.js`, // LEGENDS SHOWCASE LINKS VerticalDiscreteColorLegendExample: `${SHOWCASE_BASE_URL}/legends/vertical-discrete-color.js`, HorizontalDiscreteColorLegendExample: `${SHOWCASE_BASE_URL}/legends/horizontal-discrete-color.js`, HorizontalDiscreteCustomPalette: `${SHOWCASE_BASE_URL}/legends/horizontal-discrete-custom-palette.js`, SearchableDiscreteColorLegendExample: `${SHOWCASE_BASE_URL}/legends/searchable-discrete-color.js`, // eslint-disable-next-line max-len SearchableDiscreteColorLegendHoverExample: `${SHOWCASE_BASE_URL}/legends/searchable-discrete-color-hover.js`, ContinuousColorLegendExample: `${SHOWCASE_BASE_URL}/legends/continuous-color.js`, ContinuousSizeLegendExample: `${SHOWCASE_BASE_URL}/legends/continuous-size.js'` }; ================================================ FILE: packages/showcase/showcase-sections/axes-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {showCase} from '../index'; const { AxisOn0, AxisWithTurnedLabels, CustomAxes, CustomAxisChart, CustomAxesOrientation, CustomAxisTickFormat, CustomAxisTickElement, DecorativeAxisCrissCross, DynamicComplexEdgeHints, DynamicCrosshair, DynamicCrosshairScatterplot, DynamicHints, DynamicProgrammaticRightEdgeHints, DynamicSimpleEdgeHints, DynamicSimpleTopEdgeHints, PaddedAxis, ParallelCoordinatesExample, StaticCrosshair, StaticHints } = showCase; /* eslint-disable max-len */ const AXES = [ { name: 'Axis on 0', component: AxisOn0, componentName: 'AxisOn0' }, { name: 'Custom Axes Orientation', component: CustomAxesOrientation, componentName: 'CustomAxesOrientation' }, { name: 'Custom Axis', component: CustomAxisChart, componentName: 'CustomAxisChart' }, { name: 'Custom axis tick format', component: CustomAxisTickFormat }, { name: 'Custom axis tick label element', component: CustomAxisTickElement }, { name: 'Even more Custom Axes', component: CustomAxes, componentName: 'CustomAxes' }, { name: 'Turned axis labels', component: AxisWithTurnedLabels, componentName: 'AxisWithTurnedLabels' }, { name: 'Unpadded Axis vs Padded Axis', component: PaddedAxis, componentName: 'PaddedAxis' } ]; const TOOLTIPS = [ { name: 'Static Hints', component: StaticHints, componentName: 'StaticHints' }, { name: 'Dynamic Hints', comment: 'Move mouse over the point to see the hint.', component: DynamicHints, componentName: 'DynamicHints' }, { name: 'Dynamic Simple Edge Hints', comment: 'Mouse over point. Hint appears on different edges. Left margin enables first point to show w/o break.', component: DynamicSimpleEdgeHints, componentName: 'DynamicSimpleEdgeHints' }, { name: 'Dynamic Simple Top Edge Hints', comment: 'Mouse over point. horizontalAlign=ALIGN.AUTO, verticalAlign=ALIGN.TOP_EDGE Hint pinned to top edge, pole moves along edge, hint box on right of pole for first 2 data points and left otherwise.', component: DynamicSimpleTopEdgeHints, componentName: 'DynamicSimpleTopEdgeHints' }, { name: 'Dynamic Programmatic Right Edge Hints', comment: 'Mouse over point. getAlignStyle method returns style object with right and top CSS props set (pinned right edge and at y position)', component: DynamicProgrammaticRightEdgeHints, componentName: 'DynamicProgrammaticRightEdgeHints' }, { name: 'Dynamic Complex Edge Hints', comment: 'Mouse over point. Hint uses flex, css to show hint and pole from point to outside plot edge (css for margin values).', component: DynamicComplexEdgeHints, componentName: 'DynamicComplexEdgeHints' }, { name: 'Static Crosshair', component: StaticCrosshair, componentName: 'StaticCrosshair' }, { name: 'Dynamic Crosshair', comment: 'Move your mouse over the chart to see the point.', component: DynamicCrosshair, componentName: 'DynamicCrosshair' }, { name: 'Dynamic Crosshair Scatterplot', comment: 'Move your mouse over the chart to see the point.', component: DynamicCrosshairScatterplot, componentName: 'DynamicCrosshairScatterplot' } ]; /* eslint-enable max-len */ const DECORATIVE_AXES = [ { name: 'Diagonal Axes', component: DecorativeAxisCrissCross, componentName: 'DecorativeAxisCrissCross' }, { name: 'Parallel Coordinates', component: ParallelCoordinatesExample, componentName: 'ParallelCoordinatesExample' } ]; function AxesShowcase() { return (

    Axes

    {AXES.map(mapSection)}

    Tooltips

    {TOOLTIPS.map(mapSection)}

    DecorativeAxis

    {DECORATIVE_AXES.map(mapSection)}
    ); } export default AxesShowcase; ================================================ FILE: packages/showcase/showcase-sections/legends-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {showCase} from '../index'; const { ContinuousColorLegendExample, ContinuousSizeLegendExample, HorizontalDiscreteColorLegendExample, HorizontalDiscreteCustomPalette, SearchableDiscreteColorLegendExample, SearchableDiscreteColorLegendHoverExample, VerticalDiscreteColorLegendExample } = showCase; /* eslint-disable max-len */ const DISCRETE_LEGENDS = [ { name: 'Vertical legend', component: VerticalDiscreteColorLegendExample, componentName: 'VerticalDiscreteColorLegendExample' }, { name: 'Horizontal legend with stroke styles', component: HorizontalDiscreteColorLegendExample, componentName: 'HorizontalDiscreteColorLegendExample' }, { name: 'Custom palette with hover interaction', component: HorizontalDiscreteCustomPalette, componentName: 'HorizontalDiscreteCustomPalette' }, { name: 'Discrete color legend with search', component: SearchableDiscreteColorLegendExample, componentName: 'SearchableDiscreteColorLegendExample' }, { name: 'Discrete color legend with search and hover', component: SearchableDiscreteColorLegendHoverExample, componentName: 'SearchableDiscreteColorLegendHoverExample' } ]; /* eslint-enable max-len */ const CONTINOUS_COLOR_LEGEND = [ { name: 'Default legend', component: ContinuousColorLegendExample, componentName: 'ContinuousColorLegendExample' } ]; const CONTINOUS_SIZE_LEGEND = [ { name: 'Default legend', component: ContinuousSizeLegendExample, componentName: 'ContinuousSizeLegendExample' } ]; function LegendsExample() { return (

    Legends

    Discrete color legend

    {DISCRETE_LEGENDS.map(mapSection)}

    Continuous color legend

    {CONTINOUS_COLOR_LEGEND.map(mapSection)}

    Continuous size legend

    {CONTINOUS_SIZE_LEGEND.map(mapSection)}
    ); } export default LegendsExample; ================================================ FILE: packages/showcase/showcase-sections/misc-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {showCase} from '../index'; import {SHOWCASE_BASE_URL, REACTVIS_BASE_URL} from '../showcase-links'; const { AnimationExample, LabelSeriesExample, GradientExample, NullDataExample, SyncedCharts, TimeChart, TriangleExample, VoronoiLineChart, ZoomableChartExample, SelectionPlotExample, DragableChartExample, BidirectionDragChart, ClipExample, FlexibleCharts } = showCase; const MISC = [ { name: 'Synced Charts', component: SyncedCharts, componentName: 'SyncedCharts' }, { name: 'Time Chart', component: TimeChart, componentName: 'TimeChart' }, { name: 'Polygon Example', component: TriangleExample, componentName: 'TriangleExample', sourceLink: `${REACTVIS_BASE_URL}/src/plot/series/polygon-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/polygon-series' }, { name: 'Voronoi Line Chart', component: VoronoiLineChart, componentName: 'VoronoiLineChart' }, { name: 'Gradient & Custom Border Example', component: GradientExample, componentName: 'GradientExample', sourceLink: `${SHOWCASE_BASE_URL}/misc/gradient-example.js`, docsLink: 'http://uber.github.io/react-vis/documentation/api-reference/gradients' }, { name: 'Content Area Clipping', component: ClipExample, componentName: 'ClipExample', sourceLink: `${SHOWCASE_BASE_URL}/misc/clip-example.js`, docsLink: 'http://uber.github.io/react-vis/documentation/api-reference/clip' }, { name: 'Animation Example', component: AnimationExample, componentName: 'AnimationExample', docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/label-series' }, { name: 'Label Series Example', component: LabelSeriesExample, componentName: 'LabelSeriesExample', sourceLink: `${SHOWCASE_BASE_URL}/src/plot/series/label-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/label-series' }, { name: 'Null Data Example', component: NullDataExample, componentName: 'NullDataExample' }, { name: 'Zoomable Chart example', component: ZoomableChartExample, componentName: 'ZoomableChartExample' }, { name: 'Selection plot example', component: SelectionPlotExample, componentName: 'SelectionPlotExample' }, { name: 'Dragable Chart Example', component: DragableChartExample, componentName: 'DragableChartExample' }, { name: '2d Dragable Chart', component: BidirectionDragChart, componentName: 'BidirectionDragChart' }, { name: 'Flexible Chart', component: FlexibleCharts, componentName: 'FlexibleCharts' } ]; function MiscShowcase() { return (

    Miscellaneous

    {MISC.map(mapSection)}
    ); } export default MiscShowcase; ================================================ FILE: packages/showcase/showcase-sections/parallel-coordinates-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {showCase} from '../index'; import {REACTVIS_BASE_URL} from '../showcase-links'; const { AnimatedParallelCoordinates, BasicParallelCoordinates, BrushedParallelCoordinates } = showCase; /* eslint-disable max-len */ const PARALLEL_COORDINATES = [ { name: 'Basic Parallel Coordinates', component: BasicParallelCoordinates, componentName: 'BasicParallelCoordinates', sourceLink: `${REACTVIS_BASE_URL}/radar-chart/index.js`, docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/radar-chart' }, { name: 'Animated Parallel Coordinates', component: AnimatedParallelCoordinates, componentName: 'AnimatedParallelCoordinates' }, { name: 'Brushed Parallel Coordinates', component: BrushedParallelCoordinates, componentName: 'BrushedParallelCoordinates' } ]; function ParallelCoordinatesShowcase() { return (

    Parallel Coordinates

    {PARALLEL_COORDINATES.map(mapSection)}
    ); } export default ParallelCoordinatesShowcase; ================================================ FILE: packages/showcase/showcase-sections/plots-showcase.js ================================================ import React from 'react'; import PropTypes from 'prop-types'; import {showCase} from '../index'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {mapSection} from '../showcase-components/showcase-utils'; const { AreaChart, AreaChartElevated, BarChart, BigBaseBarChart, ClusteredStackedVerticalBarChart, ContourSeriesExample, ComplexChart, CustomScales, CustomSVGExample, CustomSVGAllTheMarks, CustomSVGRootLevel, DifferenceChart, EmptyChart, FauxScatterplotChart, GridLinesChart, HeatmapChart, HexHeatmap, HexbinSizeExample, Histogram, LabeledHeatmap, LineChart, LineChartManyColors, LineChartWithStyle, LineChartCanvas, LineSeriesCanvasNearestXYExample, LineMarkChart, MixedStackedChart, StackedVerticalBarChart, LabeledStackedVerticalBarChart, StackedHorizontalBarChart, StackedHistogram, ScatterplotChart, ScatterplotCanvas, WhiskerChart, WidthHeightMarginChart } = showCase; const PLOTS = [ { component: LineChart, componentName: 'LineChart', name: 'Line Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/line-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/line-series' }, { component: LineChartWithStyle, componentName: 'LineChartWithStyle', name: 'Line Series with style', sourceLink: `${REACTVIS_BASE_URL}/plot/series/line-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/line-series' }, { component: LineMarkChart, componentName: 'LineMarkChart', name: 'LineMark Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/line-mark-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/line-mark-series' }, { component: LineChartCanvas, componentName: 'LineChartCanvas', name: 'Line Chart Canvas' }, { component: LineSeriesCanvasNearestXYExample, componentName: 'LineSeriesCanvasNearestXYExample', name: 'Line Series Canvas' }, { component: LineChartManyColors, componentName: 'LineChartManyColors', name: 'Line Series With Many Colors' }, { component: ScatterplotChart, componentName: 'ScatterplotChart', name: 'Mark Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/mark-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/mark-series' }, { component: ScatterplotCanvas, componentName: 'ScatterplotCanvas', name: 'Mark Series Canvas' }, { component: WhiskerChart, componentName: 'WhiskerChart', name: 'Whisker Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/whisker-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/whisker-series' }, { component: AreaChart, componentName: 'AreaChart', name: 'Area Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/area-serie.js` }, { component: AreaChartElevated, componentName: 'AreaChartElevated', name: 'Area Series With vertical offset', sourceLink: 'http://uber.github.io/react-vis/documentation/series-reference/area-series' }, { component: BarChart, componentName: 'BarChart', name: 'Bar Series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/bar-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/bar-series' }, { component: BigBaseBarChart, componentName: 'BigBaseBarChart', name: 'Big Base Bar Series' }, { component: DifferenceChart, componentName: 'DifferenceChart', name: 'Difference Bar Series' }, { name: 'Stacked Horizontal Bar Series', component: StackedHorizontalBarChart, componentName: 'StackedHorizontalBarChart' }, { name: 'Stacked Vertical Bar Series', component: StackedVerticalBarChart, componentName: 'StackedVerticalBarChart' }, { name: 'Labeled Stacked Vertical Bar Series', component: LabeledStackedVerticalBarChart, componentName: 'LabeledStackedVerticalBarChart' }, { name: 'Mixed Stacked Series', component: MixedStackedChart }, { name: 'Clustered Stacked Vertical Bar Series', component: ClusteredStackedVerticalBarChart, componentName: 'ClusteredStackedVerticalBarChart' }, { name: 'Stacked Vertical Rect Series (histogram)', component: StackedHistogram, componentName: 'StackedHistogram' }, { name: 'Horizontal Rect Series', component: Histogram, componentName: 'Histogram' }, { name: 'Heatmap Series', component: HeatmapChart, componentName: 'HeatmapChart', sourceLink: `${REACTVIS_BASE_URL}/plot/series/heatmap-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/heatmap-series' }, { name: 'Hexbin Series', component: HexHeatmap, componentName: 'HexHeatmap', sourceLink: `${REACTVIS_BASE_URL}/plot/series/hexbin-series.js`, docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/hexbin-series' }, { name: 'Hexbin Size', component: HexbinSizeExample, componentName: 'HexbinSizeExample' }, { name: 'Labeled Heatmap', component: LabeledHeatmap, componentName: 'LabeledHeatmap', sourceLink: `${REACTVIS_BASE_URL}/plot/series/heatmap-serie.js` }, { name: 'Contour Series', component: ContourSeriesExample, componentName: 'ContourSeriesExample' }, { name: 'Custom SVG Series', component: CustomSVGExample, componentName: 'CustomSVGExample' }, { name: 'Custom SVG - All The Marks (with tooltips)', component: CustomSVGAllTheMarks, componentName: 'CustomSVGAllTheMarks' }, { name: 'Custom SVG - Root Level Function Definition', component: CustomSVGRootLevel, componentName: 'CustomSVGRootLevel' } ]; const BASIC_COMPONENTS = [ { name: 'Custom Size and Margin', component: WidthHeightMarginChart, componentName: 'WidthHeightMarginChart' }, { name: 'Custom scales', component: CustomScales, componentName: 'CustomScales' }, { name: 'Empty Chart', component: EmptyChart, componentName: 'EmptyChart' }, { name: 'Custom GridLines', component: GridLinesChart, componentName: 'GridLinesChart' }, { name: 'Circular Gridlines', component: FauxScatterplotChart, componentName: 'FauxScatterplotChart', sourceLink: `${REACTVIS_BASE_URL}/plot/circular-grid-lines.js`, docsLink: 'http://uber.github.io/react-vis/documentation/api-reference/grids' } ]; function PlotsShowcase(props) { const {forExample} = props; return (

    Plots

    {!forExample && (
    )}

    Series Types

    {PLOTS.map(mapSection)}

    Basic Components

    {BASIC_COMPONENTS.map(mapSection)}
    ); } PlotsShowcase.propTypes = { forExample: PropTypes.bool }; export default PlotsShowcase; ================================================ FILE: packages/showcase/showcase-sections/radar-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {showCase} from '../index'; const { AnimatedRadarChart, BasicRadarChart, FourQuadrantRadarChart, RadarChartWithTooltips, RadarChartSeriesTooltips } = showCase; const RADAR = [ { name: 'Basic Radar Chart', component: BasicRadarChart, componentName: 'BasicRadarChart', sourceLink: `${REACTVIS_BASE_URL}/radar-chart/index.js`, docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/radar-chart' }, { name: 'Animated Radar Chart', component: AnimatedRadarChart, componentName: 'AnimatedRadarChart' }, { name: 'Four Quadrant Radar Chart', component: FourQuadrantRadarChart, componentName: 'FourQuadrantRadarChart' }, { name: 'Radar Chart with Tooltips', component: RadarChartWithTooltips, componentName: 'RadarChartWithTooltips' }, { name: 'Radar Chart with Series Tooltips', component: RadarChartSeriesTooltips, componentName: 'RadarChartSeriesTooltips' } ]; function RadarShowcase() { return (

    Radar Chart

    {RADAR.map(mapSection)}
    ); } export default RadarShowcase; ================================================ FILE: packages/showcase/showcase-sections/radial-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {showCase} from '../index'; const { CustomRadiusRadialChart, DonutChartExample, SimpleRadialChart, GradientPie } = showCase; /* eslint-disable max-len */ const RADIAL = [ { name: 'Simple Radial Chart', component: SimpleRadialChart, componentName: SimpleRadialChart, sourceLink: `${REACTVIS_BASE_URL}/radial-chart/index.js`, docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/radial-chart' }, { name: 'Simple Donut Chart', component: DonutChartExample, componentName: DonutChartExample }, { name: 'Custom Radius', component: CustomRadiusRadialChart, componentName: CustomRadiusRadialChart }, { name: 'Gradient Pie', component: GradientPie, componentName: GradientPie } ]; function RadialShowcase() { return (

    Radial Chart

    {RADIAL.map(mapSection)}
    ); } export default RadialShowcase; ================================================ FILE: packages/showcase/showcase-sections/sankeys-showcase.js ================================================ import React from 'react'; import {mapSection} from '../showcase-components/showcase-utils'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {showCase} from '../index'; const { BasicSankeyExample, VoronoiSankeyExample, EnergySankeyExample, LinkEventSankeyExample, LinkHintSankeyExample } = showCase; const SANKEYS = [ { name: 'Basic', component: BasicSankeyExample, componentName: 'BasicSankeyExample', docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/sankey-diagram', sourceLink: `${REACTVIS_BASE_URL}/sankey/index.js` }, { name: 'With Voronoi Selection', component: VoronoiSankeyExample, componentName: 'VoronoiSankeyExample' }, { name: 'With link selection', component: LinkEventSankeyExample, componentName: 'LinkEventSankeyExample' }, { name: 'With hint (for links)', component: LinkHintSankeyExample, componentName: 'LinkHintSankeyExample' }, { name: 'Energy Example', component: EnergySankeyExample, componentName: 'EnergySankeyExample' } ]; function SankeysSection() { return (

    Sankeys

    {SANKEYS.map(mapSection)}
    ); } export default SankeysSection; ================================================ FILE: packages/showcase/showcase-sections/sunburst-showcase.js ================================================ import React from 'react'; import {showCase} from '../index'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {mapSection} from '../showcase-components/showcase-utils'; const { AnimatedSunburst, ArcSeriesExample, BasicSunburst, ClockExample, SunburstWithTooltips } = showCase; const SUNBURSTS = [ { name: 'Arc Series Example', component: ArcSeriesExample, componentName: 'ArcSeriesExample', docsLink: 'http://uber.github.io/react-vis/documentation/series-reference/arc-series', sourceLink: `${REACTVIS_BASE_URL}/plot/series/arc-series.js` }, { name: 'Basic Sunburst', component: BasicSunburst, componentName: 'BasicSunburst', docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/sunburst-diagram', sourceLink: `${REACTVIS_BASE_URL}/sunburst/index.js` }, { name: 'Clock', component: ClockExample, componentName: 'ClockExample' }, { name: 'Animated Sunburst', component: AnimatedSunburst, componentName: 'AnimatedSunburst' }, { name: 'Sunburst with tooltips', component: SunburstWithTooltips, componentName: 'SunburstWithTooltips' } ]; function SunburstSection() { return (

    Sunbursts

    {SUNBURSTS.map(mapSection)}
    ); } export default SunburstSection; ================================================ FILE: packages/showcase/showcase-sections/treemap-showcase.js ================================================ import React from 'react'; import {showCase} from '../index'; import {REACTVIS_BASE_URL} from '../showcase-links'; import {mapSection} from '../showcase-components/showcase-utils'; const {SimpleTreemap, TreemapExample} = showCase; const TREEMAPS = [ { name: 'Simple Treemap', component: SimpleTreemap, componentName: 'SimpleTreemap', docsLink: 'http://uber.github.io/react-vis/documentation/other-charts/treemap', sourceLink: `${REACTVIS_BASE_URL}/treemap/index.js` }, { name: 'Animated Treemap', component: TreemapExample, componentName: 'TreemapExample' } ]; function TreemapShowcase() { return (

    Treemap

    {TREEMAPS.map(mapSection)}
    ); } export default TreemapShowcase; ================================================ FILE: packages/showcase/showcase-utils.js ================================================ // sourced from // http://indiegamr.com/generate-repeatable-random-numbers-in-js/ export function generateSeededRandom(baseSeed = 2) { let seed = baseSeed; return function seededRandom(max, min) { max = max || 1; min = min || 0; seed = (seed * 9301 + 49297) % 233280; const rnd = seed / 233280; return min + rnd * (max - min); }; } ================================================ FILE: packages/showcase/sunbursts/animated-sunburst.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {Sunburst} from 'react-vis'; import ShowcaseButton from '../showcase-components/showcase-button'; function randomLeaf() { return { size: Math.random() * 1000, color: Math.random() }; } function updateData() { const totalLeaves = Math.random() * 20; const leaves = []; for (let i = 0; i < totalLeaves; i++) { const leaf = randomLeaf(); if (Math.random() > 0.8) { leaf.children = [...new Array(3)].map(() => randomLeaf()); } leaves.push(leaf); } return { title: '', color: 1, children: leaves }; } const DIVERGING_COLOR_SCALE = ['#00939C', '#85C4C8', '#EC9370', '#C22E00']; export default class AnimatedSunburst extends React.Component { state = { data: updateData(), hovering: false }; render() { const {data, hovering} = this.state; return (
    this.setState({data: updateData()})} buttonContent={'UPDATE'} />
    {hovering ? 'CURRENTLY HOVERING' : 'NOT HOVERED'}
    this.setState({hovering: true})} onValueMouseOut={() => this.setState({hovering: false})} height={300} width={350} />
    ); } } ================================================ FILE: packages/showcase/sunbursts/basic-sunburst.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import Sunburst from 'react-vis/sunburst'; import {EXTENDED_DISCRETE_COLOR_RANGE} from 'react-vis/theme'; import {LabelSeries} from 'react-vis'; import D3FlareData from '../datasets/d3-flare-example.json'; const LABEL_STYLE = { fontSize: '8px', textAnchor: 'middle' }; /** * Recursively work backwards from highlighted node to find path of valud nodes * @param {Object} node - the current node being considered * @returns {Array} an array of strings describing the key route to the current node */ function getKeyPath(node) { if (!node.parent) { return ['root']; } return [(node.data && node.data.name) || node.name].concat( getKeyPath(node.parent) ); } /** * Recursively modify data depending on whether or not each cell has been selected by the hover/highlight * @param {Object} data - the current node being considered * @param {Object|Boolean} keyPath - a map of keys that are in the highlight path * if this is false then all nodes are marked as selected * @returns {Object} Updated tree structure */ function updateData(data, keyPath) { if (data.children) { data.children.map(child => updateData(child, keyPath)); } // add a fill to all the uncolored cells if (!data.hex) { data.style = { fill: EXTENDED_DISCRETE_COLOR_RANGE[5] }; } data.style = { ...data.style, fillOpacity: keyPath && !keyPath[data.name] ? 0.2 : 1 }; return data; } const decoratedData = updateData(D3FlareData, false); export default class BasicSunburst extends React.Component { state = { pathValue: false, data: decoratedData, finalValue: 'SUNBURST', clicked: false }; render() { const {clicked, data, finalValue, pathValue} = this.state; return (
    {clicked ? 'click to unlock selection' : 'click to lock selection'}
    { if (clicked) { return; } const path = getKeyPath(node).reverse(); const pathAsMap = path.reduce((res, row) => { res[row] = true; return res; }, {}); this.setState({ finalValue: path[path.length - 1], pathValue: path.join(' > '), data: updateData(decoratedData, pathAsMap) }); }} onValueMouseOut={() => clicked ? () => {} : this.setState({ pathValue: false, finalValue: false, data: updateData(decoratedData, false) }) } onValueClick={() => this.setState({clicked: !clicked})} style={{ stroke: '#ddd', strokeOpacity: 0.3, strokeWidth: '0.5' }} colorType="literal" getSize={d => d.value} getColor={d => d.hex} data={data} height={300} width={350} > {finalValue && ( )}
    {pathValue}
    ); } } ================================================ FILE: packages/showcase/sunbursts/clock-example.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React, {useState, useEffect} from 'react'; import {XYPlot, ArcSeries} from 'react-vis'; import {EXTENDED_DISCRETE_COLOR_RANGE} from 'react-vis/theme'; const PI = Math.PI; function getSeconds() { return Math.floor(new Date().getTime() / 1000); } export default function ClockExample() { const [time, setTime] = useState(getSeconds()); useEffect(() => { const handle = setInterval(() => setTime(getSeconds()), 100); return () => clearInterval(handle); }, []); const seconds = time % 60; const minutes = (time / 60) % 60; const hours = (time / (60 * 24)) % 24; return ( d.time} getAngle0={() => 0} height={300} > ); } ================================================ FILE: packages/showcase/sunbursts/sunburst-with-tooltips.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import {Hint, Sunburst} from 'react-vis'; import {EXTENDED_DISCRETE_COLOR_RANGE as COLORS} from 'react-vis/theme'; const DATA = { children: [ { children: [ {bigness: 1, children: [], clr: COLORS[1], name: 'excellent'}, {bigness: 1, children: [], clr: COLORS[2], name: 'chart'} ], clr: COLORS[3] }, { bigness: 1, children: [], clr: COLORS[4], name: 'cool', labelStyle: { fontSize: 15, fontWeight: 'bold' } }, {bigness: 1, children: [], clr: COLORS[5], name: 'dogs'}, {bigness: 1, children: [], clr: COLORS[6], name: 'sunglasses'}, { children: [ {bigness: 1, children: [], clr: COLORS[7], name: 'great'}, {bigness: 1, children: [], clr: COLORS[8], name: 'label'} ], clr: COLORS[9] } ] }; const tipStyle = { display: 'flex', color: '#fff', background: '#000', alignItems: 'center', padding: '5px' }; const boxStyle = {height: '10px', width: '10px'}; function buildValue(hoveredCell) { const {radius, angle, angle0} = hoveredCell; const truedAngle = (angle + angle0) / 2; return { x: radius * Math.cos(truedAngle), y: radius * Math.sin(truedAngle) }; } export default class SunburstWithTooltips extends React.Component { state = { hoveredCell: false }; render() { const {hoveredCell} = this.state; return ( this.setState({hoveredCell: v.x && v.y ? v : false}) } onValueMouseOut={() => this.setState({hoveredCell: false})} height={300} margin={{top: 50, bottom: 50, left: 50, right: 50}} getLabel={d => d.name} getSize={d => d.bigness} getColor={d => d.clr} width={350} padAngle={() => 0.02} > {hoveredCell ? (
    {hoveredCell.clr}
    ) : null} ); } } ================================================ FILE: packages/showcase/treemap/dynamic-treemap.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import Treemap from 'react-vis/treemap'; import ShowcaseButton from '../showcase-components/showcase-button'; function _getRandomData(total) { const totalLeaves = total || Math.random() * 20; const leaves = []; for (let i = 0; i < totalLeaves; i++) { leaves.push({ name: total ? total : String(Math.random()).slice(0, 3), size: Math.random() * 1000, color: Math.random(), style: { border: 'thin solid red' } }); } return { title: '', color: 1, children: leaves }; } export default class DynamicTreemapExample extends React.Component { state = { hoveredNode: false, treemapData: _getRandomData(20), useCirclePacking: false }; render() { const {hoveredNode, useCirclePacking} = this.state; const treeProps = { animation: { damping: 9, stiffness: 300 }, data: this.state.treemapData, onLeafMouseOver: x => this.setState({hoveredNode: x}), onLeafMouseOut: () => this.setState({hoveredNode: false}), onLeafClick: () => this.setState({treemapData: _getRandomData()}), height: 300, mode: this.state.useCirclePacking ? 'circlePack' : 'squarify', getLabel: x => x.name, width: 350 }; return (
    this.setState({useCirclePacking: !useCirclePacking})} buttonContent={'TOGGLE CIRCLE PACK'} /> click above to the update data {hoveredNode && hoveredNode.value}
    ); } } ================================================ FILE: packages/showcase/treemap/simple-treemap.js ================================================ // Copyright (c) 2016 - 2017 Uber Technologies, Inc. // // 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. import React from 'react'; import Treemap from 'react-vis/treemap'; import D3FlareData from '../datasets/d3-flare-example.json'; import ShowcaseButton from '../showcase-components/showcase-button'; const MODE = [ 'circlePack', 'partition', 'partition-pivot', 'squarify', 'resquarify', 'slice', 'dice', 'slicedice', 'binary' ]; const STYLES = { SVG: { stroke: '#ddd', strokeWidth: '0.25', strokeOpacity: 0.5 }, DOM: { border: 'thin solid #ddd' } }; export default class SimpleTreemapExample extends React.Component { state = { modeIndex: 0, useSVG: true }; updateModeIndex = increment => () => { const newIndex = this.state.modeIndex + (increment ? 1 : -1); const modeIndex = newIndex < 0 ? MODE.length - 1 : newIndex >= MODE.length ? 0 : newIndex; this.setState({modeIndex}); }; render() { const {modeIndex, useSVG} = this.state; return (
    this.setState({useSVG: !useSVG})} buttonContent={useSVG ? 'USE DOM' : 'USE SVG'} />
    {MODE[modeIndex]}
    d.value, getColor: d => d.hex, style: STYLES[useSVG ? 'SVG' : 'DOM'] }} />
    ); } } ================================================ FILE: packages/showcase/webpack.config.js ================================================ /* eslint-env node */ const process = require('process'); const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const entry = {app: './app'}; const jsRule = { test: /\.js$/, loader: 'babel-loader', exclude: [/node_modules/], options: { rootMode: 'upward' } }; const isProd = process.env.NODE_ENV === 'production'; const config = isProd ? { entry, mode: 'production', output: { path: process.cwd(), filename: 'bundle.js' }, resolve: { modules: [ 'node_modules', path.join(__dirname, '..', 'react-vis', 'src') ] }, module: { rules: [ jsRule, { test: /\.scss$/, use: [ { loader: MiniCssExtractPlugin.loader }, 'css-loader', 'sass-loader' ] } ] }, optimization: { minimize: true }, plugins: [new MiniCssExtractPlugin('bundle.css')] } : { mode: 'development', entry, devtool: 'source-maps', output: { path: path.resolve(__dirname, './dist'), filename: 'bundle.js' }, plugins: [new MiniCssExtractPlugin()], module: { rules: [ jsRule, { test: /\.(sa|sc|c)ss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { hmr: true } }, 'css-loader', 'sass-loader' ] } ] }, resolve: { modules: [ 'node_modules', path.join(__dirname, '..', 'react-vis', 'src') ] } }; module.exports = config; ================================================ FILE: packages/website/.storybook/addons.js ================================================ import '@storybook/addon-knobs/register'; import 'storybook-addon-jsx/register'; ================================================ FILE: packages/website/.storybook/config.js ================================================ import {configure, addParameters, setAddon} from '@storybook/react'; import JSXAddon, {jsxDecorator} from 'storybook-addon-jsx'; import {SimpleChartWrapper, jsxOptions} from '../storybook/storybook-utils'; setAddon(JSXAddon); addParameters({jsx: jsxOptions}); function loadStories() { require('../storybook/index.js'); // You can require as many stories as you need. } configure(loadStories, module); ================================================ FILE: packages/website/.storybook/storybook.css ================================================ html, body, #root { height: 100%; } ================================================ FILE: packages/website/html.config.js ================================================ /* eslint-env node */ module.exports = { title: 'react-vis', baseHref: process.env.NODE_ENV === 'production' ? 'website/dist/' : '/', meta: [ { name: 'description', content: 'A composable charting library' } ], scripts: ['https://uber.github.io/react-vis/redirect.js'] }; ================================================ FILE: packages/website/package.json ================================================ { "name": "@uber/react-vis-website", "private": true, "version": "1.0.0", "description": "A composable charting library", "main": "index.js", "scripts": { "start": "ocular start", "clean": "rm -rf dist/*{.js,.css,index.html,appcache,fonts,images}", "build": "npm run clean && ocular build && npm run build-storybook", "build-storybook": "build-storybook -c .storybook -o ./dist/storybook", "lint": "ocular lint", "storybook": "start-storybook -p 9001 -c .storybook" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "@storybook/addon-knobs": "^5.3.18", "@storybook/react": "^5.3.18", "storybook-addon-jsx": "^7.2.3" }, "dependencies": { "react": "^16.8.3", "react-dom": "^16.8.3", "d3-force": "^1.0.6", "d3-random": "^1.1.0", "ocular": "0.6.2", "react-virtualized": "^9.18.5", "react-vis": "^1.11.2" }, "volta": { "node": "10.20.1", "yarn": "1.22.4" } } ================================================ FILE: packages/website/src/components/Hero.js ================================================ import React, {Component} from 'react'; import {PROJECT_NAME, PROJECT_DESC} from 'config'; import { XYPlot, AreaSeries, LineSeries, VerticalBarSeries, MarkSeries, Treemap, RadialChart } from 'react-vis'; const palette = ['#12939A', '#17B8BE', '#1E96BE', '#FF991F', 'transparent']; const MiniChart = props => { const XYProps = { colorType: 'literal', height: 120, margin: {left: 12, right: 12, top: 12, bottom: 12}, onMouseLeave: () => { props.highlight(null); props.scrub(null); }, xDomain: [0, props.data[0].length - 1], yDomain: [0, 20], width: 120 }; switch (props.type) { case 0: { // bar charts const barData = props.data.map((barseries, s) => barseries.map((d, i) => ({ ...d, color: i === props.x ? palette[3] : palette[s] })) ); return ( {barData.map((d, i) => ( { props.scrub(value.x); props.highlight(value.s); }} /> ))} ); } case 1: { // pie charts const pieData = props.data .reduce((prev, curr) => [...prev, ...curr], []) .reduce( (result, d, i) => { result[i % 3].angle += d.y; return result; }, [ {angle: 0, s: 0, color: props.s === 0 ? palette[3] : palette[0]}, {angle: 0, s: 1, color: props.s === 1 ? palette[3] : palette[1]}, {angle: 0, s: 2, color: props.s === 2 ? palette[3] : palette[2]} ] ); return ( props.highlight(value.s)} onSeriesMouseOut={() => props.highlight(null)} /> ); } case 2: // area charts return ( {props.data.map((d, i) => ( props.highlight(i)} /> ))} ); case 3: { // scatterplots const scatterData = props.data.map((scatterseries, s) => scatterseries.map(d => ({ x: d.xS, s, y: d.yS, size: d.size, color: s === props.s ? palette[3] : palette[s] })) ); return ( {scatterData.map((d, i) => ( props.highlight(i)} opacity={props.s === null || props.s === i ? 0.8 : 0.5} /> ))} ); } case 4: { // treemaps const treeMapData = props.data.reduce( (prev, treeseries, s) => { prev.children.push( treeseries.reduce( (leaf, d) => { leaf.children.push({ size: d.yS, index: d.x, series: s, style: {background: props.x === d.x ? palette[3] : palette[s]} }); return leaf; }, { title: '', style: {background: 'white'}, children: [] } ) ); return prev; }, { title: '', children: [] } ); return ( { props.highlight(node.data.series); props.scrub(node.data.index); }} style={{margin: '6px -6px -6px 6px'}} /> ); } default: // Line charts return ( {props.data.map((d, i) => ( { props.scrub(value.x); }} /> ))} {props.data.map((d, i) => ( props.highlight(i)} /> ))} {props.x !== null && props.x < props.data[0].length ? ( ) : null} {props.x !== null && props.x < props.data[0].length ? ( ({ x: props.x, y: markseries[props.x].y, fill: s === props.s ? palette[3] : palette[s] }))} size={5} stroke="white" /> ) : null} ); } }; function makeData(nbSeries, nbPoints) { return [...Array(nbSeries).keys()].map((d, s) => series(nbPoints, s)); } function series(nbPoints, s) { let previousPoint = random({scope: 3, rolls: 4}); return [...Array(nbPoints).keys()].map(d => { const y = previousPoint > 17.5 ? previousPoint - random({scope: 1, rolls: 3}) : previousPoint < 2.5 ? previousPoint + random({scope: 1, rolls: 3}) : previousPoint + random({scope: 1, rolls: 5}) - 2.5; previousPoint = y; return { x: d, s, size: random({scope: 3, rolls: 3}), xS: random({scope: 20, rolls: 1}), yS: random({scope: 20, rolls: 1}), y }; }); } function random({scope = 5, rolls = 1, integer = false}) { let result = 0; let i = 0; for (i; i < rolls; i++) { result += Math.random() * scope; } return integer ? Math.floor(result) : result; } class Hero extends Component { constructor() { super(); this.state = { x: null }; this._resize = this._resize.bind(this); } componentDidMount() { const chartsProps = this.generateData(100); window.addEventListener('resize', this._resize); this._resize(); this.setState({chartsProps}); } componentWillUnmount() { window.removeEventListener('resize', this._resize); } _resize = () => { this.setState({ height: window.innerHeight, width: window.innerWidth }); }; changeType = i => { const updatedChartsProps = [...this.state.chartsProps]; updatedChartsProps[i].type = (updatedChartsProps[i].type + 1) % 6; updatedChartsProps[i].changes = updatedChartsProps[i].changes + 1; this.setState({chartsProps: updatedChartsProps}); }; generateData = nb => { return [...Array(nb).keys()].map(() => { const nbPoints = 5 + random({scope: 5, rolls: 2, integer: true}); const nbSeries = 1 + Number(Math.random() > 0.2) + Number(Math.random() > 0.5); const data = makeData(nbSeries, nbPoints); const type = random({scope: 6, integer: true}); return { changes: 0, type, data }; }); }; highlight = s => this.setState({s}); scrub = x => this.setState({x}); render() { const nbChartsInWidth = Math.floor((this.state.width - 24) / 132); const nbChartsInHeight = Math.floor( (0.65 * (this.state.height - 24)) / 132 ); const chartsToRender = nbChartsInWidth * nbChartsInHeight; const chartsWidth = nbChartsInWidth * 132; const chartsHeight = nbChartsInHeight * 132; return (
    {this.state.chartsProps ? this.state.chartsProps.slice(0, chartsToRender).map((d, i) => (
    this.changeType(i)} style={{ cursor: 'pointer', display: 'inline-block', background: 'white', margin: 6, boxShadow: '1px 1px 2px rgba(0,0,0,0.1)', transition: 'transform 0.5s', transform: `perspective(1000px) rotateY(${d.changes}turn)` }} >
    )) : null}

    {PROJECT_NAME}

    {PROJECT_DESC}

    Get started

    ); } } export default Hero; ================================================ FILE: packages/website/src/config.js ================================================ export const PROJECT_TYPE = 'github'; export const PROJECT_NAME = 'react-vis'; export const PROJECT_ORG = 'uber'; export const PROJECT_URL = `https://github.com/${PROJECT_ORG}/${PROJECT_NAME}`; export const PROJECT_DESC = 'A composable charting library'; export const PROJECTS = {}; export const HOME_HEADING = 'A composable charting library'; export const HOME_RIGHT = null; export const HOME_BULLETS = []; export const GA_TRACKING = 'UA-64694404-13'; export const ADDITIONAL_LINKS = [ {name: 'Storybook', href: './storybook/index.html'} ]; export const BASENAME = '/react-vis'; export const HISTORY = 'browser'; ================================================ FILE: packages/website/src/demos.js ================================================ import {showCase} from '../../showcase'; import * as ShowcaseIndex from '../../showcase/showcase-index'; export default { ...showCase, ...ShowcaseIndex }; ================================================ FILE: packages/website/src/mdRoutes.js ================================================ import codepen from '../../docs/getting-started/react-vis-in-codepen.md'; import install from '../../docs/getting-started/installing-react-vis.md'; import newProject from '../../docs/getting-started/new-react-vis-project.md'; import first from '../../docs/getting-started/your-first-chart.md'; import otherThings from '../../docs/examples/building-things-other-than-charts.md'; import extensibility from '../../docs/examples/extensibility.md'; import responsiveVis from '../../docs/examples/responsive-vis.md'; import irisDashboard from '../../docs/examples/iris-dashboard.md'; import streamGraph from '../../docs/examples/stream-graph.md'; import axesShowcase from '../../docs/examples/showcases/axes-showcase.md'; import legendsShowcase from '../../docs/examples/showcases/legends-showcase.md'; import miscShowcase from '../../docs/examples/showcases/misc-showcase.md'; import plotsShowcase from '../../docs/examples/showcases/plots-showcase.md'; import radarShowcase from '../../docs/examples/showcases/radar-chart-showcase.md'; import radialShowcase from '../../docs/examples/showcases/radial-showcase.md'; import sankeysShowcase from '../../docs/examples/showcases/sankeys-showcase.md'; import sunburstShowcase from '../../docs/examples/showcases/sunburst-showcase.md'; import treemapsShowcase from '../../docs/examples/showcases/treemaps-showcase.md'; import animation from '../../docs/animation.md'; import arcSeries from '../../docs/arc-series.md'; import areaSeries from '../../docs/area-series.md'; import axes from '../../docs/axes.md'; import barSeries from '../../docs/bar-series.md'; import borders from '../../docs/borders.md'; import chartLabel from '../../docs/chart-label.md'; import colors from '../../docs/colors.md'; import contourSeries from '../../docs/contour-series.md'; import crosshair from '../../docs/crosshair.md'; import customSvgSeries from '../../docs/custom-svg-series.md'; import decorativeAxis from '../../docs/decorative-axis.md'; import flexiblePlots from '../../docs/flexible-plots.md'; import gradients from '../../docs/gradients.md'; import grids from '../../docs/grids.md'; import heatmapSeries from '../../docs/heatmap-series.md'; import hexbinSeries from '../../docs/hexbin-series.md'; import highlight from '../../docs/highlight.md'; import hint from '../../docs/hint.md'; import interaction from '../../docs/interaction.md'; import labelSeries from '../../docs/label-series.md'; import legends from '../../docs/legends.md'; import lineMarkSeries from '../../docs/line-mark-series.md'; import lineSeries from '../../docs/line-series.md'; import markSeries from '../../docs/mark-series.md'; import parallel from '../../docs/parallel-coordinates.md'; import polygonSeries from '../../docs/polygon-series.md'; import presentation from '../../docs/presentation.md'; import radar from '../../docs/radar-chart.md'; import radial from '../../docs/radial-chart.md'; import rectSeries from '../../docs/rect-series.md'; import sankey from '../../docs/sankey.md'; import scalesAndData from '../../docs/scales-and-data.md'; import series from '../../docs/series.md'; import style from '../../docs/style.md'; import sunburst from '../../docs/sunburst.md'; import treemap from '../../docs/treemap.md'; import voronoi from '../../docs/voronoi.md'; import whiskerSeries from '../../docs/whisker-series.md'; import xy from '../../docs/xy-plot.md'; import clip from '../../docs/clip.md'; const mdRoutes = [ { name: 'Examples', path: '/examples', data: [ { name: 'Showcases', children: [ { name: 'Plots', markdown: plotsShowcase }, { name: 'Axes', markdown: axesShowcase }, { name: 'Legends', markdown: legendsShowcase }, { name: 'Sunbursts', markdown: sunburstShowcase }, { name: 'Radial', markdown: radialShowcase }, { name: 'Sankeys', markdown: sankeysShowcase }, { name: 'Treemaps', markdown: treemapsShowcase }, { name: 'Radar Charts', markdown: radarShowcase }, { name: 'Misc', markdown: miscShowcase } ] }, { name: 'Charts', children: [ { name: 'Candlestick', markdown: extensibility }, { name: 'Force Directed Graph', markdown: otherThings }, { name: 'Streamgraph', markdown: streamGraph }, { name: 'Dynamic Dashboard', markdown: irisDashboard }, { name: 'Responsive Vis', markdown: responsiveVis } ] } ] }, { name: 'Documentation', path: '/documentation', data: [ { name: 'Welcome to React-vis', markdown: presentation }, { name: 'Getting Started', children: [ { name: 'React-vis in codepen', markdown: codepen }, { name: 'Installing react-vis', markdown: install }, { name: 'Creating a new react-vis project', markdown: newProject }, { name: 'Your first chart', markdown: first } ] }, { name: 'General principles', children: [ { name: 'Scales and data', markdown: scalesAndData }, { name: 'Colors', markdown: colors }, { name: 'Interaction', markdown: interaction }, { name: 'Animation', markdown: animation }, { name: 'Style', markdown: style } ] }, { name: 'API Reference', children: [ { name: 'XY-Plot', markdown: xy }, { name: 'Series', markdown: series }, { name: 'Brushing and Dragging', markdown: highlight }, { name: 'Legends', markdown: legends }, { name: 'Crosshair', markdown: crosshair }, { name: 'Grids', markdown: grids }, { name: 'Hint', markdown: hint }, { name: 'Axes', markdown: axes }, { name: 'ChartLabel', markdown: chartLabel }, { name: 'DecorativeAxis', markdown: decorativeAxis }, { name: 'Gradients', markdown: gradients }, { name: 'Flexible plots', markdown: flexiblePlots }, {name: 'Clipping', markdown: clip}, { name: 'Borders', markdown: borders }, { name: 'Voronoi', markdown: voronoi } ] }, { name: 'Series reference', children: [ { name: 'Arc Series', markdown: arcSeries }, { name: 'Area Series', markdown: areaSeries }, { name: 'Bar Series', markdown: barSeries }, { name: 'Contour Series', markdown: contourSeries }, { name: 'Custom SVG Series', markdown: customSvgSeries }, { name: 'Heatmap Series', markdown: heatmapSeries }, { name: 'Hexbin Series', markdown: hexbinSeries }, { name: 'Label Series', markdown: labelSeries }, { name: 'Line Series', markdown: lineSeries }, { name: 'Line-Mark Series', markdown: lineMarkSeries }, { name: 'Mark Series', markdown: markSeries }, { name: 'Polygon Series', markdown: polygonSeries }, { name: 'Rect Series', markdown: rectSeries }, { name: 'Whisker Series', markdown: whiskerSeries } ] }, { name: 'Other Charts', children: [ { name: 'Parallel Coordinates', markdown: parallel }, { name: 'Radar Chart', markdown: radar }, { name: 'Radial Chart', markdown: radial }, { name: 'Sankey Diagram', markdown: sankey }, { name: 'Sunburst Diagram', markdown: sunburst }, { name: 'Treemap', markdown: treemap } ] } ] } ]; // Ocular searches for links with at least one '/' and replaces them // with the first route it finds that includes the same markdown name // (that is error prone, and that's why current Axes links will all // point to Example Axes page instead of Documentation Axes page) const fixMarkdownLinks = markdown => { const markdownLinksRegex = /(?:\(([^()/\n]+\.md)\))/g; return markdown.replace(markdownLinksRegex, '(/$1)'); }; mdRoutes.forEach(section => { section.data.forEach(subsection => { if (subsection.markdown) { subsection.markdown = fixMarkdownLinks(subsection.markdown); } else { subsection.children.forEach(child => { child.markdown = fixMarkdownLinks(child.markdown); }); } }); }); export default mdRoutes; ================================================ FILE: packages/website/src/styles/_variables.scss ================================================ $primary: #00ADE6; $secondary: #05E3D5; $black: #041725; $black-20: #213746; $black-40: #5A666D; $white: #EDEDED; $white-40: #8D9BA3; $font-family: 'Source Sans Pro'; $footer-height: 13rem; $topbar-height: 4rem; $topbar-maxheight: 8rem; $toc-width: 17rem; $mobile: 576px; $gradient-1: linear-gradient(to right, $primary 0%, $secondary 100%); $gradient-2: linear-gradient(to bottom, $primary 0%, $secondary 100%); ================================================ FILE: packages/website/src/styles/index.scss ================================================ .Hero .container { position: absolute; background: #fff; box-shadow: 1px 1px 2px rgba(0,0,0,0.1); cursor: pointer; height: 120px; overflow: hidden; transition: background 0.5s; padding: 12px; width: 384px; h1 { margin-top: 0; line-height: 84px; letter-spacing: normal; color: #494949; margin-left: 24px; transition: color 0.5s; } #get-started { left: 400px; opacity: 0; color: white; animation: pulse 2s infinite; } #project-desc { opacity: 1; left: 36px; } p { transition: opacity 0.5s, left 0.5s; font-size: 1rem; line-height: 30px; position: absolute; bottom: 12px; } &:hover { background: #17b8be; h1 { color: white; } #project-desc { opacity: 0; left: -100px; } #get-started { opacity: 1; left: 36px; } } } .container.f.fw { display: none; } .container { hr.short { display: none; } &.markdown-body { h2 + h4, h2 + h5 { margin-top: 0; } h4, h5 { margin-top: 2em; font-size: 1.1em; line-height: 1.5em; font-weight: 600; text-decoration: underline; & ~ ul code, & ~ p code { background: rgba(#00ADE6, 0.1); } & ~ p, & ~ pre, & ~ ul { margin-left: 2em; font-size: 0.9em; line-height: 1.5em; @media (max-width: 400px) { margin-left: 1em; } & + h2 { margin-top: 2em; } } } } .Contributors.m-top { margin-top: 0; .Contributor { width: 8rem; height: 10rem; background: none; margin: 10px; img { border-radius: 50%; border: 4px solid #17b8be; box-shadow: 0 0 0 #17b8be; transition: border 0.5s, box-shadow 0.5s; } span { display: block; font-size: 1rem; position: absolute; margin-left: -8px; bottom: 0; color: #494949; text-align: center; text-shadow: none; width: 8rem; } } .Contributor:after { content: ''; position: absolute; left: 3px; top: 3px; width: calc(8rem - 10px); height: calc(8rem - 10px); border: 2px solid white; border-radius: 50%; } .Contributor:hover { img { border-radius: 50%; border: 4px solid white; box-shadow: 0 0 20px #17b8be; } } } } .inline-code.container > div { align-items: center; display: flex; height: 100%; justify-content: center; } @keyframes pulse { 0% { font-size: 16px; color: #ddd; } 70% { font-size: 17px; color: white; } 100% { font-size: 16px; color: #ddd; } } ================================================ FILE: packages/website/static/.gitkeep ================================================ ================================================ FILE: packages/website/storybook/areaseries-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import { withKnobs, color, number, object, select } from '@storybook/addon-knobs/react'; import {AreaSeries, LineSeries} from 'react-vis'; import {generateLinearData, nonUniformX} from './storybook-data.js'; import {SimpleChartWrapper} from './storybook-utils.js'; function styledAreaSeries(props) { return ( ); } storiesOf('Series/AreaSeries/Base', module) .addDecorator(withKnobs) .addWithJSX('Single Area chart', () => { return ( ); }) .addWithJSX('Single Area chart paired with LineSeries', () => { return ( ); }) .addWithJSX('With negative numbers', () => { return ( ); }) .addWithJSX('With non-uniform x numbers', () => { return ( ); }) .addWithJSX('Multiple Area series', () => { return ( ); }) .addWithJSX('Multiple stacked Area series', () => { return ( ); }); storiesOf('Series/AreaSeries/Styling', module) .addDecorator(withKnobs) .addWithJSX('opacity', () => { return ( {styledAreaSeries({ data: generateLinearData({key: 'area1'}), opacity: 0.75 })} ); }) .addWithJSX('stroke', () => { return ( {styledAreaSeries({ data: generateLinearData({key: 'area1'}), stroke: '#2c51be' })} ); }) .addWithJSX('style object', () => { return ( {styledAreaSeries({ data: generateLinearData({key: 'area1'}), style: { stroke: '#E48000', strokeAreajoin: 'round', strokeWidth: '3px' } })} ); }); storiesOf('Series/AreaSeries/Curve', module) .addDecorator(withKnobs) .addWithJSX('Curve', () => { return ( ); }); ================================================ FILE: packages/website/storybook/axis-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import {withKnobs, number, select} from '@storybook/addon-knobs/react'; import {LineSeries, VerticalBarSeries, XAxis, YAxis} from 'react-vis'; import {generateLinearData, getTime, getWord} from './storybook-data.js'; import {SimpleChartWrapperNoAxes} from './storybook-utils'; storiesOf('Axes and scales/Axis Formatting/Base', module) .addDecorator(withKnobs) .addWithJSX('Axis orientation', () => { const XAxisOrientation = select( 'XAxis.orientation', {bottom: 'bottom', top: 'top'}, 'bottom', 'XAxis' ); const YAxisOrientation = select( 'YAxis.orientation', {left: 'left', right: 'right'}, 'left', 'YAxis' ); return ( ); }) .addWithJSX('Axis titles', () => { const XAxisPosition = select( 'XAxis.position', {start: 'start', middle: 'middle', end: 'end'}, 'end', 'XAxis' ); const YAxisPosition = select( 'YAxis.position', {start: 'start', middle: 'middle', end: 'end'}, 'end', 'YAxis' ); return ( ); }) .addWithJSX('Tick total', () => { const xTickTotal = number( 'XAxis.tickTotal', 10, {max: 20, min: 0, range: true}, 'XAxis' ); const yTickTotal = number( 'YAxis.tickTotal', 10, {max: 20, min: 0, range: true}, 'YAxis' ); return ( ); }) .addWithJSX('Tick Size', () => { const xTickSize = number( 'XAxis.tickSize', 6, {max: 10, min: 0, range: true}, 'XAxis' ); const yTickSize = number( 'YAxis.tickSize', 6, {max: 10, min: 0, range: true}, 'YAxis' ); return ( ); }) .addWithJSX('Tick Size (Inner)', () => { const xTickSize = number( 'XAxis.tickSizeInner', 6, {max: 10, min: 0, range: true}, 'XAxis' ); const yTickSize = number( 'YAxis.tickSizeInner', 6, {max: 10, min: 0, range: true}, 'YAxis' ); return ( ); }) .addWithJSX('Tick Size (Outer)', () => { const xTickSize = number( 'XAxis.tickSizeOuter', 6, {max: 10, min: 0, range: true}, 'XAxis' ); const yTickSize = number( 'YAxis.tickSizeOuter', 6, {max: 10, min: 0, range: true}, 'YAxis' ); return ( ); }) .addWithJSX('Tick orientation', () => { const tickLabelAngle = number( 'tickLabelAngle', 0, {max: 90, min: -90, range: true}, 'XAxis' ); return ( new Date(d).toLocaleDateString()} tickLabelAngle={tickLabelAngle} /> ); }); storiesOf('Axes and scales/Scales', module) .addDecorator(withKnobs) .addWithJSX('time Scale', () => { return ( new Date(d).toLocaleDateString()} /> ); }) .addWithJSX('category scale', () => { const data = generateLinearData({ nbPoints: 8, changeRatio: 0.4, key: 'bar1' }); return ( ); }) .addWithJSX('ordinal scale', () => { const data = generateLinearData({ nbPoints: 8, changeRatio: 0.4, key: 'bar-with-words', extraParams: [['x', getWord({})]] }); return ( ); }); ================================================ FILE: packages/website/storybook/barseries-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import { withKnobs, color, number, object, text } from '@storybook/addon-knobs/react'; import {HorizontalBarSeries, VerticalBarSeries} from 'react-vis'; import {generateLinearData, intRandom, random} from './storybook-data.js'; import {chooseColorScale, SimpleChartWrapper} from './storybook-utils.js'; function addBarSeriesStory(isVertical = true) { const seriesName = isVertical ? 'VerticalBarSeries' : 'HorizontalBarSeries'; const Series = isVertical ? VerticalBarSeries : HorizontalBarSeries; function styledSeries(props) { return ( ); } const xyPlotParams = isVertical ? {} : {xDomain: [0, 20], yDomain: [0, 8]}; function dataGenerator(params) { return generateLinearData({...params, flipXY: !isVertical}); } storiesOf(`Series/${seriesName}/Base`, module) .addDecorator(withKnobs) .addWithJSX(`single ${seriesName}`, () => { return ( ); }) .addWithJSX(`multiple ${seriesName} - clustered`, () => { return ( ); }) .addWithJSX(`multiple ${seriesName} - stacked`, () => { return ( ); }); storiesOf(`Series/${seriesName}/Styling/By datapoint`, module) .addDecorator(withKnobs) .addWithJSX('color', () => { const {colorScale, colorRange} = chooseColorScale(); return ( ); }) .addWithJSX('opacity', () => { return ( ); }); storiesOf(`Series/${seriesName}/Styling/At series level`, module) .addDecorator(withKnobs) .addWithJSX('fill', () => { return ( {styledSeries({ data: dataGenerator({nbPoints: 8, changeRatio: 0.4, key: 'bar1'}), fill: '#2c51be' })} ); }) .addWithJSX('opacity', () => { return ( {styledSeries({ data: dataGenerator({nbPoints: 8, changeRatio: 0.4, key: 'bar1'}), opacity: 0.5 })} ); }) .addWithJSX('stroke', () => { return ( {styledSeries({ data: dataGenerator({nbPoints: 8, changeRatio: 0.4, key: 'bar1'}), stroke: '#2c51be' })} ); }) .addWithJSX('style', () => { return ( {styledSeries({ data: dataGenerator({nbPoints: 8, changeRatio: 0.4, key: 'bar1'}), style: { stroke: '#2c51be', strokeWidth: '3px' } })} ); }); } addBarSeriesStory(); addBarSeriesStory(false); ================================================ FILE: packages/website/storybook/index.js ================================================ import '../../react-vis/dist/style.css'; import '../.storybook/storybook.css'; import './areaseries-story'; import './barseries-story'; import './lineseries-story'; import './markseries-story'; import './radial-story'; import './axis-story'; import './legend-story'; import './misc-story'; ================================================ FILE: packages/website/storybook/legend-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import { withKnobs, boolean, color, number, select, text } from '@storybook/addon-knobs/react'; import { ContinuousColorLegend, ContinuousSizeLegend, DiscreteColorLegend, SearchableDiscreteColorLegend } from 'react-vis'; import {CATEGORY_PALETTE, LINEAR_PALETTE} from './storybook-utils.js'; storiesOf('Legends', module) .addDecorator(withKnobs) .addWithJSX('Discrete color legend', () => ( )) .addWithJSX('Searchable discrete color legend', () => ( )) .addWithJSX('Continuous Color Legend', () => { const width = number('width', 300, { max: 500, min: 100, range: true, step: 1 }); const startTitle = number('startTitle', 0, { max: 1000, min: -1000, range: true, step: 1 }); const endTitle = number('endTitle', 100, { max: 1000, min: -1000, range: true, step: 1 }); const midTitle = number('midTitle', 50, { max: 1000, min: -1000, range: true, step: 1 }); const startColor = color('startColor', LINEAR_PALETTE[0]); const midColor = color('midColor', '#DA854F'); const endColor = color('endColor', LINEAR_PALETTE[1]); const useWidth = boolean('use Width?', true); const useMidColor = boolean('use midColor?', false); return ( ); }) .addWithJSX('Continuous Size Legend', () => { const width = number('width', 300, { max: 500, min: 100, range: true, step: 1 }); const useWidth = boolean('use Width?', true); const startTitle = number('startTitle', 0, { max: 1000, min: -1000, range: true, step: 1 }); const endTitle = number('endTitle', 100, { max: 1000, min: -1000, range: true, step: 1 }); const startSize = number('startSize', 2, { max: 100, min: 1, range: true, step: 1 }); const endSize = number('endSize', 20, { max: 100, min: 1, range: true, step: 1 }); const circlesTotal = number('circlesTotal', 10, { max: 20, min: 1, range: true, step: 1 }); return ( ); }); ================================================ FILE: packages/website/storybook/lineseries-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import { withKnobs, color, number, object, select, text } from '@storybook/addon-knobs/react'; import {LineSeries} from 'react-vis'; import {generateLinearData, nonUniformX} from './storybook-data.js'; import {SimpleChartWrapper} from './storybook-utils.js'; function styledLineSeries(props) { return ( ); } storiesOf('Series/LineSeries/Base', module) .addDecorator(withKnobs) .addWithJSX('Single Line chart', () => { return ( ); }) .addWithJSX('With negative numbers', () => { return ( ); }) .addWithJSX('With non-uniform x numbers', () => { return ( ); }) .addWithJSX('Multiple Line series', () => { return ( ); }); storiesOf('Series/LineSeries/Styling', module) .addDecorator(withKnobs) .addWithJSX('opacity', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), opacity: 0.5 })} ); }) .addWithJSX('stroke', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), stroke: '#2c51be' })} ); }) .addWithJSX('strokeDasharray', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), strokeDasharray: '5, 5, 1, 5' })} ); }) .addWithJSX('strokeStyle', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), strokeStyle: 'dashed' })} ); }) .addWithJSX('strokeWidth', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), strokeWidth: '5px' })} ); }) .addWithJSX('style object', () => { return ( {styledLineSeries({ data: generateLinearData({key: 'line1'}), style: { stroke: '#E48000', strokeLinejoin: 'round', strokeWidth: '3px' } })} ); }); storiesOf('Series/LineSeries/Curve', module) .addDecorator(withKnobs) .addWithJSX('Curve', () => { return ( ); }); ================================================ FILE: packages/website/storybook/markseries-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import { withKnobs, color, number, object, select } from '@storybook/addon-knobs/react'; import {MarkSeries} from 'react-vis'; import {generateScatterplotData, intRandom, random} from './storybook-data.js'; import {chooseColorScale, SimpleChartWrapper} from './storybook-utils.js'; function styledMarkSeries(props) { return ( ); } storiesOf('Series/MarkSeries/Base', module) .addDecorator(withKnobs) .addWithJSX('Single scatterplot', () => { return ( ); }) .addWithJSX('Multiple MarkSeries', () => { return ( ); }); storiesOf('Series/MarkSeries/Styling/By Datapoint', module) .addDecorator(withKnobs) .addWithJSX('color', () => { const {colorScale, colorRange} = chooseColorScale(); return ( ); }) .addWithJSX('fill', () => { const {colorScale, colorRange} = chooseColorScale(); return ( ); }) .addWithJSX('opacity', () => { return ( ); }) .addWithJSX('size', () => { return ( ); }) .addWithJSX('stroke', () => { const {colorScale, colorRange} = chooseColorScale(); return ( ); }); storiesOf('Series/MarkSeries/Styling/At series level', module) .addDecorator(withKnobs) .addWithJSX('fill', () => { return ( {styledMarkSeries({ data: generateScatterplotData({key: 'scatter1'}), fill: '#2c51be' })} ); }) .addWithJSX('opacity', () => { return ( {styledMarkSeries({ data: generateScatterplotData({key: 'scatter1'}), opacity: 0.5 })} ); }) .addWithJSX('stroke', () => { return ( {styledMarkSeries({ data: generateScatterplotData({key: 'scatter1'}), stroke: '#2c51be' })} ); }) .addWithJSX('strokeWidth', () => { return ( {styledMarkSeries({ data: generateScatterplotData({key: 'scatter1'}), strokeWidth: '3px' })} ); }) .addWithJSX('style', () => { return ( {styledMarkSeries({ data: generateScatterplotData({key: 'scatter1'}), style: { stroke: '#E48000', strokeOpacity: 0.5, strokeWidth: '3px' } })} ); }); ================================================ FILE: packages/website/storybook/misc-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import {withKnobs, boolean} from '@storybook/addon-knobs/react'; import {SimpleChartWrapper} from './storybook-utils'; import {generateLinearData} from './storybook-data'; import {LineSeries, ContentClipPath} from 'react-vis'; const data = generateLinearData({randomFactor: 10}); storiesOf('Misc', module) .addDecorator(withKnobs) .addWithJSX('Clip Content', () => { const margin = {left: 40, top: 40, bottom: 40, right: 40}; const xDomain = [data[1].x, data[data.length - 2].x]; const shouldClip = boolean( 'Enable Clipping', false, 'General chart options' ); return ( {shouldClip && } ); }); ================================================ FILE: packages/website/storybook/radial-story.js ================================================ /* eslint-env node */ import React from 'react'; import {storiesOf} from '@storybook/react'; import {withKnobs, boolean, number, object} from '@storybook/addon-knobs/react'; import {generateRadialData} from './storybook-data.js'; import {SimpleRadialChartWrapper} from './storybook-utils.js'; function labelProps() { const showLabels = boolean('showLabels', true, 'Labels'); const labelsRadiusMultiplier = number( 'labelsRadiusMultiplier', 1.1, {max: 2, min: 0, range: true, step: 0.1}, 'Labels' ); const labelsStyle = object('labelStyle', {fontSize: 12}, 'Labels'); return {labelsRadiusMultiplier, labelsStyle, showLabels}; } storiesOf('Series/RadialCharts/Pie Chart', module) .addDecorator(withKnobs) .addWithJSX('Single Pie Chart', () => { const nbSlices = number( 'nbSlices', 5, {max: 8, min: 1, range: true, step: 1}, 'Pie Chart' ); return ( ); }) .addWithJSX('Single Pie Chart with Labels', () => { const nbSlices = number( 'nbSlices', 5, {max: 8, min: 1, range: true, step: 1}, 'Pie Chart' ); return ( ); }); ================================================ FILE: packages/website/storybook/storybook-data.js ================================================ const data = {}; const DEC23 = 1513987200000; const DAY_IN_MS = 86400000; export function generateLinearData({ nbPoints = 20, randomFactor = 1, startValue = 10, changeRatio = 0.1, extraParams = [], flipXY, key }) { if (data[key]) { return flipXY ? xyFlip(data[key]) : data[key]; } const result = new Array(nbPoints).fill(0).reduce( (series, curr, i) => [ ...series, enrich({ extraParams, datapoint: { x: i + 1, y: series[i].y * (1 + (Math.random() - 0.5) * changeRatio) + (Math.random() - 0.5) * randomFactor }, nbPoints, series, i }) ], [ enrich({ extraParams, datapoint: {x: 0, y: startValue}, nbPoints, series: [], i: 0 }) ] ); if (key !== undefined) { data[key] = result; } return flipXY ? xyFlip(result) : result; } export function generateScatterplotData(args) { const extraParams = [ ['x', random({max: 20})], ['y', random({max: 20})] ].concat(args.extraParams || []); return generateLinearData({...args, extraParams}); } export function generateRadialData(args) { return generateScatterplotData(args).map(({x, y, ...d}, i) => { return { angle: Math.round(x + y), label: getWord()({i}), ...d }; }); } export function xyFlip(arr) { return arr.map(d => ({ ...d, x: d.y, y: d.x })); } export function enrich({datapoint, extraParams, nbPoints, series, i}) { return extraParams.reduce((result, param) => { result[param[0]] = param[1]({...datapoint, series, nbPoints, i}); return result; }, datapoint); } export function nonUniformX() { return ({x, series, i}) => { if (!i) { return x; } return series[i].x + Math.random() + Math.random() + Math.random(); }; } export function random({max = 1, min = 0}) { return () => min + Math.random() * (max - min); } export function intRandom({max = 10, min = 0}) { return () => Math.floor(min + Math.random() * (max - min)); } export function getTime({startTime = DEC23}) { return ({i}) => startTime + i * DAY_IN_MS; } export function getWord() { return ({i}) => [ 'deck.gl', 'math.gl', 'probe.gl', 'vis.gl', 'react-map-gl', 'vis-academy', 'luma.gl', 'kepler.gl' ][i % 8]; } ================================================ FILE: packages/website/storybook/storybook-utils.js ================================================ import React from 'react'; import {AutoSizer} from 'react-virtualized'; import { XYPlot, RadialChart, XAxis, YAxis, VerticalGridLines, HorizontalGridLines } from 'react-vis'; import {boolean, select} from '@storybook/addon-knobs/react'; export const CATEGORY_PALETTE = [ '#19CDD7', '#DDB27C', '#88572C', '#FF991F', '#F15C17', '#223F9A', '#DA70BF', '#125C77', '#4DC19C', '#776E57', '#12939A', '#17B8BE', '#F6D18A', '#B7885E', '#FFCB99', '#F89570', '#829AE3', '#E79FD5', '#1E96BE', '#89DAC1', '#B3AD9E' ]; export const LINEAR_PALETTE = ['#EF5D28', '#FF9833']; export function SimpleChartWrapper(props) { return ( {({height, width}) => ( {props.noXAxis ? null : boolean('X Axis', true, 'General chart options') && } {props.noYAxis ? null : boolean('Y Axis', true, 'General chart options') && } {props.noVerticalGridLines ? null : boolean('vertical gridlines', true, 'General chart options') && ( )} {props.noHorizontalGridLines ? null : boolean( 'horizontal gridlines', true, 'General chart options' ) && } {props.children} )} ); } export function SimpleRadialChartWrapper(props) { return ( {({height, width}) => ( )} ); } export function SimpleChartWrapperNoAxes(props) { return SimpleChartWrapper({...props, noXAxis: true, noYAxis: true}); } export function chooseColorScale() { const colorScale = select( 'colorScale', {linear: 'linear', category: 'category'}, 'category' ); const colorRange = { category: CATEGORY_PALETTE, linear: LINEAR_PALETTE }[colorScale]; return {colorRange, colorScale}; } export const jsxOptions = { defaultProps: false, displayName: component => { if ( component.type.name === 'SimpleChartWrapper' || component.type.name === 'SimpleChartWrapperNoAxes' ) { return 'XYPlot'; } if (component.type.name === 'SimpleRadialChartWrapper') { return 'RadialChart'; } return component.type.displayName; }, filterProps: ['className', 'getNull', 'nullAccessor', 'stack'] }; ================================================ FILE: packages/website/webpack.config.js ================================================ /* eslint-env node */ const {resolve} = require('path'); module.exports = { module: { rules: [ { test: /\.js$/, loader: 'babel-loader', exclude: [/node_modules/], options: { rootMode: 'upward' } } ] }, resolve: { alias: { react: resolve(__dirname, './node_modules/react') } } }; ================================================ FILE: publish-docs.sh ================================================ #!/bin/bash cd website && npm run build && rm -rf /tmp/dist-vis && mv dist /tmp/dist-vis && git checkout gh-pages && rm -rf dist && mv /tmp/dist-vis dist && mv dist/index.html .. && cd .. && git add . && git commit -m 'Upgrade docs' && git push && git checkout master ================================================ FILE: publish.sh ================================================ #!/bin/bash DRY_RUN=0 while true; do case $1 in -v | --version ) VERSION=$2; shift 2 ;; -t | --tag ) TAG=$2; shift 2 ;; -r | --registry ) REGISTRY=$2; shift 2 ;; --dry-run ) DRY_RUN=1; shift ;; -- ) shift; break ;; * ) break ;; esac done if [ -z $VERSION ] then echo "Error: must specify version, with the -v option." exit 0 fi if [ -z $REGISTRY ] then echo "Registry unset, use the default npm registry https://registry.npmjs.org." REGISTRY="https://registry.npmjs.org" fi cp CHANGELOG.md README.md LICENSE packages/react-vis cd packages/react-vis npm version $VERSION publish_command="npm publish" if [ -z $TAG ] then publish_command+=" --tag ${TAG} --registry ${REGISTRY}" else publish_command+=" --registry ${REGISTRY}" fi if [ $DRY_RUN -eq 1 ] then publish_command+=" --dry-run" fi eval $publish_command rm CHANGELOG.md README.md LICENSE ================================================ FILE: remove-refs-to-unpm.pl ================================================ #!/bin/bash perl -pi -e 's/unpm\.uberinternal\.com/registry\.yarnpkg\.com/g' `find . -name yarn.lock`